I am creating simple widget for contact management, which allows user to dial and send sms to desired contact.
It works fine as "normal widget", but when I add it as lockscreen widget on Android 4.2, sms app or dial app does not start.
Well in fact they star, but "behind" lockscreen, so user still must manually unlock screen to be able to dial/send sms.
I searched web for some solution, but nothing come in handy.
I' am aware of FLAG_DISABLE_KEYGUARD or FLAG_SHOW_WHEN_LOCKED, but since sms/dial apps are not "mine" so i dont know if they set up proper flag.
As a workaround i tried to create my activity which set those flag and then simply starts desired one (dial or sms), but this does not help.
There is a way to unlock screen, but this involves using KeyguardManager and KeyguardLock (which work fine), but in a result of using KeyguardLock.newKeyguardLock() I end up with phone not being able to turn lock automatically, surely because I do not release this lock (it causes lock to appear again, which is not what i want).
In fact, this widget should work simmilarly to default sms widget or mail widget on lock screen?
So, my question is, how to achieve that and start new activity from lockscreen?
Well, i found solution myself. it turned out i was close :)
To launch 3rd party app/activity, simplest solution is to create some kind of proxy activity, which will set proper flags on window and then launches desired activity and FINISHES.
sample code is shown below:
calling intent in widget (calling proxy):
#Override
public void onReceive(Context context, Intent intent) {
Utilities.printLog(TAG, "onReceive");
Utilities.printLog(TAG, "intent: " + intent);
if (intent.getAction().equals(ACTION)) {
final String number = intent.getStringExtra(EXTRAS);
Toast.makeText(context, "Selected number: " + number,
Toast.LENGTH_SHORT)
.show();
/** REMOVING KEYGUARD RECEIVER **/
// not really an option - lock is still holded by widget and screen
// cannot be locked again ;(
// KeyguardManager keyguardManager = (KeyguardManager) context
// .getSystemService(Context.KEYGUARD_SERVICE);
// KeyguardLock lock = keyguardManager
// .newKeyguardLock(Context.KEYGUARD_SERVICE);
// lock.disableKeyguard();
final Intent activity = new Intent(context, MainActivity.class);
activity.putExtras(intent.getExtras());
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
activity.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(activity);
}
super.onReceive(context, intent);
}
in proxy activity just call:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
final Intent callingIntent = getIntent();
final String actionToLaunch = callingIntent.getStringExtra(ContactsStackWidgetProvider.ACTION);
final String number = callingIntent.getStringExtra(ContactsStackWidgetProvider.EXTRAS);
final Intent activity = new Intent();
if (actionToLaunch.equals(Intent.ACTION_DIAL)) {
activity.setAction(Intent.ACTION_DIAL);
activity.setData(Uri.parse("tel:"+number));
} else if (actionToLaunch.equals(Intent.ACTION_SENDTO)) {
activity.setAction(Intent.ACTION_SENDTO);
activity.setData(Uri.parse("sms:"+number));
} else {
throw new IllegalArgumentException("Unrecognized action: "
+ actionToLaunch);
}
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startActivity(activity);
finish();//it is important to finish, but after a small delay
}
}, 50L);
}
Related
I have an old android app with an alarm where the user can set a time and alarm behavior like ringtone and ring duration, volume, if it only vibrates or if it's silent. It was working but it stopped working a long time ago due to new android rules and just now I had time to update it but I can't find an up-to-date answer to these questions.
Once the alarm is due I need to open an activity, on top of anything, including another app or lock screen, just like the default android alarm or an incoming call. This activity will have a message and a button to dismiss. Once dismissed I need the phone state back to where it was before.
I can set the alarm and it works, the BroadcastReceiver opens the activity if the app is in the foreground or background, but not if the app was forced to close. It pops-up the default crash message that my app stopped. Plus, I have no idea of how to make it opens on top of any lock screen.
I'm guessing it's because of missing permissions or flags.
I'm working with Xamarin so if you don't know it all you need to know is that the activities metadata are set on the class and compiled afterwards to the manifest.
Here's the activity I want to show when the alarm ends (not the main activity):
[Activity(Label = "#string/app_name", Theme = "#style/MainTheme.StopAlarm", LaunchMode = Android.Content.PM.LaunchMode.SingleTask)]
public class StopAlarmActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.StopAlarmLayout);
Bundle bundle = Intent.Extras;
string msg = bundle.GetString("message");
TextView nameTV = FindViewById<TextView>(Resource.Id.alarmTextView);
nameTV.Text = msg;
Button okButton = FindViewById<Button>(Resource.Id.okButton);
okButton.Text = AppResources.OK;
okButton.Click += (object sender, EventArgs args) =>
{
Finish();
};
}
}
My receiver:
[BroadcastReceiver]
public class AlarmReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
Toast.MakeText(context, "Alarm worked.", ToastLength.Long).Show();
string msg = intent.Extras.GetString("message");
var myIntent = new Intent(context, typeof(StopAlarmActivity));
Bundle bundle = new Bundle();
bundle.PutString("message", msg);
myIntent.PutExtras(bundle);
myIntent.SetFlags(ActivityFlags.FromBackground);
myIntent.SetFlags(ActivityFlags.NewTask);
myIntent.AddCategory(Intent.CategoryLauncher);
Forms.Context.StartActivity(myIntent);
}
}
Please don't waste your time telling me that this behavior should be avoided. It's an alarm, it's meant to wake him up if set by himself. Plus, the default android alarm doesn't do what my users want to do. The alarms are previously set based on some data in the app as a suggestion. The user has to run them and it's highly customizable for his needs.
Just have to set the correct window flags. This works from API 14(?) up to Oreo Beta:
Activity:
[Activity(Label = "AlarmActivity")]
public class AlarmActivity : Activity, View.IOnClickListener
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Alarm);
FindViewById<Button>(Resource.Id.myButton).SetOnClickListener(this);
}
public override void OnAttachedToWindow()
{
Window.AddFlags(WindowManagerFlags.ShowWhenLocked |
WindowManagerFlags.KeepScreenOn |
WindowManagerFlags.DismissKeyguard |
WindowManagerFlags.TurnScreenOn);
}
public void OnClick(View v)
{
Finish();
}
}
AlarmManager Test:
Call this routine and the alarm manager will start up the AlarmActivity in one minute, so apply this within a Button click/listener and lock your screen:
using (var manager = (Android.App.AlarmManager)GetSystemService(AlarmService))
using (var calendar = Calendar.Instance)
{
calendar.Add(CalendarField.Minute, 1);
Log.Debug("SO", $"Current date is : {Calendar.Instance.Time.ToString()}");
Log.Debug("SO", $"Alarm will fire at {calendar.Time.ToString()}");
var alarmIntent = new Intent(ApplicationContext, typeof(AlarmActivity));
var pendingIntent = PendingIntent.GetActivity(this, 0, alarmIntent, PendingIntentFlags.OneShot);
manager.SetExact(AlarmType.RtcWakeup, calendar.TimeInMillis, pendingIntent);
}
You're actually using the wrong Context in the OnReceive method. Instead of doing this:
Forms.Context.StartActivity(myIntent);
try this:
context.StartActivity(myIntent);
When you force your application to close, there won't be any Forms.Context to use.
Update: If you want to show the Activity on the lock screen, you need to set showForAllUsers to true. Here's the description from the Android documentation:
Specify that an Activity should be shown even if the current/foreground user is different from the user of the Activity. This will also force the android.view.LayoutParams.FLAG_SHOW_WHEN_LOCKED flag to be set for all windows of this activity.
Earlier you would've used a much more descriptive showOnLockScreen flag but it was deprecated in API level 23.
The problem is that if my app is running and the device (screen) is locked, the app is restarted while the device is locked (I know because I can hear the sound of my app at startup).
[Edit]
This seems very complicated. I think it would be easier to turn off sounds in the app, but I do not know how to do this only when the device is asleep:
public void playSound(int id){
if(!DEVICE_IS_ASLEEP())
snds[id].play(soundID[id], 1, 1, 0, 0, 1);
}
you may registerReceiver using Context (probably inside Service)
//assuming user starting Service by press smth in app (or simple open it), so the screen will be on for sure
boolean screenOn=true;
//put this inside your onCreate
private void initBroadcasts(){
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
//new feature from API 17 - screensaver (?)
if(android.os.Build.VERSION.SDK_INT>=17){
filter.addAction(Intent.ACTION_DREAMING_STARTED);
filter.addAction(Intent.ACTION_DREAMING_STOPPED);
}
screenOn = getScreenUnlocked();
this.registerReceiver(screenBroadcastReceiver, filter);
}
screenBroadcastReceiver is a BroadcastReceiver as below:
private BroadcastReceiver screenBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent myIntent) {
if(myIntent.getAction().equals(Intent.ACTION_SCREEN_ON))
screenOn=getScreenUnlocked();
else if(myIntent.getAction().equals(Intent.ACTION_SCREEN_OFF))
screenOn=false;
else if(myIntent.getAction().equals(Intent.ACTION_USER_PRESENT))
screenOn=true;
else if(android.os.Build.VERSION.SDK_INT>=17){
if(myIntent.getAction().equals(Intent.ACTION_DREAMING_STARTED))
screenOn=false;
else if(myIntent.getAction().equals(Intent.ACTION_DREAMING_STOPPED))
screenOn=getScreenUnlocked();
}
}
};
check if screen is unlocked:
private boolean getScreenUnlocked(){
KeyguardManager kgMgr =
(KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
return !kgMgr.inKeyguardRestrictedInputMode();
}
When configuration changes happens with app like screen lock, the activity is restarted. You will get so many answers on stackoverflow to avoid this problem like this; but according to Google Engineers, it is bad practice to retain the activity. You will get the proper answer about how to avoid this problem here
I built a custom lock screen app that uses a broadcast receiver and service to listen for when the user turns on or off the screen and from there launch my activity. The activity is supposed to completely replace the lock screen. In order to do this my app is supposed to disable the android stock lock so that my app can function as the new lock screen.
Instead what happens is once the application is first installed the the service first started the application appears to be working. and when the user first turns off the screen of their phone when they turn it back on they are presented with my app running on top and is able to unlock their phone with my app. But then once inside the android OS if the user presses the home button the next time they turn off the screen and turn it back on instead of being brought back to my application they are brought to the stock unlock screen with my application open underneath it, when it should be on top.
Here is my code:
My Service:
public class MyService extends Service {
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.d("MyService","Service STARTED");
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
final BroadcastReceiver mReceiver = new ScreenReceiver();
registerReceiver(mReceiver, filter);
}
}
My broadcast receiver:
public class ScreenReceiver extends BroadcastReceiver {
public static ArrayList<String> runningApplications = new ArrayList<String>();
private Context ctext;
public static boolean screenIsLocked;
public static KeyguardManager keyguardManager;
public static KeyguardLock lock;
#Override
public void onReceive(final Context context, final Intent intent) {
ctext = context;
keyguardManager = (KeyguardManager)ctext.getSystemService(Activity.KEYGUARD_SERVICE);
lock = keyguardManager.newKeyguardLock(Context.KEYGUARD_SERVICE);
lock.disableKeyguard();
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
screenIsLocked = true;
Log.d("ScreenReceiver", "False");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
screenIsLocked = false;
Log.d("ScreenReceiver", "True");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
}
}
My activity that is started is basically empty with just one unlock button that calls finish(); when pressed.
The behavior of keyguard-related logic can vary from device to device. That's because lockscreens are often custom-made by device manufacturers (i.e. not stock), some of them respect the keyguard logic you use, some don't.
Also, afaik the newer way to control keyguard is to use window flags:
// inside activity
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
This will not solve the problem though, devices still have their say about this.
E.g. from my experience, Galaxy Nexus will show your activity's window above keyguard but will not dismiss it (you'd think Google-branded device should respect the flag, eh), so if you hit the back button in your activity - you'll get standard lockscreen --- while HTC One X seems to handle the dismiss part properly: your activity window will cause standard lockscreen to get dismissed as expected.
I found no way to force all devices to behave properly. Android API is not meant to enable you to create custom lock screens (at least not currently). Take a look at the ones in the store - they all have the exact same problem of not being stable enough.
As Dianne Hackborn says in this Google Groups answer, anything you can do in this regard is a hack so expect it to break from time to time.
I tried to compile your code and got the same error you were talking about. I tried to modify it to make it to work and finally got the problem!!!
public class ScreenReceiver extends BroadcastReceiver {
public static ArrayList<String> runningApplications = new ArrayList<String>();
private Context ctext;
public static boolean screenIsLocked;
public static KeyguardManager keyguardManager;
public static KeyguardLock lock;
#Override
public void onReceive(final Context context, final Intent intent) {
ctext = context;
keyguardManager = (KeyguardManager)ctext.getSystemService(Activity.KEYGUARD_SERVICE);
lock = keyguardManager.newKeyguardLock(Context.KEYGUARD_SERVICE);
lock.disableKeyguard();
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
screenIsLocked = true;
Log.d("ScreenReceiver", "False");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
screenIsLocked = false;
Log.d("ScreenReceiver", "True");
Intent intenti = new Intent();
intenti.setClass(context, starterActivity.class);
intenti.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intenti.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intenti);
}
}
With this change to the broadcast receiver class I was able to overcome the problem
Try it and tell me if there is any problem.
EDIT:I think the problem might lie in the finish() method....Android dumps apps when it requires memory...I think finish() might be helping android in trashing the app(and this might be the reason why your problem occurs randomly)
Im building a custom home screen with a custom lockscreen.
When the screen turn off, I launch the lock screen (activity),
However, when the lock screen is killed (by "finish()"), it goes back to
the last activity in my homescreen apk, and not to the real activity (apk) that
was visible right before the screen went off.
For example, if i'm in Calculator application, or in Clock applicaiton, And the lock screen turns on, When the lock screen activity is finished, It doesn't return to Calculator/Clock
Here's where I register the lock screen (in the main launcher activity) for receiving screen on/off events:
private void doLockScreenOperations()
{
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
KeyguardLock lock = keyguardManager.newKeyguardLock(KEYGUARD_SERVICE);
lock.disableKeyguard();
IntentFilter lockfiFilter = new IntentFilter();
lockfiFilter.addAction(Intent.ACTION_SCREEN_OFF);
lockfiFilter.addAction(Intent.ACTION_SCREEN_ON);
getApplicationContext().registerReceiver(new LockScreenReceiver(), lockfiFilter);
}
Here's the receiver itself, where I launch the lock screen's activity:
public class LockScreenReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF))
{
if (LockScreenActivity.isLockScreenAlive == false)
{
Intent lockIntent = new Intent(context, LockScreenActivity.class);
lockIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(lockIntent);
}
}
else if (action.equals(Intent.ACTION_SCREEN_ON))
{
}
}
}
The LockScreenActivity is, at that moment, a simple activity with a button
that is called finish() when the button is clicked.
I have no idea how to fix this.
Thanks in advance!
I'm not entirely sure mate, but you have to consider this:
Intent.FLAG_ACTIVITY_NEW_TASK starts a fresh new group of views
so logically you don't have a previous activity to go back to when exiting your lock screen.
I'm building one myself, very similarly to how you do it actually.
but unfortunately having these problems:
-it loads to slow sometimes
-it loads whenever it "feels" like :/
nm, goodluck mate
I have read threads on the matter but all of them were regarding launching an activity WHEN the screen is locked or when it is unlocked. However, I need for my program to launch a new activity regardless of the screen being locked or not.
I am using gps and proximity alerts to check when a destination has been reached.
My activity registers a ProximityAlertReceiver such that:
private class ProximityAlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String key = LocationManager.KEY_PROXIMITY_ENTERING;
Boolean entering = intent.getBooleanExtra(key, false);
if (entering) {
System.out.println("You have entered the proximity area");
} else {
System.out.println("You have exited the proximity area");
}
Bundle bundle = intent.getExtras();
int status = bundle.getInt("status");
Intent i = new Intent();
i.setClass(context, MEcheScreen.class);
Bundle bundle1 = new Bundle();
bundle1.putInt("status", status);
i.putExtras(bundle1);
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(i);
}
}
So, when I a proximity alert fires, a new activity will be started.
I am using the public void onNewIntent(Intent newIntent) {} method to handle when the new activity is launched.
So, the problem is, when the screen is locked and a proximity alert is fired, the Intent in the ProximityAlertReceiver class does not get started.
I tried to use the keyguardmanager to disable the keyguard. However, after it has been disabled, it returns to the main screen of the program, but the activity is still not started until I press a button or tap the screen.
Maybe you should fire a notification instead (with sounds and flashing lights even if necessary) from which the activity can be launched IF wanted by the user, which would seem the right thing to do..
a couple of things are needed ...
in your reciever add the following when launching the activity
intenet.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
in the onCreate of the activity add
KeyguardManager mKeyGuardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE);
KeyguardLock mLock = mKeyGuardManager.newKeyguardLock("name of my app");
mLock.disableKeyguard();
this works for me in an app that is lauched at device startup
I also then mess with
Settings.System.getInt(cr,Settings.System.SCREEN_OFF_TIMEOUT,0);
but that is another story