I have some problems with starting new activity. In my app I have setup broadcast receiver which listens for intent (screen off). When screen is off, my app should start new activity (under certain conditions of course. I'm not making spam app). But sometimes it isn't.
I have declared that activity "singleTop" in my manifest, so I override "onNewIntent" method also.(I think it's important to tell this) But here is the thing.
When phone goes to sleep and if certain conditions are met, then 2 icons appear on the screen (My activity). I made those icons clickable, and if I click my second icon I'm FINISHING (finish()) current activity and I'm starting new CALL LOG activity (owned by Android). When I do that, it should bring back previous activity (with different layout - only one icon - previous displayed together with second one), but again only if phone goes to sleep. The thing is that it doesn't. According to my LOGCAT, all conditions are met but only activity is not started. What is happening with my activity lifecyle here?
Screen Off Receiver code:
#Override
public void onReceive(Context context, Intent intent) {
Log.d("APP", "MAINSERVICE, Screen OFF");
MissedCallObserver mco = new MissedCallObserver(MainService.this);
if(!CallListener.isCallActive) {
Log.d("APP", "MAINSERVICE, INSIDE IF ( call is not active )");
if(mco.checkMissedCalls() > 0 || mco.checkUnreadSms() > 0) {
Log.d("APP", "MAINSERVICE, calls calculated. IF entered. Next is starting activity");
startHandleActivity();
}
}
} // outer if
} // ScreenOffReceiver
private void startHandleActivity() {
Intent startH_Activity = new Intent(MainService.this, HandleActivity.class);
startH_Activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startH_Activity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(startH_Activity);
}
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
wl.acquire();
This will allow to execute code reliably after the Screen Off Intent fires.
Related
I want to know why Android fails to load my activities.
I have intents set up in a RecyclerView within the MainActivity that launch another activity (Activity2). Currently this is no problems when opening the app and clicking these items.
I have a widget that is also set up to open Activity2. It does so correctly without any problems to backstack navigation, except when the app was closed. In which case the widget just closed the app instead of the back button going from Activity2 to MainActivity.
The manifest declares both activities as "SingleTop".
In order to fix this, I added code to the onBackPressed() method and bundle extra to know if widget or main started Activity2. Now code will start an intent for MainActivity if widget launched Activity2 from an empty/closed app, but the onClicks for the items no longer function to launch Activity2 in this case.
TLDR: Intent that starts MainActivity breaks intent launches back to activity that started MainActivity.
This is my code that ends the 2nd activity if widget started from dead activity.
if(mDidWidgetStart && isTaskRoot()) {
Log.d(TAG, "isTaskRoot and widget started");
Intent mainActIntent = new Intent(MediaPlayerActivity.this, MainMediaActivity.class);
startActivity(mainActIntent);
finish();
}
In my manifest, both activities are declared with android:launchMode:"singleTop".
My onclick is called within the RV item like such:
if(...) {
ActivityOptionsCompat options = ActivityOptionsCompat.
makeSceneTransitionAnimation((Activity) v.getContext(),
holder.thumbnailView,
holder.thumbnailView.getTransitionName());
VideoParcelable ytVideo = (VideoParcelable)
Toast.makeText(mContext, "Intent launched",
Toast.LENGTH_SHORT).show();
Intent myIntent= new Intent(mContext,
MediaPlayerActivity.class);
myIntent.putExtra("video_object", video);
MyIntent.putExtras(options.toBundle());
mContext.startActivity(MyIntent);
//logging to determine values are present
Log.v(TAG, "mContext = " + mContext);
Log.v(TAG, "v.getContext() = " + v.getContext());
Log.v(TAG, "myIntent: " + myIntent);
}
I know the above code's if statement and mContext is correct because the Toast message appears when the item is clicked, but the intent is never started. The logs from a failed click return non null values.
ADDED WIDGET UPDATE CODE REQUESTED IN A COMMENT
private static RemoteViews getDefaultWidgetViews(Context context) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.video_widget);
Intent launchMainActivityIntent = new Intent(context, MainMediaActivity.class);
//set pending intent
PendingIntent appPendingIntent = PendingIntent.getActivity(context, 0, launchMainActivityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
//sets actual click action on the widget layout view
remoteViews.setOnClickPendingIntent(R.id.video_widget_layout, appPendingIntent);
//return created remote views
return remoteViews;
}
UPDATE: After giving up and restarting project at a later time this bug is no longer showing itself. I've changed no code... Could this be caused by a gradle sync problem of some sort? It couldn't have been the emulator, as the same results happened on my handset.
*note to self, screen shot and capture every detail of any future weirdness such as this. Does anyone know if/where AS keeps old Logcat output from previous sessions or is it always cleared?
I am writing my code in Android Studio, but the project that I am writing has no GUI, and never updates views for a user. It simply listens for the last installed app with a Broadcast Receiver and sends info to another java file.
I am wondering how I can send a string from a broadcast receiver to an activity (maybe this should not be an "activity" but a "service"?), and have this work while the app is in the background.
Right now I have a broadcast receiver listening for the last installed app on my phone (this runs in the background). And I have a custom broadcast receiver set up to notify my main activity when the broadcast receiver gets an installed app. But this custom receiver doesn't work in the background. Again, maybe I shouldn't be using an Activity to receive info from the BroadcastReceiver?
Is there a way I can send information from my broadcast receiver to my main activity while the app is in the background? I also need my main activity to resume normal function while in the background (there are no GUI updates done while it's in the background).
My BroadcastReceiver which is sending data to my Main Activity through a new Intent
public class NewInstallReceiver extends BroadcastReceiver {
ApplicationInfo ai;
#Override
public void onReceive(Context context, Intent intent) {
final PackageManager pm = context.getPackageManager();
try {
// if app is added, get package info
ai = pm.getApplicationInfo(intent.getData().getSchemeSpecificPart(), 0);
Intent i = new Intent("broadcastName");
// Data pass to activity
i.putExtra("appInfo", ai);
context.sendBroadcast(i);
} catch (final PackageManager.NameNotFoundException e) {
ai = null;
}
}
}
In my MainActivity in onCreate I register the receiver:
registerReceiver(broadcastReceiver, new IntentFilter("broadcastName"));
I receive data from BroadcastReceiver
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// get applicationInfo
Bundle b = intent.getExtras();
Object applicationInfo = b.get("appInfo");
ApplicationInfo appInfo = (ApplicationInfo) applicationInfo;
getNewData(appInfo);
}
};
Then I have to unregister the receiver in MainActivity, but obviously this doesn't allow for me to receive info from BroadcastReceiver once the app goes in the background
#Override
public void onStop()
{
try{
unregisterReceiver(broadcastReceiver);
}
catch(Exception e){
System.out.println("Exception is " + e.toString());
}
super.onStop();
}
Honestly, I don't think that update an Activity that is in background is a good practice (even if possible).
According to DOCS:
An activity is a single, focused thing that the user can do. Almost all activities interact with the user, so the Activity class takes care of creating a window for you in which you can place your UI with setContentView(View). While activities are often presented to the user as full-screen windows, they can also be used in other ways: as floating windows... or embedded inside of another activity (using ActivityGroup).
So, if the Activity is in background, it does not visible. So, it is useless to update its contents (because it is not visible to user).
In fact, you should implement following behavior instead:
Use your Service only to save the information (in a database/content provider) and to notifies the Activity that a new information is available. Then, if your activity is open, it immediately consumes that information. If Activity is not opened, the info remains saved for when the user needed it.
If app was only stopped, you can update the Activity content during onResume() method.
If your app was killed and user opened it again, you can update ALL Views during onCreate() method. onCreate() is called before the View is displayed to user.
This way, the info will be updated when needed: when the user wan's to check the info. Otherwise, it is a waste of time.
MainActivity has an attribute android:launchMode="singleInstance" in AndroidManifest.xml
Here's the onCreate method from MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "onCreate() from MainActivity");
session = new TempSession(this);
// check if logged in. If not, take the user back to login activity.
session.checkLogin(); // <---- HERE
Toast.makeText(this, "logged in as " + session.getUsername(), Toast.LENGTH_SHORT).show();
// Prevents screen from turning off when in this Activity.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
....
and the TempSession, it checks if user is logged in, and if not, it will take the user to the LoginActivity.
This is the checkLogin() method
public void checkLogin(){
// Check login status
if(!this.isLoggedIn() || getUsername() == null) {
// user is not logged in redirect him to Login Activity
Intent i = new Intent(context, LoginActivity.class);
// Closing all the Activities
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
// Add new Flag to start new Activity
i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
// Staring Login Activity
context.startActivity(i);
}
}
From my understanding, where I marked <---- HERE from MainActivity, when it calls checkLogin(), shouldn't it finish all task before it proceeds?
Currently, my app would open up LoginActivity, but still proceeds to the next instructions (e.g. makes a Toast saying "logged in as null", and keeps going on in background) and calls onPause() -> onStop() .
Is there any way to stop the MainActivity from keep going at that point?
This situation causes pressing back from LoginActivity bypasses to MainActivity.
Let me know if you need more information.
Thank you in advance.
Is there any way to stop the MainActivity from keep going at that point?
Yes. Have checkLogin() return some value that indicates MainActivity#onCreate(...) should return instead of proceeding.
Calling startActivity(...) does not start an activity. Think of it as asking the Android framework to start an activity. It places this job at the end of the UI thread's work queue. That means that before it starts the new activity, it will finish any other jobs that are already in the work queue, including the rest of the current method and possibly other lifecycle methods of the current activity.
The reason that your toast still shows up is because merely starting a new activity does not destroy your old activity. It's merely pushed to the backstack. Of course you can change this using the android:launchMode attribute in your manifest. A simple strategy in your cause might be to simply return true from your checkLogin() method when the user is logged in and change your onCreate as follows:
if( session.checkLogin() ) {
Toast.makeText(this, "logged in as " + session.getUsername(), Toast.LENGTH_SHORT).show();
// Prevents screen from turning off when in this Activity.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
....
}
I have an AlertActivity and an Activity. When a broadcast is received, both activities needs to finish. But the below code results Black screen if AlertActivity is on top of Activity.
Below is the code in Activity:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("BROADCAST_INTENT")){
if(alertActvity != null)
alertActivity.finish();
finish();
}
}
And code in AlertActivity:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("BROADCAST_INTENT"))
finish();
}
}
First, Activity's onStop() is getting called before AlertActivity's onStop() is called which results in Black screen, even AlertActivity's finish() called before Activity's finish().
Please help me in this regard.
Finally, I found a solution for this:
Finishing an Activity with a delay of 1 second which really works. By that time, AlertActivity finishes and black screen cannot be displayed.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
finish();
}
}, 1000);
as in both AlertActivity and Activity, you are checking for same action "BROADCAST_INTENT", I assume you registered both receiver in their own class.
If you did so, then actually you have two broadcast receiver waiting for same event. when this event occurs, both of your receiver are going to get it.
So in your AlertActivity is getting finished twice.
I think #Amit K. Saha, is right, your AlertActivity may be finishing twice
Solution :
If your application is running >= API 16 then you can use finishAffinity() method :
Finish this activity as well as all activities immediately below it in the current task that have the same affinity. This is typically used when an application can be launched on to another task (such as from an ACTION_VIEW of a content type it understands) and the user has used the up navigation to switch out of the current task and in to its own task. In this case, if the user has navigated down into any other activities of the second application, all of those should be removed from the original task as part of the task switch.
Note that this finish does not allow you to deliver results to the
previous activity, and an exception will be thrown if you are trying
to do so.
You can call finishAffinity() from AlertActivity because it is on top of Activity. This will finish AlertActivity as well as Activity
My transparent Activity finish results black screen, after a search, i find it is caused by activity switching animation in Android 4.4. But above android 5.1 the phenomenon does not show up.
So I add the below code:
#Override
public void finish() {
super.finish();
overridePendingTransition(0, 0);
}
The black screen after finish is gone.
I think this may be helpful.
try this
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("BROADCAST_INTENT"))
context.finish();
}
}
I have an Activity that I want to run a Delay Function when it runs. I [i]don't[/i] want a delay in OnClick function, I want delay when activity [i]starts[/i].
I have tried the following solutions :
How to set delay in Android onClick function
How to pause/delay on Android?
Android: Timer/Delay Alternative
Android timer set delay
...and some more and none of them did what I want. I want a delay when activity starts (for my project , when The Game Starts) but with that codes, it starts the delay (e.g 10s) when I put my finger on the screen.
You should put a Thread.sleep(long) before the "setContentView(R.layout.xxxx..)" in the onCreate(..) function. In that way, it will actually delay before showing you the elements of the Activity.
If you want to delay even before the onCreate(...) is fired, the approach will need to be different, here is one suggestion:
Run a Service and check for the Foreground application using ActivityManager class (see sample code below). Keep checking for when your app is fired or brought to the 'foreground' (using code below) and then just go back to homescreen & start a timer (in the service itself). Once the timer expires, start your app.
You can run the function below inside an AsyncTask in the Service.
The two approaches are quite different and really depends on what you are looking to achieve exactly.
#SuppressWarnings("deprecation")
private void getRunningAppName() throws NameNotFoundException {
Log.v("neiltag", "Entered getRunningAppName()");
ActivityManager am = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
// The first in the list of RunningTasks is always the foreground task.
RunningTaskInfo foregroundTaskInfo = am.getRunningTasks(1).get(0);
String foregroundTaskPackageName = foregroundTaskInfo .topActivity.getPackageName();
PackageManager pm = this.getPackageManager();
PackageInfo foregroundAppPackageInfo = pm.getPackageInfo(foregroundTaskPackageName, 0);
String foregroundTaskAppName = foregroundAppPackageInfo.applicationInfo.loadLabel(pm).toString();
String packageName = foregroundAppPackageInfo.packageName;
if(foregroundTaskAppName.matches("<NAME OF YOUR APP HERE>")) {
//If your app is fired go back to the Homescreen(i.e. the delay)
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "You are not allowed to open Facbeook now, sorry!", Toast.LENGTH_SHORT).show();
}
});
}
//ADD A TIMER HERE
//ONCE TIMER EXPIRES, FIRE UP YOUR APP AGAIN
}