I have a RingtonePreference nested inside 2 PreferenceScreen declared as:
<PreferenceScreen
//other preferences
<PreferenceScreen
//other preferences
<RingtonePreference
android:key="ringtone"
android:title="#string/ringtone_title"
android:summary="#string/sipringtone_ringtone_summary"
android:ringtoneType="ringtone" />
/>
/>
The PreferenceFragment is nested inside a ActivityGroup as such. The path to the fragment is as followed:
MainActivity(TabActivity)->Activity1(ActivityGroup)->Activity2(Activity)->PreferenceFragment
I did not write this code but am picking off where someone left off. The dialogs from clicking every preference was crashing the app because it did not like the context of Activity2. This was an ActivityGroup related issue that was resolved by forcing the context of each preference to the context of Activity1.
EditPreferences, ListPreferences, and CheckPreferences all worked as intended but RingtonePreference is giving me a lot of trouble. Although the dialog pops up to let me choose the selection of ringtones, it does not save the setting.
Selecting a ringtone from the list and pressing the OK button does not trigger onPreferenceChange() or onSharedPreferenceChanged(). I tried creating a custom RingtonePreference and overriding onSaveRingtone() but that did not get called at all. However, other methods like onPrepareRingtonePickerIntent() and onRestoreRingtone() did get called. I tried a bunch of other options that were mentioned on stack overflow but had no luck. I'm running out of ideas to get RingtonePreference to work and think starting my own ringtone picker using RingtoneManager is the best alternative. If anyone could give me some advise on how to get RingtonePreference to work then that'll be awesome.
Edit: I believe this is a context problem but I can't find out how to solve it yet.
I was unable to get the RingtonePreference to work but I did find some new details and an alternative. RingtonePreference looks like it starts a new activity for the dialog. If you have launchmode=singleInstance, that will mess up the RingtonePreference because you're starting that activity on a different Task stack. Using launchmode=singleTask or removing the launchmode could solve your problem, but not for me entirely (but I still needed launchmode to not be equal to singleInstance). My solution was to add the preference manually in java code. The steps were
1) Find your preference screen
2) Make a preference
3) Set your preference details, i.e. title & summary
4) (Optional) Arrange the order of your preference (google the setOrder function for preference)
5) Set the onPreferenceCLickerListener to the preference you created
6) Inside onPreferenceClick, Launch the ringtone picker dialog configured by RingtoneManager and start the activity from where ever you want.
7) Add the preference to your preference screen chosen in step 1.
8) On the activity/fragment where you started the ringtone picker, override on onActivityResult() and handle the chosen ringtone.
Good luck!
kyrax's answer seems appropriate, although I didn't want to go through all the mess of creating a Preference programmatically and then inserting it that way.
To solve this problem, I started off with a complete XML and then I simply added the OnPreferenceChangeListener to the RingtonePreference. This can be done from your PreferenceFragment:
Preference notificationSoundPref = findPreference("ringtone);
notificationSoundPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// do what you need here
return true;
}
});
Related
So I have this activity that creates 4 fragments for me, which are a bottom navigation bar.
when I change to dark mode or light mode, the fragments reset and take me back to the "main fragment" which is basically home in the bottom navigation bar.
so my question is how do I stay in the tab I was in while changing UI mode? while of course changing the UI mode of the fragments as well
The best aproach is to let activity recreate itself with its fragments, some configuration changes might be required to show correct UI. And its always possible that your user will move your app to the background, system will kill it after a while and then when your user will go back to it - you will be back with your problem of recreation. I am talking about it to prevent you from using hacks like configChanges in androidmanifest which prevent Activity recreation on configuration change event.
But back to the main point:
when configuration change destroys your Activity, fragments will be automatically recreated when new instance of Activity will be created. But its up to you to save some of its state. I am not sure how your UI and code looks like. The very basic way to save and restore state (like active tab in your question) is to use: Activity.onSaveInstanceState(Bundle) / Activity.onCreate(savedInstanceState: Bundle?) and Fragment.onSaveInstanceState(Bundle)/Fragment.onCreate(savedInstanceState: Bundle?) mechanism. You save relevant variables in bundles in onSaveInstanceState, and later in onCreate restore this state using savedInstanceState variable, but only if its non null - which happens when Activity is created for the first time.
This is a broad topic, you can find more on this here:
https://developer.android.com/topic/libraries/architecture/saving-states
https://developer.android.com/guide/fragments/saving-state
The app I'm working on shows some sensitive information that must not be shown on the "Recent Tasks" screen when stopping the app by pressing the home button.
I'd like to blur the sensitive data in the screenshot or show the app logo instead.
I am aware of the following approaches but they don't fit my requirements:
Setting the actvitie's android:excludeFromRecents to true in the manifiest prevents the app from being shown at all in the recent tasks. This would disrupt the user experience.
Using FLAG_SECURE results in a blank card on the recents tasks screen. (How do I prevent Android taking a screenshot when my app goes to the background?) I don't like the blank screen. However, I'll stick to this solution if there is no workaround.
Overriding onCreateThumbnail seems like the ideal solution but, unfortunately, doesn't work as it's currently not invoked by the OS :( (https://code.google.com/p/android/issues/detail?id=29370)
And then there are some workarounds that I tried out but that didn't work as hoped:
Start a new activity that shows the app logo in onPause so that it's screenshot is shown instead of the actual activitie's one. But the new activity takes too long to open and it disrupts the user experience.
Set the activitie's content view to an image of the app logo in onPause. That seemed like a great solution to me. Unfortunately, the screenshot for the recent tasks screen is taken at an unspecified time. During testing the app logo quickly appears before the app is closed when pressing 'Home' but the resulting screenshot shows the activity a short time before that.
Removing the sensitive data from the widgets (e.g. textView.setText("")) has the same problem of screenshot timing just mentioned.
Any alternative ideas or solutions to the listed workarounds?
I looked into this a couple of months ago for the same purpose as you.
Unfortunately, I had to conclude that it is simply not possible. I dug through the android source code and confirmed it.
There is no callbacks or methods from android that allows you to customize it (that works anyway). Besides FLAG_SECURE, this part of the code does not accept any input or change.
OnPause and similar lifecycle methods are called too late (the screenshot is taken already). All lifecycle methods that would hint that you're about to go into the background runs too late.
The image you see in the recent tasks is an actual screenshot - and thus isn't affected by changes you do (too late) to your view. That means you can't modify your view just-in-time (like making it invisible, replacing with something else, adding SECURE_FLAG, or any other obstruction of the view). As an aside, these images can be found on an emulator at /data/system_ce/0/recent_images.
The only exception is using FLAG_SECURE, which will prevent the screenshot from being taken of your application. I experimented with setting this FLAG in onPause and removing it in onResume, however as mentioned already these lifecycle methods runs after the screenshot is taken already, and thus had absolutely no effect.
As discussed in How to change the snapshot shown by recent apps list? there used to be a callback that you could use to customize the thumbnail: onCreateThumbnail. However, this does not work and it is never called. To be clear, the callback is still there, it is simply never called by the OS. The fact that it stopped working is poorly documented, but apparently was silently deprecated/removed in 4.0.3
As for the thumbnail itself, it is a screenshot taken serverside. It is taken before onPause is called (or in fact before any callbacks indicating that your activity is about to go into the background is called).
When your app does go into the background, your actual view is animated (to get that zoom-out transition). That animation can be affected through changes you do in onPause (if you're fast enough that is) (I experimented with setting opacity to 0 on the window among other things). This will however only affect the animation. When the animation is finished, the view is replaced by the screenshot taken earlier.
Also see these questions that discuss this:
When does Android take its recent apps switcher screenshot?
Show custom application image in task manager on ICS or JB
Android never call method onCreateThumbnail
Currently (28/10/2020) is impossibile customizing app thumbnail in recent apps screen.
As explained by #Dellkan in the previous answer, the onCreateThumbnail method is not called anymore by the OS.
Unfortunately, also the suggestion to create a kind of launcher/splash screen without the FLAG_SECURE flag to let the app take a screenshot of that activity is not working, because the screenshot is taken on the activity you see and not at the launch of the app.
You cannot even customize the color of window background when using FLAG_SECURE as reported here.
How about implementing a layout overlay on top of your entire activity?
Make it transparent, it's click-through by default, so no negative impact on UX while in use.
In onPause() set a half-transparent, blurred image as the background of that layout, the data will be scrambled behind it. In onResume() change the background to fully transparent again. Voila.
It might be faster than other types of overlays. The positive side effect is, if you do the unblurring as a short animation effect when the user goes back (with a proper library that uses C++ instead of Java), it might even look cool and the users wouldnt even mind seeing it.
I haven't tried this myself, but it's something you haven't tried yet.
Since onPause is called to late, I use WindowFocusChangeListener to observe when the Fragment loses focus. At this moment we can hide all view which show sensitive data:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// hide sensitive data when window moves to background (before system screenshot is captured)
myViewWithSensitiveData.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
There is a way to customize it. You need your Activities with sensitive data to FLAG_SECURE in onCreate before you setContentView. Then you need an empty Activity, which renders whatever you want to have as the customized thumbnail. This usually is some sort of splash screen. This new Activity needs to be the launcher and is the only Activity not FLAG_SECURE. This Activity is launched and in onResume starts your actual Activity with the sensitive data.
Android OS will take a screenshot of that new Activity at the beginning of your App. Unfortunately the users will also see this Activity for a short moment. Since every other Activity is FLAG_SECURE, Android OS will use the only available screenshot it made at the beginning.
Was looking for a solution and found some dirty things in case you don't want to use 'FLAG_SECURE'. It doesn't give a nice picture but protects data and doesn't prevent making screenshots for the user while they are in the app.
protected void onPause () {
this.getWindow().getDecorView().getRootView().setScaleX((float)200);
this.getWindow().getDecorView().getRootView().setScaleY((float)200);
super.onPause();
}
protected void onResume () {
this.getWindow().getDecorView().getRootView().setScaleX((float)1);
this.getWindow().getDecorView().getRootView().setScaleY((float)1);
super.onResume();
}
I think this can only achieve through BroadCastReceiver but there is no receiver present. So therefore you first disable default screenshot functionality in android and then implementing your own functionality to take screenshot and before taking screenshot you should blur your secure information.
This question already has answers here:
Best practice: AsyncTask during orientation change
(9 answers)
Closed 7 years ago.
Use case:
The user start the app that will load a captcha. The user fill the captcha and try to download some information.
Problem:
If the user rotate the device while downloading the Activity is destroyed. At the end of the execution the AsyncTask is trying to update the destroyed Activity and the result is a "View not attached to window manager".
"Wrong" solution:
I can mask the problem with a android:configChanges="orientation|keyboardHidden|screenSize" but with this the layout is not going to be updated to landscape.
What I'm asking:
Is it possible to change the orientation and change the "reference" of the Context passed to the AsyncTask?
This answer suggest to check if the dialog is not null, but it's not what I'm looking for.
Here he suggests to use a WeakReference (here a nice snippet on how to use it) but I didn't understand if it's what I'm looking for.
To be more explicit this is what I'm doing in the onPostExecute:
#Override
protected void onPostExecute(Auto result) {
progress.dismiss();
new DownloaderCaptcha(context).execute("");
((EditText)context.findViewById(R.id.editTextCaptcha)).setText("");
context.findViewById(R.id.progrBar).setVisibility(View.VISIBLE);
context.findViewById(R.id.captcha).setVisibility(View.INVISIBLE);
if(result != null) {
Storage.storeHistory(context, result.getTarga().getValue());
Intent i = new Intent(context, MenuActivity.class);
i.putExtra("result", result);
context.startActivity(i);
} else {
ErrorDialog.show(context, error);
}
}
Here are my tips:
Do not use android:configChanges to address this issue.
Do not use Activity#onRetainNonConfigurationInstance() to address it either (as this approach is deprecated).
Instead, use a retained worker Fragment. I've recently posted an article describing how to handle configuration changes using retained Fragments. It solves the problem of retaining an AsyncTask across a rotation change nicely. You basically need to host your AsyncTask inside a Fragment, call setRetainInstance(true) on the Fragment, and report the AsyncTask's progress/results back to it's Activity through the retained Fragment.
"Wrong" solution:
I can mask the problem with a
android:configChanges="orientation|keyboardHidden|screenSize" but with
this the layout is not going to be updated to landscape.
Actually it will.
android:configChanges Lists configuration changes that the activity
will handle itself. When a configuration change occurs at runtime, the
activity is shut down and restarted by default, but declaring a
configuration with this attribute will prevent the activity from being
restarted. Instead, the activity remains running and its
onConfigurationChanged() method is called.
Take a look at http://developer.android.com/guide/topics/manifest/activity-element.html#config
You can implement the layout for both Landscape and Portrait modes or to cancel your AsyncTask on configuration change.
My overriding question is this: In Android PreferenceActivity, how can I write an OnClickListener which will have the same functionality as pressing the Android back button as I navigate through PreferenceScreen defined menus? That is to say, I would like users of my App to explicity see a menu choice "Back" which will bring them to the previous menu, or bring them out of the menu activity to their previous activity if they are at the root of this particular PreferenceActivity session.
The android developer documents tell us
Note that this XML resource contains a preference screen holding another fragment, the Prefs1FragmentInner implemented here. This allows the user to traverse down a hierarchy of preferences; pressing back will pop each fragment off the stack to return to the previous preferences.
And they are correct about that. I navigate happily through my menus by clicking on PreferenceScreen items to get to that screen, and using the Android back button to go Back up a level. But I'm not sure a casual user really understands the "Back" button, I know I didn't until I read about it in Developer docs. SO I would would like them to have an explicit Preference defined menu choice whos OnClickListener duplicates the function of the Android back button.
So I tried to put in a Preference in my menu that would go back. Having determined that a not Overriden onBackPressed in a my subclass of PreferenceActivity just referred back to Activity.onBackPressed() which merely calls finish(), I tried this OnClickListener:
private OnPreferenceClickListener clickFinishesSuccessfully = new OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
finish();
return true;
}
};
As it turns out, this did NOT do the same thing as pressing the back button! Pressing this button always took me out of the PreferenceActivity entirely, back to the Activity from which I had called my PreferenceActivity. Specifically, it did NOT navigate back through my menus no matter how deep I was when I clicked it.
I am guessing here: When I have gotten to a submenu by clicking an onscreen preference which is really a PreferenceScreen, I am no longer in my own PreferenceActivity. I must be in some other Activity?
So my functional question: what can I put in my OnClickListener of my "Back" Preference to get the same function as the Android Back button navigating through my menus?
I think the casual user should know about the back button. The button is used everywhere so it might be a problem getting used in the first day but after that it's natural. Without being used to the "back" button I can hardly imagine doing the everyday tasks.
The preference you want to add just duplicates functionality and doesn't provide a consistent way with the rest of the system. If Google was considering back being an uncommon thing for casual users would have added that option in phone's Settings which is also a PreferenceActivity.
I have an application containing multiple activities.
At the moment the whole application contains about 8 activities. First I show a splash screen for a few seconds where all the preferences are loaded and set up (from sharedPreferences) - these are saved in a "Setting" class I made for this - this class basicly just have a bunch of static variables, so all activities in the app can read these and modify them as they need to.
EDIT: More data is getting stored in this class as the app runs, some of this is from a webservice - the data is parsed into obejcts and references to these obejcts are saved in the Settings class too (or a list of the objects).
My problem is then, that when users press the HOME key, the current activity is put in the background. If the activity is in the background for a long time (a lot of users "close" apps by pressing home instead of back), and then reopened it shows the activity that was running before HOME was pressed.
As an example - lets say the user starts the app, sees the Splash screen for a few seconds. The splash screen then starts a new activity and calls finish on itself.
This means that now the Activity stack is just the MainActivity (main menu). In the MainActivity I supply all the buttons with listeners in the onCreate method, and most buttons require some info from the Settings class mentioned above.
When I then press HOME and reopens the app a few hours later, none of the buttons works anymore - seems the graphic is reacting etc, but something still goes wrong. One of the buttons that should work even with all settings wiped will just open a dialog with some text in it.
The listener:
Button b = (Button)v.findViewById(R.id.id_b1);
b.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Dialog dialog = createDialog(MainActivity.this, DIALOG_CONST1);
dialog.show();
}
});
I have some constantst for dialog types, as I'm using some custom designs for the dialogs I have created a class to created the dialogs for me.
From the activity containing the button (simplified a bit):
public static Dialog createDialog(final Context c, int dialogId) {
Dialog dialog = null;
CustomDialog.Builder customBuilder;
switch (dialogId) {
...
case d1:
customBuilder = new CustomDialog.Builder(c, DIALOG_CONST1);
//Sets up the parapters to create the dialog afterwards
customBuilder.setTitle("Header").setMessage("Content")
.setPositiveButton("", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
dialog = customBuilder.create(); //Creates the dialog from the above settings
dialog.setCanceledOnTouchOutside(true);
break;
...
}
... //Set the width of the dialog
return dialog;
}
The CustomDialog class extends the Dialog class and then depending on the const it
s supplied with it inflates one of several dialog layouts and adds content etc. Works great normally, but after the app have been paused by HOME for a while, and things go wrong no dialog is shown when I press the button. I do se a flash of a loading dialog on the other buttons, but nothing happens afterwards - no dialog is shown. Strange thing is that if I press on the button again in the middle of the screen, I can't press it (grahics dont react), but when pressing once in the side of the screen and then in the middle the graphics do react, the dialogs does get cancelled when pressing outside, so I'm wondering if there is some very slim "transparent" dialog in the middle or something - I just have no clue why this would happen.
EDIT: Actually looking through the variables I save in Settings class I see that I do save the width and height in pixels of the screen. Width used to set the width of the custom dialogs to a certain % of the screen width. IF the Settings gets wiped, that might explain why I don't see any dialogs as width is set to 0... I really can't seem to figure what is happening if it's not all my variables in the Settings class that gets wiped - everything really do point to that.
dialog.getWindow().setLayout(Settings._widthpx - (Settings._widthpx/5), dialog.getWindow().getAttributes().height); //Width = 80%
Actually I have to admit that I don't really know what is causing this, as I'm rather new to Android. I suspect that the GC does delete all my variables after a while when the Settings class haven't been used - causing all settings to be wiped when the user returns a few hours later. This however does not explain why the buttons doesn't work (one of them require no setting).
This main activity can start other activities, and these can again launch new activities.
I think all my problems can be solved if I can just force the whole app to close when HOME is pressed, and thereby force it to be started from scratch whenever the icon is pressed - forcing the listeners on the buttons and the settings to be loaded.
I read on here about "android:clearTaskOnLaunch", " android:launchMode" and "android:finishOnTaskLaunch" but I'm not quite sure how to use these correct.
Anyone who could either explain to me why the buttons does not work, or what might happen to the variables in my Settings class when the app has been in the background for a while, or maybe give me a few good hints on how to use the "activity" settigns properly.
EDIT: The app will be running Android 1.6+, so I can't use any newer functions or anything...
Thank you very much
DO NOT override home key functionality. Maybe you'll find something, somewhere, that would allow you to do this. A god-fearing, standards-embracing application would not in any way override the home key.
Just put android:clearTaskOnLaunch=true in your manifest. This will ensure that your main activity will be launched everytime you press the launcher icon.
I suspect that the GC does delete all my variables after a while when
the Settings class haven't been used - causing all settings to be
wiped when the user returns a few hours later
Android is not that evil to do this to your app. It may kill your application, services, and whatever is running on the background after some time of inactivity and/or need for more memory, but will never leave your application hanging in there without your variables.
I had actually customized this for android 2.0 development 2.0.May be better options are available now.What I did was to declare all activities global and when you click home button from anywhere we check whether each of them is not null.If not null close them and set references to null.The activities are only declared globally.They are defined only when they are to be used.
If you are using Android 4.0 or higher, you can enable "Don't keep activities" in Developer options?. If you don't have a 4.0 device, then use the emulator.
you have to kill all the activities that are on the top of current activity on Home button and
for do that you have to override Home key functionality in which you have to write
Intent intent = new Intent(context,login.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
this will clear all the activities on the top of that activity where you press home key and switch it to login activity and
if you don't want to override Home key functionallity then you have to write only one in Androidmanifest.xml that is android:clearTaskOnLaunch=true from this when ever you click on app icon it will starts your 1st activity
I Suggest the second approach is best suted for you.
For posterity, in my app I just added finish(); in the onStop() method after everything i do there and it worked as a charm.