I am building an Android application which includes a Unity 3d interactive experience.
I have imported the Unity project into Android Studio but when launched the activity is fullscreen and does not show the Android action bar.
How can I do this?
Integration Steps
Create new Unity project.
Export "Google Android Project" from Unity.
Import project into Android Studio.
Attempted Solutions
Changing the theme in the manifest.
Setting the theme in the UnityPlayerActivity Java class.
Overriding the Unity Android Manifest by placing a manifest with an updated theme in the Unity directory /Assets/Plugins/Android.
Changing UnityPlayerActivity to extend AppCompatActivity. This will show the action bar but there is a white gap between it and the status bar.
Setting "Screen.fullScreen = false;" in Unity SceneManager. This removes the immersive mode so that the Android status bar is visible.
Turning off "Status Bar Hidden" in player settings. Appears to have no effect.
Wrapping the UnityPlayer in a FrameLayout. This allows me to resize the the Unity as a view.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company.unity.test"
android:installLocation="preferExternal"
android:versionCode="1"
android:versionName="1.0">
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:banner="#drawable/app_banner"
android:debuggable="false"
android:icon="#drawable/app_icon"
android:isGame="true"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoTitleBar.Fullscreen">
<activity
android:name="com.company.unity.test.UnityPlayerActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"
android:label="#string/app_name"
android:launchMode="singleTask"
android:screenOrientation="fullSensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<meta-data
android:name="unityplayer.UnityActivity"
android:value="true" />
</activity>
</application>
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="23" />
<uses-feature android:glEsVersion="0x00020000" />
<uses-feature
android:name="android.hardware.sensor.accelerometer"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen.multitouch"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen.multitouch.distinct"
android:required="false" />
</manifest>
UnityPlayerActivity.java
package com.company.unity.test;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Window;
import com.unity3d.player.UnityPlayer;
public class UnityPlayerActivity extends Activity
{
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
// Setup activity layout
#Override protected void onCreate (Bundle savedInstanceState)
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy
mUnityPlayer = new UnityPlayer(this);
setContentView(mUnityPlayer);
mUnityPlayer.requestFocus();
}
// Quit Unity
#Override protected void onDestroy ()
{
mUnityPlayer.quit();
super.onDestroy();
}
// Pause Unity
#Override protected void onPause()
{
super.onPause();
mUnityPlayer.pause();
}
// Resume Unity
#Override protected void onResume()
{
super.onResume();
mUnityPlayer.resume();
}
// This ensures the layout will be correct.
#Override public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
mUnityPlayer.configurationChanged(newConfig);
}
// Notify Unity of the focus change.
#Override public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
mUnityPlayer.windowFocusChanged(hasFocus);
}
// For some reason the multiple keyevent type is not supported by the ndk.
// Force event injection by overriding dispatchKeyEvent().
#Override public boolean dispatchKeyEvent(KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
return mUnityPlayer.injectEvent(event);
return super.dispatchKeyEvent(event);
}
// Pass any events not handled by (unfocused) views straight to UnityPlayer
#Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
#Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
#Override public boolean onTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
/*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
}
for Unity version 5.5 best way to avoid Immersive Mode in Android is to pass in the constructor not an Activity but to cast of the ApplicationContext to the Wrapper Context.
mUnityPlayer = new UnityPlayer((ContextWrapper) getApplicationContext());
That works because in the obfuscated UnityPlayer.class (in unity-classes.jar) there is a check of instance.
private void h() {
if(this.h instanceof Activity) {
((Activity)this.h).getWindow().setFlags(1024, 1024);
}
}
So if it's not an Activity the UnityPlayer.class doesn't set the flag.
I think I've found solution.
Change that line:
mUnityPlayer = new UnityPlayer(this);
so it creates your own UnityPlayer subclass, which overrides setFullscreen method (little bit hacky):
public class UnityPlayerWrapper extends UnityPlayer {
public UnityPlayerWrapper(ContextWrapper contextWrapper) {
super(contextWrapper);
}
#Override
protected void setFullscreen(boolean b) {
super.setFullscreen(false);
}
}
In addition, please remove that line: requestWindowFeature(Window.FEATURE_NO_TITLE);
Piotr answer is working for older version and Lorenzo DM's answer also valid but
mUnityPlayer = new UnityPlayer((ContextWrapper) getApplicationContext());
not working in some devices. So Finally I've modify UnityPlayerActivity here is new solution
public class UnityPlayerActivity extends AppCompatActivity
{
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
ActionBar actionBar;
private Toolbar toolbar;
private FrameLayout unityContainer;
// Setup activity layout
#Override protected void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mUnityPlayer = new UnityPlayer(this);
setContentView(R.layout.activity_unity_player);
mappingWidgets();
init();
}
void mappingWidgets(){
toolbar = (Toolbar) findViewById(R.id.toolbar);
unityContainer = (FrameLayout) findViewById(R.id.unity_container);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
unityContainer.addView(mUnityPlayer.getView(), 0, layoutParams);
mUnityPlayer.requestFocus();
}
void init(){
setSupportActionBar(toolbar);
actionBar = getSupportActionBar();
if (actionBar != null)
actionBar.setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
onDestroy();
}
});
setTitle(getString(R.string.app_name));
}
#Override protected void onNewIntent(Intent intent)
{
setIntent(intent);
}
// Quit Unity
#Override protected void onDestroy ()
{
mUnityPlayer.quit();
super.onDestroy();
}
// Pause Unity
#Override protected void onPause()
{
super.onPause();
mUnityPlayer.pause();
}
// Resume Unity
#Override protected void onResume()
{
super.onResume();
mUnityPlayer.resume();
}
// Low Memory Unity
#Override public void onLowMemory()
{
super.onLowMemory();
mUnityPlayer.lowMemory();
}
// Trim Memory Unity
#Override public void onTrimMemory(int level)
{
super.onTrimMemory(level);
if (level == TRIM_MEMORY_RUNNING_CRITICAL)
{
mUnityPlayer.lowMemory();
}
}
// This ensures the layout will be correct.
#Override public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
mUnityPlayer.configurationChanged(newConfig);
}
// Notify Unity of the focus change.
#Override public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
mUnityPlayer.windowFocusChanged(hasFocus);
}
// For some reason the multiple keyevent type is not supported by the ndk.
// Force event injection by overriding dispatchKeyEvent().
#Override public boolean dispatchKeyEvent(KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
return mUnityPlayer.injectEvent(event);
return super.dispatchKeyEvent(event);
}
// Pass any events not handled by (unfocused) views straight to UnityPlayer
//#Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
// Pass any events not handled by (unfocused) views straight to UnityPlayer
#Override public boolean onKeyUp(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK) {
finish();
onDestroy();
return true;
}
return mUnityPlayer.injectEvent(event); }
#Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
#Override public boolean onTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
/*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
}
Related
I want to listen to accessibility gesture when user inside my app. It means onGusture should call when My App is at foreground but onGesture always called no matter my app at foreground or not. I try to set package name in Java as well as in XML but it's not working. Please take look at this tell me what I am doing wrong
MyAccessibilityService.java
public class MyAccessibilityService extends AccessibilityService {
private String TAG="MyAccessibilityServiceTAG";
#Override
protected boolean onGesture(int gestured) {//This method always called, I want this method only called when my App in foreground
Log.d(TAG,"onGesture:"+gestureId);
Toast.makeText(getApplicationContext(),"Gesture: "+gestureId,Toast.LENGTH_SHORT).show();
return super.onGesture(gestureId);
}
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
System.out.println("Event Occurred");
Log.d(TAG, "onAccessibilityEvent: event=" + event);
AccessibilityNodeInfo nodeInfo = event.getSource();
if (null == nodeInfo) return;
}
#Override
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo info = getServiceInfo();
// your other assignments
info.packageNames = new String[]{"com.vorail.ttsdemo"};
setServiceInfo(info);
}
#Override
public void onInterrupt() {
Log.d(TAG,"Accessibility Interrupted" );
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"Service Destroyed");
}
}
accessibility_service_config.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/app_name"
android:accessibilityEventTypes="typeTouchExplorationGestureStart"
android:canRequestFilterKeyEvents="false"
android:accessibilityFeedbackType="feedbackSpoken"
android:packageNames="com.vorail.ttsdemo"
android:notificationTimeout="100"
android:settingsActivity="com.vorail.ttsdemo.MainActivity"
android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode"
android:canRequestTouchExplorationMode="true"
/>
By setting android:packageNames to something specific to your app, you'll filter the incoming events:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeViewClicked|typeViewFocused|typeTouchInteractionStart|typeTouchInteractionEnd|typeTouchExplorationGestureStart|typeTouchExplorationGestureEnd"
android:packageNames="com.app.yourapplication"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true" />
I have created a service to play music through my activities. I Have 2 activities and a splashScreen. I start the service from the first acitivity and by clicking some items I go to the second activity. The music is played properly but when I put my app in background or I lock my phone or I'm in another app I still hear the sound. I cannot put stopService in onStop because if I go to my second activity, the music will stop.
Here's my service music class :
public class BackgroundMusicService extends Service {
MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.game_music);
player.setLooping(true);
player.setVolume(10, 10);
player.start();
}
public int onStartCommand(Intent intent, int flags, int startId) {
player.start();
return 1;
}
#Override
public void onDestroy() {
player.stop();
player.release();
}
#Override
public void onLowMemory() {
}
}
and :
public class Activity1 extends AppCompatActivity implements View.OnClickListener{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_1);
Intent backgSound = new Intent(Activity1.this, BackgroundMusicService.class);
startService(backgSound);
Button b1 = (Button) findViewById(R.id.b1);
b1.setOnClickListener(this);
}
#Override
public void onDestroy() {
super.onDestroy();
Intent backgSound = new Intent(Activity1.this, BackgroundMusicService.class);
stopService(backgSound);
}
#Override
public void onClick(View v) {
startActivity(new Intent(this,Activity2.class);
}
And the second activity is just a view (for test).
How could I keep the sound over these 2 activites and stop it when my app is in background or my phone is locked.
I've tried to stopService in onStop then startService in onResume, but it does not work between activities.
Judging by what you want you need a more fine grained control over the starting and stopping of your MediaPlayer object. An easy solution would be to add intent-filters and actions like so:
public class BackgroundMusicService extends Service {
public static final String ACTION_START_MUSIC = "package_name.action_start_music";
public static final String ACTION_STOP_MUSIC = "package_name.action_stop";
private MediaPlayer player;
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
player = MediaPlayer.create(this, R.raw.game_music);
player.setLooping(true);
player.setVolume(10, 10);
}
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent.getAction() != null){
switch (intent.getAction()){
case ACTION_START_MUSIC :
if(!player.isPlaying()){
player.start();
}
break;
case ACTION_STOP_MUSIC :
if(player.isPlaying()) {
player.stop();
}
break;
default: break;
}
}
return START_STICKY;
}
#Override
public void onDestroy() {
player.release();
}
#Override
public void onLowMemory() {
}
}
Update your manifest :
<service android:name=".BackgroundMusicService"
android:exported="false">
<intent-filter>
<action android:name="package_name.action_start_music" />
<action android:name="package_name.action_stop" />
</intent-filter>
</service>
To use:
startService(new Intent(BackgroundMusicService.ACTION_START_MUSIC));
startService(new Intent(BackgroundMusicService.ACTION_STOP_MUSIC));
It is well known that onDestroy() is not always calling, moving your onDestroy actions to the onPause() method will work:
Note: do not count on this method being called as a place for saving data! For example, if an activity is editing data in a content provider, those edits should be committed in either onPause() or onSaveInstanceState(Bundle), not here. This method is usually implemented to free resources like threads that are associated with an activity, so that a destroyed activity does not leave such things around while the rest of its application is still running. There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
More info here.
You need to read more about the basics of Android.
Regards.
I have implemented a Searchview into my application, and have run into a weird problem. Basically, the user is able to enter a string to be searched and it is submitted properly. However, my application will filter the ListView based on the search query, and then an Intent will be launched resulting in the activity starting again and returning to it's original state where it is displaying all the results.
Here is the related code.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
...
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
updateEntriesView(dbHelper.queryByName(query));
Toast.makeText(getApplicationContext(), "Searching for " + query, Toast.LENGTH_LONG).show();
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
}
private void updateEntriesView(ArrayList<Entry> response) {
mListView = (ListView) findViewById(R.id.listview_entries);
if (adapter == null) {
adapter = new EntryListAdapter(EntriesActivity.this, R.layout.row_entry_layout, response);
mListView.setAdapter(adapter);
} else {
adapter.clear();
adapter.addAll(response);
adapter.notifyDataSetChanged();
}
adapter.setNotifyOnChange(true);
}
I can not figure out to change this behavior and I can not find any documentation regarding it's implementation online. Is there a way I can override this feature and prevent it from launching an intent and restarting the activity to it's original unfiltered state?
EDIT:
I was able to solve this issue myself. For anyone who may be having the same problem here was my solution.
So I had an intent-filter in my AndroidManifest.xml under the activity that would launch an intent upon a search action. This is what was causing it to launch an intent and start a new instance of my activity.
My solution to this was to convert this activity into a single top activity and add code to handle the intent. By making the activity single top, it allows the activity to handle incoming intents without starting up a new instance of the intent. For a more detailed description on the difference between a normal and single top activity, this is a good article on the subject https://www.mobomo.com/2011/06/android-understanding-activity-launchmode/
Now, because my activity is single top, I can override the onNewIntent() method and add my code for handling this intent without having it launch a new instance. The relevant code for solving my problem is below.
AndroidManifest.xml
<activity android:name=".EntriesActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="#xml/searchable"></meta-data>
</activity>
EntriesActivity.java
public class EntriesActivity extends AppCompatActivity {
...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
...
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
updateEntriesView(dbHelper.queryByName(query));
Toast.makeText(getApplicationContext(), "Searching for " + query, Toast.LENGTH_LONG).show();
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
updateEntriesView(dbHelper.queryEntriesByName(query));
}
}
private void updateEntriesView(ArrayList<Entry> response) {
if (adapter == null) {
adapter = new EntryListAdapter(EntriesActivity.this, R.layout.row_entry_layout, response);
mEntriesList.setAdapter(adapter);
} else {
adapter.clear();
adapter.addAll(response);
adapter.notifyDataSetChanged();
}
adapter.setNotifyOnChange(true);
}
}
I hope this helps.
You need to clear focus of the text view using searchView.clearFocus();
Refer This Answer: https://stackoverflow.com/a/15127252/7710739
I know this is old but if your searching and resulting in the same Activity try removing the
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
It should not try to launch a new activity then i don't beleive.
I have a Cordova plugin which uses the https://github.com/googlesamples/android-Camera2Video example. I would like to disable the back button while in Camera.
To do this, I believe Fragments can't accept back button events, so have added an Activity to the plugin. I can successfully run the plugin, camera and activity, disabled the back button.
However when I close the plugin it errors with:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
To get around this I have used commitAllowingStateLoss instead of commit however when I open the plugin the second time, it errors with:
java.lang.NullPointerException: Attempt to invoke interface method 'android.app.Activity org.apache.cordova.CordovaInterface.getActivity()' on a null object reference
I presume I'm not stopping the activity correctly, and allowing the state to be lost, which causes a second error. How do I get around this?
I've tried this method but no luck:
PhoneGap Android Plugin - close the plugin Activity
I've put my code in a branch here:
https://github.com/kmturley/cordova-plugin-media-custom/tree/feature/back-button
Add a summary of the changes:
plugin.xml
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<activity android:name="com.example.android.camera2video.MediaCustomActivity" android:label="#string/activity_name" android:theme="#style/Theme.Transparent">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</config-file>
<source-file src="src/android/MediaCustom.java" target-dir="src/com/example/android/camera2video" />
<source-file src="src/android/MediaCustomActivity.java" target-dir="src/com/example/android/camera2video" />
<source-file src="src/android/Camera2VideoFragment.java" target-dir="src/com/example/android/camera2video" />
MediaCustom.java
public void show() {
if (cameraFragment != null) {
return;
}
cameraFragment = Camera2VideoFragment.newInstance(cordova, callbackContext);
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
cordova.getActivity().setContentView(resources.getIdentifier("activity_camera", "layout", packageName));
FragmentManager fragmentManager = cordova.getActivity().getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(resources.getIdentifier("container", "id", packageName), cameraFragment);
fragmentTransaction.commit();
MediaCustomActivity.start(cordova.getActivity());
}
});
}
public void hide() {
if (cameraFragment == null) {
return;
}
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
MediaCustomActivity.stop(cordova.getActivity());
cordova.getActivity().setContentView(getView());
FragmentManager fragmentManager = cordova.getActivity().getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.remove(cameraFragment);
fragmentTransaction.commitAllowingStateLoss(); // commit();
cameraFragment = null;
}
});
}
MediaCustomActivity.java
public static void start(Activity activity) {
Intent intent = new Intent(activity, MediaCustomActivity.class);
activity.startActivity(intent);
}
public static void stop(Activity activity) {
// what can we do here?
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
Window window = this.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return false;
}
return super.onKeyDown(keyCode, event);
}
I am wondering how to check if my application is open and currently visible to the user when receiving an onMessage() from GCM. At first, I was just using my own boolean isVisible, but then I realized this isn't reliable, because if the app isn't open, the object I use to access that flag is null. While this in itself could be used to see if the app is open, it seems a little bit messy. Is there a way in Android from a system level to somehow check if the application is currently open, and if the user is viewing the app? Keep in mind an app could technically be running, but not be visible, because a user has recently pressed the "home" button sending it to the background.
#Override
protected void onMessage(Context arg0, Intent arg1) {
String turn = intent.getExtras().getString("turn");
if (turn.equals("yours"){
if (/*app is open*/){ <------------------ what can go here?
// dont generate a notification
// display something in the game instead
}
else{
// generate notification telling player its their turn
}
}
}
I would use order broadcasts to do that.
In your onMessage method:
Intent responseIntent = new Intent("com.yourpackage.GOT_PUSH");
sendOrderedBroadcast(responseIntent, null);
In your Activity:
public class YourActivity extends Activity {
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Right here do what you want in your activity
abortBroadcast();
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//.....
}
#Override
protected void onPause() {
unregisterReceiver(mBroadcastReceiver);
super.onPause();
}
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter("com.yourpackage.GOT_PUSH");
filter.setPriority(2);
registerReceiver(mBroadcastReceiver, filter);
super.onResume();
}
}
The other BroadcastReceiver
public class SecondReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//In this receiver just send your notification
}
}
Manifest:
<activity
android:name=".YourActivity"
android:label="#string/app_name">
<intent-filter>
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".SecondReceiver">
<intent-filter
android:priority="1">
<action
android:name="com.yourpackage.GOT_PUSH" />
</intent-filter>
</receiver>
Basically in the onMessage method you send an Intent which is first received by the BroadcastReceiver registered inside YourActivity if it is running and in foreground, otherwise it is received by the SecondReceiver.
Use SharedPreferences saving the boolean isVisible, and when you get the value from the preference you can add a default value.
SharedPreferences settings = context.getSharedPreferences("NAME_XXX", Activity.MODE_PRIVATE);
settings.getBoolean("visible", false);
What I always do is have a reference to the current Activity.
I set the current Activity in every onResume to this and set it to null in every onPause.
If the current Activity is null then the app is not open. If it's not null you can see if the correct Activity is open and deliver it to that Activity.
GCMIntentService:
public static Activity currentActivity;
public static final Object CURRENTACTIVIYLOCK = new Object();
#Override
protected void onHandleIntent(Intent intent) {
synchronized(CURRENTACTIVIYLOCK) {
if (currentActivity != null) {
if (currentActivity.getClass() == CorrectActivity.class) {
CorrectActivity act = (CorrectActivity)currentActivity;
act.runOnUiThread(new Runnable() {
public void run() {
// Notifiy activity
}
});
} else {
// show notification ?
}
} else {
// show notification
}
}
}
CorrectActivity:
#Override
protected void onResume() {
synchronized (GCMIntentService.CURRENTACTIVITYLOCK) {
GCMIntentService.currentActivity = this;
}
}
super.onResume();
}
#Override
protected void onPause() {
synchronized (GCMIntentService.CURRENTACTIVITYLOCK) {
GCMIntentService.currentActivity = null;
}
super.onPause();
}
The thing that worked for me:
Create a final Class Constants, inside it, create static varaiable:
public final class Constants{
public static AppCompatActivity mCurrentActivity;
}
Now, on each on resume of your activties say:
#Override
protected void onResume() {
super.onResume();
Constants.mCurrentActivity = this;
}
When receieving notification, check if current activity is null, if its null, application is not opened, if activity isn't null, you can check things like:
if(Constants.mCurrentActivity instanceof MainActivity){
((MainActivity) Constants.mCurrentActivity).yourPublicMethodOrStaticObject;
}