Android Application Background Loading System Dynamic Wallpapers

Posted by 2gd-2be-2rue on Fri, 14 Jun 2019 19:17:15 +0200

Android Application Background Loading System Dynamic Wallpapers

demand

Customer demand is always confusing. Our application and system launcher are two forms of desktop that exist together. Customer demand that application must support system desktop wallpaper. Static wallpaper is easy to achieve for this requirement, but dynamic wallpaper is a hassle, after all, we are just an application pseudo launcher, not on the real launcher source code.Desktop programs that make changes.

thinking

After checking a lot of data on the web, we have a little idea. Dynamic wallpaper does not run in the active interface. Although it exists in the android system as apk, it mainly runs in a wallpaper window.So we need to set our application theme to be transparent, then fit the window of the dynamic wallpaper to the acivity window of the application, and after looking at the source of android's native launcher 3, we find that the system also works this way.

Realization

1. Transparent Background

Set the theme of the main Activity of the application to be transparent

 <activity
            android:name=".activity.MainActivity"
            android:launchMode="singleTask"
            android:screenOrientation="landscape"
            android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

2. Wallpaper window fits Acivity window

By setting the properties of the current window, add a logo that shows the wallpaper window, attach the wallpaper window to the application background, and clear the logo when not needed

  if(isLiveWall){
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
        }else {
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
        }

3. Close the system dialog

After completing the first two steps, you can load the dynamic wallpaper of the current system into the application background, and if it's a non-launcher application, you're done.But because our application is a dual desktop with the home attribute of the desktop, it has been tested that a recent task's system dialog box and the native launcher desktop will overlay between the Activity window and the wallpaper window

It looks like this:


System window background overlap

In this way, the backgrounds overlap. It is assumed that the dialog box for the most recent task should be displayed on the wallpaper window by default. If it is not closed, it will be displayed in our transparent background, so similar system dialog boxes need to be closed.


Window Stack Chart with Multiple Transparent Windows


The reason the system launcher desktop will overlap is because launche3 also displays the wallpaper window on the default transparent background, and the wallpaper window will be displayed on the bottom of the system desktop window if both active windows are marked to display the wallpaper.
However, the default desktop for the system does not have these problems, so there should be a solution in the system launcher's source code

 protected void onNewIntent(Intent intent) {
        long startTime = 0;
        if (DEBUG_RESUME_TIME) {
            startTime = System.currentTimeMillis();
        }
        super.onNewIntent(intent);

        boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
                Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
                != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);

        // Check this condition before handling isActionMain, as this will get reset.
        boolean shouldMoveToDefaultScreen = alreadyOnHome &&
                mState == State.WORKSPACE && getTopFloatingView() == null;

        boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
        if (isActionMain) {
            // also will cancel mWaitingForResult.
            closeSystemDialogs();
    }
       public void closeSystemDialogs() {
        getWindow().closeAllPanels();

        // Whatever we were doing is hereby canceled.
        setWaitingForResult(null);
    }

In the onNewIntent callback we can see that when the launcher receives an Intent returning to the desktop, it calls closeSystemDialogs() to close the system dialog, which can be closed by adding this code to our code

protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if(intent.getAction().equals(Intent.ACTION_MAIN)){
            closeSystemDialogs();
        }
    }

     private void closeSystemDialogs() {
        getWindow().closeAllPanels();
        Intent close = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
        sendBroadcast(close);
    }

4. Set up dynamic Wallpapers

This is not the end yet. You need to set a default dynamic wallpaper according to the user's needs, and then look for the setup API that only static Wallpapers have. Dynamic wallpapers, unless you write them yourself, do not provide an open API, but
Stack Overflow
The above finds a way to do this through reflection, but it must have system privileges

 <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />

Then call the hide class's method IWallpaperManager.setWallpaperComponent(ComponentName) to implement it

 public void setLiveWallPaper(String wallPaper){
        try
        {
            WallpaperManager manager = WallpaperManager.getInstance(mContext);
            Method method = WallpaperManager.class.getMethod("getIWallpaperManager", new Class[]{});
            Object objIWallpaperManager = method.invoke(manager, new Object[]{});
            Class[] param = new Class[1];
            param[0] = ComponentName.class;
            method = objIWallpaperManager.getClass().getMethod("setWallpaperComponent", param);

            //get the intent of the desired wallpaper service. Note: I created my own
            //custom wallpaper service. You'll need a class reference and package
            //of the desired live wallpaper
            Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
            intent.setClassName("com.android.noisefield", "com.android.noisefield.NoiseFieldWallpaper");
            //set the live wallpaper (throws security exception if you're not system-privileged app)
            method.invoke(objIWallpaperManager, intent.getComponent());
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

intent.setClassName("com.android.noisefield", "com.android.noisefield.NoiseFieldWallpaper");
Replace this with the package name, wallpaper service name of the dynamic wallpaper you define or any other Android wallpaper that comes with it

summary

It took a total of three days to complete this requirement. It was too easy to think about at first, but the source code and StackOverflow helped, so you have to read more Android system source code in the future.

Topics: Android Windows Attribute