Customize a pretty practical lock screen app, if you can win the user's approval, replace the system with the lock screen, is definitely a small day live entrance. This time is just a summary of the difficulties in the recent research on the Android platform's lock-screen app development.
First, the preface
The general principle of the lock screen is very simple. Monitor the system's bright screen broadcast, in the screen when the display of their own lock screen interface, the user in the lock screen interface on a series of actions to unlock. Some mobile phone start lock screen interface process will be very card, so it will be obvious to see the screen after the start of the screen lock interface delay, so you can also choose to monitor the system to screen out the broadcast, the screen is turned off when the lock screen interface is ready, direct screen display (the screen after your app will be more likely to be killed, this should pay attention to do the protection).
Also need to note that the screen and the screen broadcast, Screen_on/screen_off are only dynamic monitoring, so to open a service to register, the service from the start and protect the job to do well.
The basic implementation details are not much said, this article will only talk about a few difficulties encountered.
Second, the difficulty in the realization of the lock screen
1. Screen Home key
Since it is a lock screen interface, of course, only through the interface of some sliding or input action to unlock the screen, can not be simply directly by the home button a press, the untied. From 4.0 onwards, home directly in the framework layer is the system response to the desktop, the Third-party applications can no longer use the Activity.onkeydown method to monitor and intercept the home key, although also symbolically retains the home key keycode to be compatible, but the home key press Go, and will not callback this method.
In addition to onkeydown, there is no other way to monitor the home button, some. Foreground app back to the background there will be broadcast action_close_system_dialogs, received broadcast intent after the analysis of the "reason" parameters, you can know what the reason for the exit. Home Key Press, reason is "HomeKey", the recent task key pressed, reason is "Recentapps".
This is certainly not the final solution, because some of the Samsung ROM does not have this broadcast. And the broadcast means just to inform you that the framework layer has put your application back to the desktop, you can monitor the home button, but there is no way to intercept the home key. Perhaps think of can monitor the home button, immediately put their own activity and re-open the display, I tried, the home key press after the startactivity will have a delay of 3 seconds, this should be Google had thought we would do so, did such a delay program.
Direct interception doesn't work, think of another way. By pressing the home key is to return the system to the launcher (the Desktop launcher), if our lock-screen activity itself is launcher, then the push of the key will not be equal to our lock-screen activity, and it will prevent it from turning off the lock screen activity.
How to declare your activity as launcher and add intent-filter to the activity:
<intent-filter>
<action android:name= "Android.intent.action.MAIN"/>
<category android: Name= "Android.intent.category.HOME"/>
<category android:name= "Android.intent.category.DEFAULT"/>
</intent-filter>
In this way, the newly installed app will be a launcher app, so when you press the home button for the first time, there is a window that prompts you to choose which launcher to enter and select our own activity so that the home key is taken over by us.
However, there is an obvious problem, if you do not press the home button in our lock screen interface, we will also enter the lock screen activity. Of course, the solution is also simple, when we press home to enter the OnCreate of the lock screen activity to make a judgment, if the previous front activity is a lock screen activity, then do not have to handle the home key, if not a lock screen activity, That's going to turn off the lock screen activity and jump to the user's real desktop launcher. The real desktop launcher is which one we can look for:
list<string> pkgnamest = new arraylist<string> ();
list<string> actnamest = new arraylist<string> ();
list<resolveinfo> Resolveinfos = Context.getpackagemanager (). Queryintentactivities (Intent, PACKAGEMANAGER.MATCH_DEFAULT_ONLY); for
(int i = 0; i < resolveinfos.size (); i++) {
string string = Resolveinfos.get (i). activityinfo.packagename;< C5/>if (!string.equals (Context.getpackagename ())) {//own launcher do not
pkgnamest.add (string);
string = Resolveinfos.get (i). Activityinfo.name;
Actnamest.add (string);
}
}
If the actual launcher is only one, then jump directly to the past can be:
ComponentName componentname = new ComponentName (Pkgname, actname);
Intent Intent = new Intent ();
Intent.setcomponent (componentname);
Context.startactivity (intent);
(activity). Finish ();
If the phone is installed with multiple launcher (such as 360 desktop type of app) will be a bit more trouble, need to show a list of users to choose which launcher, this in product form may make users feel a little puzzled.
Now, if you click the Home button in another app, it jumps to our lock screen activity and jumps to the real launcher. There may be activity flashes in the scene, affecting the user experience. The best way to do this is to get another activity to act as a home key jump activity, which is set to be transparent and will not be perceived by the user. Thus, the product shape becomes, the lock screen activity presses the home key, jumps to the transparent activity, jumps back to the lock screen activity, is equivalent to the home key is not valid; Other app presses home key, jumps to transparent activity, jumps to the real desktop.
To implement transparent activity, simply declare in XML
Android:theme= "@android: Style/theme.translucent.notitlebar"
Such an interface is transparent, actually occupy the top of the screen, so jump remember to finish off, otherwise it will block the interface after the jump interaction. In addition, Theme.nodisplay can also set the activity is not visible, but not occupy, but the author realized that the Nodisplay activity can not be set to launcher (after setting will play the window to let you reset, so repeated)
2. The Suspension window realization Way
Because the home key can not be directly blocked by the restrictions, activity implementation of the lock screen will need to go around a lot of road. So some lock-screen applications will use the suspension window to achieve, the suspension window can ignore the home button, when the home button will not fall back to the background. So there's no need to tangle on the home key issue. Suspension window Unified by WindowManager to manage, the specific implementation is relatively simple, I do not repeat, there is a pit to note that the suspension window needs to declare permissions:
<uses-permission android:name= "Android.permission.SYSTEM_ALERT_WINDOW"/>
Some mobile phone settings, the default is not to use the authorization of the use of the suspension window, so the application should also consider to guide the user Authorized suspension window.
In addition, some emergency unlock scenes, such as call answering, alarm processing, for the implementation of the lock screen interface, the system will automatically hide all the foreground activity, so that users directly to deal with these scenes. But the suspension window will cover the scene, so encounter these scenes, the suspension window to achieve the lock screen interface to handle these special scenes of automatic unlock.
3. Disable System Lock screen
With its own lock screen interface, you need to disable the system lock screen, so as to avoid the user needs to unlock the situation two times.
First we need to know if the user has a lock screen set, as follows:
For APIs level 16 and above, you can use the following method to determine whether a lock is available:
((Keyguardmanager) Getsystemservice (Context.keyguard_service)). Iskeyguardsecure ()
For API level 15 and the following SDK, you can use reflection to determine:
try {
class<?> clazz = Class.forName ("Com.android.internal.widget.LockPatternUtils");
Constructor<?> constructor = Clazz.getconstructor (Context.class);
Constructor.setaccessible (true);
Object utils = Constructor.newinstance (this);
Method method = Clazz.getmethod ("Issecure");
Return (Boolean) Method.invoke (utils);
catch (Exception e) {
e.printstacktrace ();
}
OK, the user set the system lock screen, how to turn it off? Some predecessors suggested this approach.
Keyguardmanager km = (keyguardmanager) getsystemservice (context.keyguard_service);
Keyguardmanager.keyguardlock Keyguardlock = Km.newkeyguardlock ("");
Keyguardlock.disablekeyguard ();
Requires permissions
<uses-permission android:name= "Android.permission.DISABLE_KEYGUARD"/>
But after the author test, this method can only disable the sliding lock, if the user is set a pattern or pin lock, it can not be directly canceled. Disable the password lock or pattern lock is a very dangerous behavior, based on this, Google should not open it to developers, so now the lock screen application of the Disable lock method, are directly to the System lock screen settings interface, direct users to manually shut down. You can jump to the user's lock-screen setup interface with the following code:
Intent in = new Intent (settings.action_security_settings);
StartActivity (in);
This will also have some compatibility problems, such as, 360 mobile phone ROM does not set the System lock screen function in the security settings, so open the security settings of the interface can not find a cancellation system lock screen Place, this in a number of lock-screen applications do not do compatibility.
Third, the additional function of the difficulties
The above functions are directly aimed at the implementation of the lock screen itself. Lock screen Application In addition to itself can have "lock screen" function, there should be some other beautiful and practical functions, at least should be as far as possible to the system lock screen style close and play, only to facilitate acceptance by users.
1. Get notification
The new notification should be displayed in the lock screen interface, so we need to monitor the notification bar. Starting with the Android 4.3 (API 18), Google provides us with a Notificationlistenerservice class where third-party apps can be more easily accessible to the notification bar (Notification access), of course, Such sensitive permissions have to apply their own declarations, and also to guide the user to manually authorize. as follows, establish a Notificationmonitor class that inherits from Notificationlistenerservice and declares permissions:
<service android:name= ". Notificationmonitor "
android:permission=" Android.permission.BIND_NOTIFICATION_LISTENER_SERVICE ">
<intent-filter>
<action android:name= "Android.service.notification.NotificationListenerService"/ >
</intent-filter>
</service>
Then, as with the boot user shutting down the system lock screen, the user is instructed to authorize the notification bar use:
StartActivity (New Intent (settings.action_notification_listener_settings));
The following code can be used to check whether the right to use the notification bar has been obtained:
Private Boolean isnotificationlistenenabled () {
String pkgname = Getpackagename ();
Final String flat = Settings.Secure.getString (Getcontentresolver (),
"Enabled_notification_listeners");
if (! Textutils.isempty (flat)) {
final string[] names = Flat.split (":");
for (int i = 0; i < names.length i++) {
final componentname cn = componentname.unflattenfromstring (names[i));
if (CN!= null) {
if (textutils.equals (Pkgname, Cn.getpackagename ())) {return
true;
}
}
}} return
false;
}
Get the notice bar to use, the system notice column changes can be in the notificationmonitor inside the monitor heard:
public class Notificationmonitor extends Notificationlistenerservice {@Override public ibinder onbind (Intent Intent) {
Todo:return the communication channel to the service.
return null; @Override public int Onstartcommand (Intent Intent, int flags, int startid) {return Super.onstartcommand (intent,fla
Gs,startid); }//New notification arrives @Override public void onnotificationposted (Statusbarnotification sbn) {Super.onnotificationpos
Ted (SBN); //New notification arrival, API 21 new @Override public void onnotificationposted (Statusbarnotification SBN, Rankingmap ranking
MAP) {super.onnotificationposted (SBN, Rankingmap); //notification was removed @Override public void onnotificationremoved (Statusbarnotification sbn) {Super.onnotificationrem
Oved (SBN); //notification has been removed, API 21 adds @Override public void onnotificationremoved (Statusbarnotification SBN, Rankingmap ranking
MAP) {super.onnotificationremoved (SBN, Rankingmap);
//notification sort change, API 21 new @Override public void Onnotificationrankingupdate (Rankingmap rankingmap) {super.onnotificationrankingupdate (RANKINGMAP); //service the system notification bar to complete the binding callback, bind to receive notification bar callback, API 21 new @Override public void onlistenerconnected () {Super.onlistenerconnect
Ed ();
}
}
At the same time, Notificationlistenerservice also provides a cancelnotification and cancelallnotification method for removing notifications from the notification bar, Can be conveniently implemented in the Custom Lock screen interface removal notice.
The author realizes this class when found a pit, all the code is OK, notice the right to use also authorized, but to notify when there is no callback onnotificationposted, check the problem for a long time, then see someone on the Internet encounter the same problem, Another new class to copy the code past, OK, so it seems to be a compiler problem.
The service that gets the right to the notification bar is naturally guaranteed to be alive, and if killed, the Android system can reboot it. So usually see some applications require access to the notification bar, pay attention to this type of application will be permanently in the background. Of course, if the service's process crashes to a certain number of times, the Android system will be discouraged and will not restart the service until the next shutdown reboot, so it's best to put the service in a lightweight, independent process.
2. Get hotseat Area shortcuts
Desktop shortcuts are divided into two categories, the desktop area, which is the part of the screen scrolling, hotseat, which is placed on the bottom of the desktop that does not scroll with the screen. Shortcuts in user-defined hotseat areas are common applications. If you can also add this part of the lock screen to the quick start, it will be a more friendly function. The main problem with this is how to get a shortcut to the hotseat area.
The system shortcuts are stored in the Favorites table in the database file Launcher.db, as shown in the figure:
You can see the id,title and intent with the corresponding shortcuts, which are used to indicate the ID of the folder you are in, but you can see that some container are negative. This is why, the author looked at the Android launcher related to the source code, found that two sentences:
/**
* The icon is a resource identified by a package name and the integer ID.
* * Public
static final int container_desktop = -100;
public static final int container_hotseat =-101;
In other words, container-100 is the desktop area shortcut, container-101 is exactly the hotseat area to find the shortcut.
Now that you know how the shortcuts are stored, the next question is to find the path to the Launcher.db file.
In different versions of the Android native API, the Launcher.db store path is not the same because the package name of the Launcher launcher used by default is not the same.
Android API 7 and below:/data/data/com.android.launcher/databases/laucher.db
Android API 8~18:/data/data/com.android.launcher2/databases/laucher.db
Android API 19 and above:/data/data/com.android.launcher3/databases/laucher.db
And for a variety of third-party ROM, the use of strange laucher package name, this path is more chaotic:
HTC:/data/data/com.htc.launcher/databases/laucher.db
360:/data/data/net.qihoo360.launcher/databases/laucher.db
Huawei:/data/data/com.huawei.launcher3/databases/laucher.db
Millet:/data/data/com.miui.mihome2/databases/laucher.db
...
Of course, we do not directly read the database to get the information of the shortcut, the system Laucher will provide contentprovider to the external reading. Avoid the large pits that are compatible with the database path, and then fall into another big pit and read the shortcuts through provider, and the required permissions and URIs need to be compatible as well.
From the storage of shortcuts, how serious is the fragmentation of Android, so finally I decided not to go deep to the compatibility of implementation, this is the act of loss, interested in the implementation can look at this article, to determine whether a shortcut exists is how difficult: http:// Www.jianshu.com/p/dc3d ...
3. Get Wallpaper
The background of the lock screen interface and mobile desktop wallpaper consistent, not to make users feel abrupt, there are two ways to get wallpaper.
Activity style Mode
If the activity is the implementation of the lock screen interface, you can set up the activity of the theme can be used as a background wallpaper.
Android:theme= "@android: Style/theme.wallpaper.notitlebar"
Wallpapermanager mode
The suspension window mode of the lock screen interface is not theme, then you can get the wallpaper by Wallpapermanager.
Get the wallpaper manager
wallpapermanager wallpapermanager = Wallpapermanager
. getinstance (this);
Get the current wallpaper
drawable wallpaperdrawable = wallpapermanager.getdrawable ();
Turn the drawable into Bitmap
Bitmap BM = ((bitmapdrawable) wallpaperdrawable). Getbitmap ();
Mrootview.setbackgrounddrawable (New bitmapdrawable (BM));
This way in the millet and other imitation iOS on a screen of the desktop is OK, but in the native Android-like two-screen desktop (shortcuts and all apps are on different screens), the shortcut that screen gets the wallpaper is a whole large wallpaper, and the actual laucher is shown after the cut wallpaper. So the above method will be the size of the wallpaper to set the background. You need to go. According to the screen number of Laucher and currently is the first few screen to do the map, laucher total screen number can be found in the Workspacescreens table above launcher.db, and the specific current in the first few screen is the existence of launcher app memory instances, cannot be obtained. If you really want to cut, it is recommended to directly follow the screen wide high cut down the entire wallpaper left a screen.
The above is the entire content of this article, I hope to help you learn, but also hope that we support the cloud habitat community.