圖凌閃屏頁及Android彩蛋探究

100斤發表於2019-01-24

原文:blog.fiftykg.com/

前言

通過本文,你可以 1、瞭解一種特別的閃屏 2、瞭解android版本彩蛋的實現原理 3、獲得一個android各版本彩蛋的demo

特殊的閃屏

在體驗App時發現了一款叫‘圖凌’的app,閃屏頁非常特別。從下圖可以看到,是一個以桌面桌布為背景的頁面。

圖凌動態效果

這種閃屏效果讓人眼前一亮,所以非常好奇他的實現原理。在不破解apk的情況下(破解失敗,有騰訊樂固加固==),猜想了幾種實現方式:

1、通過Api獲取桌布,然後設定Activity的背景
2、特殊的Activity theme
複製程式碼

在逐個驗證猜想之前,想到了一個頁面的實現與‘圖凌’的閃屏非常相似,就是Android的版本彩蛋(設定-關於手機-Android版本點選3下)。而這個頁面是可以找到原始碼的。

扒原始碼

開啟彩蛋頁面,執行以下命令,可以得知Activity的名字是PlatLogoActivity。

adb shell dumpsys activity | grep "Focus"

mFocusedActivity: ActivityRecord{2829baa u0 android/com.android.internal.app.PlatLogoActivity t4844}
mFocusedStack=ActivityStack{d93bf0d stackId=1, 4 tasks} mLastFocusedStack=ActivityStack{d93bf0d stackId=1, 4 tasks}
複製程式碼

(或者 adb shell dumpsys activity top)

通過線上原始碼平臺Xref,找到PlatLogoActivity,原始碼就不貼了,因為重點並不在PlatLogoActivity.java,而是在AndroidManifest,theme才是關鍵!也就是開始的第二個猜想。

<activity android:name="com.android.internal.app.PlatLogoActivity"
android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
android:configChanges="orientation|keyboardHidden"
android:process=":ui">
</activity>
複製程式碼

試一試

通過原始碼基本瞭解了實現原理,但沒有跑一下Demo是不靠譜的。 PlatLogoActivity的實現非常獨立,沒有太多依賴。所以copy了一下各個版本的PlatLogoActivity做了一個Demo。

Github: github.com/PortgasAce/…

圖凌閃屏頁及Android彩蛋探究

圖凌閃屏頁及Android彩蛋探究

探究

現在我們已經知道theme可以方便的實現‘圖凌’的閃屏效果。那麼程式碼可以實現嗎?官方有開放相應的api嗎?

這些問題的答案可以從theme的實現原理入手。通過google或者xref搜尋theme的一些關鍵字,最終可以找到PhoneWindow#generateLayout有這樣一段解析theme標籤的程式碼:

... //省略了很多標籤解析
if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {
requestFeature(FEATURE_SWIPE_TO_DISMISS);
}

if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}
...//省略了很多標籤解析
複製程式碼

theme的主要實現就是通過window#requestFeaturewindow#setFlags方法改變樣式。

臨摹著寫了Theme.Wallpaper.NoTitleBar.Fullscreen的java實現,見github:PlatLogoActivityNoStyle 主要程式碼:

requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().getDecorView().setWillNotDraw(true);
getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
複製程式碼

到此為止,猜想2通過theme和java實現都是驗證了。那麼其他猜想是否可行呢? 答案當然是可以的啦。通過sdk提供的WallpaperManager可以獲取桌面桌布。

Drawable bg = WallpaperManager.getInstance(context).getDrawable();
rootView.setBackground(bg);
複製程式碼

以上程式碼就可以給Activity設定背景為桌面桌布。

但是有一個問題。動態桌布(live wallpapewr)時通過該方法獲取的drawable不但不會動,而且是錯誤的圖片。

其實liveWallpaper獲取的正確姿勢是通過wallpaperManager#getWallpaperInfo#loadThumbnail

  private Drawable getWallpaperDrawable() {
    Drawable wallpaperDrawable;
    PackageManager pm = getApplicationContext().getPackageManager();
    WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
    if (wallpaperManager.getWallpaperInfo() != null) {
      /*
       * Wallpaper info is not equal to null, that is if the live wallpaper
       * is set, then get the drawable image from the package for the
       * live wallpaper
       */
      wallpaperDrawable = wallpaperManager
          .getWallpaperInfo().loadThumbnail(pm);
    } else {
      /*
       * Else, if static wallpapers are set, then directly get the
       * wallpaper image
       */
      wallpaperDrawable = wallpaperManager.getDrawable();
    }
    return wallpaperDrawable;
  }
複製程式碼

但是問題還是沒有完全解決,背景不會動!每次呼叫loadThumbnail返回的圖片都是一樣的,因此猜想1只使用於靜態桌布。

以上。

巨人的肩膀

Andriod中Style/Theme原理以及Activity介面檔案選取過程淺析:blog.csdn.net/qinjuning/a…

相關文章