I need to set the application theme according to the user preference. This process happens in separate activity and it is not my main activity. I used this code to change the application theme:
getApplicationContext().setTheme(R.style.theme);
I know that it is perfectly work if I used it before
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
methods inside onCreate() method. But I have to do it in separate method. I tried to restart the application after that code as follows but it is not restart the application.
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
finish();
startActivity(intent);
Also I don't have any idea after restarting the application that it would work.
Any suggestion would be appreciated to solve the problem to change the application theme programmatically .
Try the following, it restarts the application after one second:
private void restartSelf() {
AlarmManager am = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis() + 1000, // one second
PendingIntent.getActivity(this, 0, getIntent(), PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_CANCEL_CURRENT));
finish();
}
restartSelf() should be a member of your main activity. Alternatively, you may replace this with your application's context and and getIntent() with the main activity's Intent.
If I understood you correctly, can you try saving the user preference choice?
After the user chooses a theme, you can save the choice in persistent memory (such as shared preferences) and after the select, restart the application. In the onCreate of the main activity, you can access the user choice and set the theme there before super.onCreate( ) by accessing the persistent memory.
Related
I have issue in intent of my launcher activity.Scenerio is:
1. Send intents form notification service to my launcher activity
PendingIntent contentIntent = PendingIntent.getActivity(this, TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID, new Intent(this, MainActivity.class).putExtra("is_log", true), Intent.FLAG_ACTIVITY_CLEAR_TOP);
2. In my MainActivity i getting this intent. code is:
if(this.getIntent().getExtras()!=null){
boolean isLogNewTripScreen = (boolean)this.getIntent().getExtras().getBoolean("is_log");
}
}
3. this work fine but when i come from notification service,but when i launch from not notification service ,that data in intentis still there.How can i remove that data from intent.
EDIT: I've created a sample application to test this problem and possible solutions. Here are my findings:
If you launch your app from a notification with extras and then later return to your app by selecting it from the list of recent tasks, Android will launch the app again the same way it was launched from the notification (ie: with the extras). This is either a bug or a feature, depending on who you ask.
You'll need to add additional code to deal with this situation. I can offer 2 suggestions:
1. Use FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
When you create your notification, set the flag Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS in the Intent. Then, when the user selects the notification and launches the app from the notification, this will not create an entry for this task in the list of recent tasks. Also, if there was an entry in the list of recent tasks for this application, that entry will also be removed. In this case, it will not be possible for the user to return to this task from the list of recent tasks. This solves your problem by removing the possibility that the user launches the app from the list of recent tasks (but only when the app has been launched from the notification).
2. Detect FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
When the user launches your app from the list of recent tasks, Android sets the flag Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY in the Intent that is passed to onCreate() of your launch activity. You can detect the presence of this flag in onCreate() and then you know that the app has been launched from the recent tasks list and not from the notification. In this case, you can just ignore the fact that the extras in the Intent still contain data.
Choose the solution that best suits the workflow for your application. And thanks for the question, this was an interesting challenge to solve :-)
Additional information:
You are creating the PendingIntent incorrectly. You are calling
PendingIntent contentIntent = PendingIntent.getActivity(this,
TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID,
new Intent(this, MainActivity.class).putExtra("is_log", true),
Intent.FLAG_ACTIVITY_CLEAR_TOP);
You are passing Intent.FLAG_ACTIVITY_CLEAR_TOP as the 4th parameter to getActivity(). However, that parameter should be PendingIntent flags. If you want to set FLAG_ACTIVITY_CLEAR_TOP on the Intent, you need to do it this way:
PendingIntent contentIntent = PendingIntent.getActivity(this,
TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID,
new Intent(this, MainActivity.class).putExtra("is_log", true)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
I noticed that using fragments. I read a QR Code in Activity A that opens fragment 1, send its content to a webservice and if goes right, replace it with fragment 2. When user press back, the onBackPressed in Activity A call finish. If user select the app again in the list, it was opening fragment 1 instead of fragment 2.
I solved that checking in onBackPressed if extra contains a field indicating that fragment 2 was already opened. If true, moveTaskToBack(true) is called instead of finish()
Activity A
#Override
public void onBackPressed() {
Bundle extras = getIntent().getExtras();
if(extras.containsKey(Constants.TICKET_DONT_SHOW_QRCODE_SCREEN)){
moveTaskToBack(true);
}else {
finish();
}
}
Fragment 2
Intent mainIntent = getActivity().getIntent();
mainIntent.putExtra(Constants.TICKET_DONT_SHOW_QRCODE_SCREEN, true);
getActivity().setIntent(mainIntent);
I've tested all the answers of stackoverflow with no luck, what worked for me was this. Create a helper class to check the activity flags. Or a function, it does not matter.
object FlagHelper {
fun notLaunchedFromNotification(activity: AppCompatActivity): Boolean {
return activity.intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
}
}
Then use as the following code. It returns a boolean so you can check the intent extras when it's false
val notLaunchedFromNotification = FlagHelper.notLaunchedFromNotification(this)
Add android:launchMode="singleInstance" to your launcher activity
and then Use flag Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS while starting your activity
This is the code I use to create the PendingIntent for my notification.
Intent notificationIntent = new Intent(context, Activity1.class);
PendingIntent myIntent = PendingIntent.getActivity(context, 0,notificationIntent, 0);
This PendingIntent launches Activity1 when the notification is click.
How can I simply reopen the app and go to the most recent Activity (as though clicking on the launcher icon) instead of launching a new Activity when the notification is clicked?
Activity1 is just an example. I have multiple Activity in the app. I just want to reopen the app and go to the most recent Activity
NOTE: this looks like wrong design for me, because notification should allow user to enter activity that is in context with the notification.
Technically, you can create redirecting activity and your notification intent should launch it when tapped. In its onCreate() you check what activity you want user to be redirected (you can keep this info in SharedPreferences, and each activity would write this info in onCreate() (or make that in your base class if you have it). Then in redirector you call regular startActivity() to go last activity and call finish() to conclude your redirector. Moreover, your redirector activity does not need any layout so add
android:theme="#android:style/Theme.NoDisplay"
to its Manifest entry (of course you also need no call to setContentView())
create Activity1 as a singletask activity , by changing the launchMode of the activity to singleTask...
set your activity to launchMode="singleTop" in your Manifest.xml then use this code instead of what you are using above to reopen the active one:
Title = "YourAppName";
Text = "open";
notificationIntent = new Intent(this, Activity1.class);
cIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), Title, Text, cIntent);
You can change the android:launchMode in the manifest file for the activity targeted by the pending intent.
Typically, you can use singleTop, which will reuse the same instance when the targeted activity is already on top of the task stack (i.e.: Activity is shown before you left your app).
You can also consider SingleTask and SingleInstance, if you want to keep only a single instance of the activity.
When my application is idle, Android kills the process.
If user reopens the application after some time, only the top Activity is created - this is a problem for me because the activity depends on initialization of other objects (which are now destroyed).
What I want to do in that case is to re-launch the application.
How can I do that?
Just identify that your Application is being launched after it was previously destroyed by Android, you could do this by keeping a variable in a custom Application class, and set it to true after your applicaiton is initialized. So when the applicaction is re-launched, this flag is false, and then just make an Intent to launch your main Activity specifying FLAG_ACTIVITY_CLEAR_TOP :
Intent reLaunchMain=new Intent(this,MainActivity.class);
reLaunchMain.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(reLaunchMain);
I think this answer only for you.
After finish progress call this
finish();
Intent intent = new Intent(this, sameactivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
You should probably be looking at storing such Objects in your app's implementation of the Application class.
If these objects contain state that needs to be more persistent, you should save the state of such Objects in each Activity's onPause() method, either to the database, in SharedPreferences or remotely.
So I've got an activity in my app that is currently marked as
android:launchMode="singleTop"
...and I currently have logic in both onCreate and onNewIntent to make sure that the screen is always showing the data delivered by the newest Intent that launched. And I'd like to be able to change between Holo.Light and Holo.Dark based on the data delivered by that Intent.
Calling setTheme doesn't work (see these two links):
Why getApplicationContext().setTheme() in a Activity does not work?
http://code.google.com/p/android/issues/detail?id=4394
That second link has a workaround that involves creating a second AndroidManifest.xml entry that has the other theme and points to an empty subclass of the activity in question. This works, but it breaks singleTop (since there can now be two instances of the activity on the stack).
I'm out of ideas. Anybody know if there's any way to do this aside from rolling my own custom ActionBar view for this activity?
You need to set the theme using the setTheme() method, but then to reload the activity.
I have a singleTask activity, and a code that runs on API<11, so I have this code to reload the activity:
public void reload() {
Intent intent = getIntent();
overridePendingTransition(0, 0);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
overridePendingTransition(0, 0);
startActivity(intent);
}
I'm pretty much just finishing the activity and calling it again. I disable any transition animation to make the reloading look instant.
Since you're referring to the Holo themes I assume you're working with API 11+.
API 11 added the method Activity#recreate(), which sends your current activity through the same teardown/recreate process that normally happens for config changes such as rotating the screen between landscape and portrait orientation. Your onCreate method will be called again on a new Activity instance, allowing you to set the theme on the Activity before the window is initialized as usual.
The Google Books apps uses this tactic to switch between light/dark themes for "night mode."
I'll post my solution that doesn't really add nothing new here but merge the various tips together.
After changing the Theme of the activity and optionally ( depends on what you are looking for ) of the application:
public void updateTheme( Activity a, int themeID ) {
a.getApplication().setTheme( themeID );
a.setTheme( themeID );
}
You then have to recreate the activity ( just as after a configuration change ).
For the OS 11> there is a API , instead for previous versions you have to force it finishing and restarting the activity just as Udinic pointed out.
public boolean isBeforeHoneycomb() {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
}
public void reload() {
if( isBeforeHoneycomb() ) {
Intent intent = getIntent();
overridePendingTransition(0, 0);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
finish();
overridePendingTransition(0, 0);
startActivity(intent);
}else{
recreate();
}
}
I have a widget that contains 4 buttons to show 4 stock prices, each of them will launch into the same activity Quote.class to show stock details. In onUpdate(), it will set up the pendingIntent with extras with stock symbol. After I hit button A, it goes to Quote activity that shows stock A. Then I hit the BACK button to the homescreen, Quote activity calls onDestroy() and when I hit button B, stock B will show properly. However, when i hit HOME button after it shows stock A, the Quote activity only calls onStop without calling onDestroy(), then as i hit button B, it will call onStart() and it shows the same instance that shows stock A.
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.etappwidget);
setQuoteIntent(context, views, R.id.view1, "BAC", 1);
setQuoteIntent(context, views, R.id.view2, "C", 2);
setQuoteIntent(context, views, R.id.view3, "GOOG", 3);
setQuoteIntent(context, views, R.id.view4, "AAPL", 4);
private static void setQuoteIntent(Context context, RemoteViews views, int viewId, String symbol, int requestCode) {
Intent i = new Intent(context, Quote.class);
i.putExtra("SYMBOL", symbol);
i.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
PendingIntent pi = PendingIntent.getActivity(context, requestCode, i, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(viewId, pi);
}
Originally I thought adding a flag in the Intent should solve this problem. But I have tried
i.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK or FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_NO_HISTORY), none of them makes any difference.
So is there any ways to make it work? How can i remove the history stack from HOME button? How can I call onCreate in Quote activity and get new extras when i hit button B? Any help is appreciated. Thanks
It is normal for onDestroy to possibly not get called if you do simple task switching (like holding the HOME button). If you need to do clean-up, it needs to go in onPause.
I believe the problem is that you have a PendingIntent that only differs by extra. PendingIntents are cached, so if you use two with the same action and data, they'll overwrite each other. You can circumvent that by giving each some random data. Try passing the symbol in the data rather than through the extra (which is preferred anyway).
You can try this:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
Your activity will not be seen in the history stack
clickIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
Since: API Level 1 If set, the new activity is not kept in the
history stack. As soon as the user navigates away from it, the
activity is finished. This may also be set with the noHistory
attribute.
This solved the same issue of mine while implementing widget activities.
When you press home, most probably the activity will not be destroyed. It is put to pause state in such case. So, I guess your code to initiate the stock view would stationed in onCreate rather than onResume. So, moving those to onResume should solve the problem.
If you are using a different Action in your Intent and use the SingleTop or similar flag and override onNewIntent to detect the correct action that should do the trick. You need to be prepared to handle the intent in onCreate and onNewIntent.