android 8.1 Launcher添加切换主题功能
实现思路:APK包的方式,将主题资源放在Android工程上通过打包安装实现主题的替换。
修改方法:
一:面板添加theme控件
修改Launcher3/res/layout/overview_panel.xml
<!-- zrx add start -->
<TextView
android:id="@+id/theme_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="4dp"
android:drawableTop="@drawable/ic_setting"
android:drawableTint="?attr/workspaceTextColor"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
android:stateListAnimator="@animator/overview_button_anim"
android:text="@string/theme_button_text"
android:textAllCaps="true"
android:textColor="?attr/workspaceTextColor"
android:textSize="12sp" />
<!-- zrx add end -->
二:点击theme控件进入展示主题列表界面
layout布局
添加界面的布局theme_pick.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" >
<LinearLayout
android:id="@+id/theme_title_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/theme_title" />
</LinearLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/theme_title_layout"
android:background="@android:color/white" >
</android.support.v7.widget.RecyclerView>
<TextView
android:id="@+id/theme_apply"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:background="@android:color/white"
android:clickable="true"
android:gravity="center"
android:text="@string/theme_apply" />
</RelativeLayout>
添加子item的布局recycle_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="150dp"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="132dp" >
<ImageView
android:id="@+id/recycler_view_item_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:scaleType="fitXY" />
<CheckBox
android:id="@+id/theme_item_check"
android:layout_width="22dp"
android:layout_height="22dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="5dp"
android:background="@drawable/theme_selector"
android:button="@null"
android:visibility="gone" />
</RelativeLayout>
<TextView
android:id="@+id/recycler_view_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:textColor="@android:color/black" />
</LinearLayout>
三:对主题列表界面的处理
新建ThemePickActivity.java
package com.android.launcher3.theme;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.util.ArrayList;
import com.android.launcher3.R;
import android.content.Intent;
import com.android.launcher3.Launcher;
/**
*
* @author zrx add
*
*/
public class ThemePickActivity extends Activity {
private RecyclerView mRecyclerView;
private PackageManager pm;
private boolean checkState = false;
private boolean checkLongState = false;
private RecycleViewAdapter recycleViewAdapter;
private TextView mApplyTextView;
private String mCurrentThemeName;
private IStateClearCallback mStateClearCallback;
private static final String DEFAULT_THEME_PACHKAGE = "com.zrx.default";
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.theme_pick);
String currentThemeName = ThemeSharePerfence.getThemeShareprefValue(this);
if(currentThemeName != null){
checkState = true;
mCurrentThemeName = currentThemeName;
}else{
mCurrentThemeName = DEFAULT_THEME_PACHKAGE;
}
initViews();
pm = getPackageManager();
}
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
if(mStateClearCallback != null){
mStateClearCallback.doClearWithHearts();
mStateClearCallback = null;
return;
}
super.onBackPressed();
}
private void initViews(){
mRecyclerView = (RecyclerView)findViewById(R.id.recycler_view);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
int spanCount = 3;
int spacing = 20;
boolean includeEdge = true;
mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(spanCount, spacing, includeEdge));
// mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
recycleViewAdapter = new RecycleViewAdapter(this);
recycleViewAdapter.setThemes(ThemeContainer.getInstance().getAllThemeList(this));
mRecyclerView.setAdapter(recycleViewAdapter);
mApplyTextView = (TextView) findViewById(R.id.theme_apply);
mApplyTextView.setVisibility(View.GONE);
}
private void restartLauncherActivity() {
android.os.Process.killProcess(android.os.Process.myPid());
Intent intent = getBaseContext().getPackageManager()
.getLaunchIntentForPackage(getBaseContext().getPackageName());
if (intent == null) {
intent = new Intent(this, Launcher.class);
}
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
class RecycleViewAdapter extends RecyclerView.Adapter{
private ArrayList<Object> themes;
private ThemeInstaller mThemeInstaller;
private CheckBox mCurrentCheckBox;
private ArrayList<ThemeInfo> mDeleteThemeInfos;
private Context mContext;
public RecycleViewAdapter(Context context){
mContext = context;
}
public void setThemes(ArrayList<Object> themeList){
if(themeList == null){
return;
}
themes = new ArrayList<Object>();
ThemeInfo defaultTheme = new ThemeInfo(getString(R.string.default_theme), R.drawable.default_theme_wallpaper);
defaultTheme.setFlag(ThemeInfo.THEME_FLAG_DEFAULT);
defaultTheme.setPackageName(DEFAULT_THEME_PACHKAGE);
themes.add(defaultTheme);
for (int i = 0; i < themeList.size(); i++)
{
themes.add(themeList.get(i));
}
mThemeInstaller = new ThemeInstaller();
mDeleteThemeInfos = new ArrayList<ThemeInfo>();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
return new MyHodler(LayoutInflater.from(ThemePickActivity.this).inflate(R.layout.recycle_item, parent, false));
}
@Override
public int getItemCount()
{
return themes.size();
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder,
final int position) {
// Log.i("tyty","onBindViewHolder");
final Object data = themes.get(position);
if (data instanceof ThemeInfo) {
((MyHodler) holder).mTextView.setText(((ThemeInfo) data)
.getThemeName());
Drawable drawable = ((ThemeInfo) data)
.getThemeThumbnailsDrawable();
if (drawable != null) {
((MyHodler) holder).mImageView.setImageDrawable(drawable);
} else {
((MyHodler) holder).mImageView
.setImageResource(((ThemeInfo) data)
.getThemeThumbnails());
}
// check but
final CheckBox checkBox = ((MyHodler) holder).mCheckBox;
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton,
boolean b) {
if (b) {
((ThemeInfo) data)
.setFlag(ThemeInfo.THEME_FLAG_DELETE);
mDeleteThemeInfos.add(((ThemeInfo) data));
} else {
((ThemeInfo) data)
.setFlag(ThemeInfo.THEME_FLAG_NORMAL);
mDeleteThemeInfos.remove(data);
}
}
});
if (checkLongState
&& (((ThemeInfo) data).getFlag() != ThemeInfo.THEME_FLAG_DEFAULT)) {
checkBox.setVisibility(View.VISIBLE);
// 不是当前主题的checkbox,才能选择
if (mCurrentCheckBox != null
&& mCurrentCheckBox == checkBox) {
// don't doing anything
} else {
if (((ThemeInfo) data).getFlag() == ThemeInfo.THEME_FLAG_NORMAL) {
checkBox.setChecked(false);
} else if (((ThemeInfo) data).getFlag() == ThemeInfo.THEME_FLAG_DELETE) {
checkBox.setChecked(true);
}
}
} else {
checkBox.setChecked(false);
checkBox.setVisibility(View.GONE);
}
((MyHodler) holder).mImageView.setClickable(true);
((MyHodler) holder).mImageView
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (checkLongState) {
onBackPressed();
return;
}
String packageName = ((ThemeInfo) data)
.getPackageName();
// if
// (!mThemeInstaller.themeIsInstall(packageName,
// pm))
// {
// mThemeInstaller.installTheme(packageName,
// pm);
// } else
// {
// //check theme
// Toast.makeText(ThemePickActivity.this,
// "theme is " + packageName,
// Toast.LENGTH_SHORT).show();
// }
if (mCurrentCheckBox != null) {
mCurrentCheckBox.setEnabled(true);
mCurrentCheckBox.setVisibility(View.GONE);
}
// not check agait
// checkBox.setChecked(true);
{
checkState = true;
checkBox.setVisibility(View.VISIBLE);
checkBox.setEnabled(false);
mStateClearCallback = new IStateClearCallback() {
@Override
public void doClearWithHearts() {
exitCheckState();
mApplyTextView
.setVisibility(View.GONE);
checkBox.setEnabled(true);
checkState = false;
}
};
mApplyTextView.setText("Apply");
mApplyTextView.setVisibility(View.VISIBLE);
mApplyTextView
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String packageName = ((ThemeInfo) data)
.getPackageName();
// set current theme.
// theme is apk? or has
// install ?
// only has install
final ProgressDialog progressDialog = ProgressDialog
.show(mContext,
"主题设置",
"主题设置中i。。。",
false);
progressDialog.show();
new Thread(new Runnable() {
@Override
public void run() {
try {
mThemeInstaller
.setTheme(
(ThemeInfo) data,
mContext);
// Log.i("tyty","theme set finish");
progressDialog
.dismiss();
ThemeSharePerfence
.setThemeShareprefValue(
packageName,
mContext);
// android.os.Process.killProcess(android.os.Process.myPid());
restartLauncherActivity();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
// if
// restartLauncherActivity
// fast, the pref will no
// restore.
// ThemeSharePerfence.setThemeShareprefValue(packageName,
// mContext);
// return
onBackPressed();
}
});
}
mCurrentCheckBox = checkBox;
}
});
((MyHodler) holder).mImageView.setLongClickable(true);
((MyHodler) holder).mImageView
.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
String packageName = ((ThemeInfo) data)
.getPackageName();
//
// if
// (mThemeInstaller.themeIsInstall(packageName,
// pm))
// {
// mThemeInstaller.unInstallTheme(packageName,
// pm, mContext);
// } else
// {
// //remove file
// Toast.makeText(ThemePickActivity.this,
// "file remove " + packageName,
// Toast.LENGTH_SHORT).show();
// }
// current checkbox must enable
if (mCurrentCheckBox != null) {
mCurrentCheckBox.setEnabled(true);
}
// if long press ,clear check state and show
// text delete
checkLongState = true;
checkState = false;
mStateClearCallback = new IStateClearCallback() {
@Override
public void doClearWithHearts() {
// 去掉,checkbox的扎u那个状态
ArrayList<ThemeInfo> mlist = getDeleteThemeInfos();
for (int i = 0; i < mlist.size(); i++) {
mlist.get(i)
.setFlag(
ThemeInfo.THEME_FLAG_NORMAL);
}
// 隐藏操作view
mApplyTextView.setVisibility(View.GONE);
// 长按状态取消
checkLongState = false;
// 更新list。。。。recycle
notifyDataSetChanged();
}
};
mApplyTextView.setText("Delete");
mApplyTextView.setVisibility(View.VISIBLE);
mApplyTextView
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// set theme .and restart
// activity.
// delete theme .can get list
ArrayList<ThemeInfo> lists = recycleViewAdapter
.getDeleteThemeInfos();
ArrayList<Object> aalsit = ThemeContainer
.getInstance()
.getAllThemeList(
ThemePickActivity.this);
for (ThemeInfo ThemeInfo : lists) {
if (recycleViewAdapter
.getThemeInstaller()
.uninstallTheme(
ThemeInfo
.getPackageName(),
pm,
mContext)) {
recycleViewAdapter
.setThemes(aalsit);
recycleViewAdapter
.notifyDataSetChanged();
}
}
onBackPressed();
}
// back
});
RecycleViewAdapter.this.notifyDataSetChanged();
return true;
}
});
// data deal
if (mCurrentThemeName != null
&& ((ThemeInfo) data).getPackageName().equals(
mCurrentThemeName)) {
checkBox.setEnabled(false);
checkBox.setVisibility(View.VISIBLE);
mCurrentCheckBox = checkBox;
// imageview not working ?for click. < setClickable >
((MyHodler) holder).mImageView.setEnabled(false);
// ((MyHodler) holder).mImageView.setOnClickListener(null);
}
}
}
private void exitCheckState() {
if (mCurrentCheckBox != null) {
mCurrentCheckBox.setChecked(false);
mCurrentCheckBox.setVisibility(View.GONE);
}
}
private ArrayList<ThemeInfo> getDeleteThemeInfos() {
return mDeleteThemeInfos;
}
private ThemeInstaller getThemeInstaller() {
return mThemeInstaller;
}
}
class MyHodler extends RecyclerView.ViewHolder
{
public ImageView mImageView;
public TextView mTextView;
public CheckBox mCheckBox;
public MyHodler(View itemView)
{
super(itemView);
mImageView = (ImageView) itemView.findViewById(R.id.recycler_view_item_icon);
mTextView = (TextView) itemView.findViewById(R.id.recycler_view_item_name);
mCheckBox = (CheckBox) itemView.findViewById(R.id.theme_item_check);
}
}
interface IStateClearCallback
{
public void doClearWithHearts();
}
}
其他逻辑类
添加ThemeSharePerfence.java
package com.android.launcher3.theme;
import android.content.Context;
import android.content.SharedPreferences;
/**
* SharedPreferences封装类
* @author zrx
*
*/
public class ThemeSharePerfence {
public static final String THEME_SHAREPREF_NAME = "theme";
public static final String THEME_SHAREPREF_THEME_CURRENT_NAME = "theme_current";
public static SharedPreferences getThemePreferences(Context context){
return context.getSharedPreferences(THEME_SHAREPREF_NAME, Context.MODE_PRIVATE);
}
public static String getThemeShareprefValue(Context context){
return getThemePreferences(context).getString(THEME_SHAREPREF_THEME_CURRENT_NAME, null);
}
public static boolean setThemeShareprefValue(String value, Context context){
SharedPreferences.Editor editor = getThemePreferences(context).edit();
editor.putString(THEME_SHAREPREF_THEME_CURRENT_NAME, value);
return editor.commit();
}
}
添加ThemeInfo.java,主题详细信息
package com.android.launcher3.theme;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import java.util.HashMap;
/**
* theme info
* @author zrx
*
*/
public class ThemeInfo extends Object {
public static final int BROWSER = 0;
public static final int CALCULATOR = 1;
public static final int CAMERA = 2;
public static final int CONTACTS = 3;
public static final int DESKCLOCK = 4;
public static final int DOWNLOADS = 5;
public static final int EMAIL = 6;
public static final int FILEMANAGER = 7;
public static final int FM = 8;
public static final int GALLERY = 9;
public static final int GOOGLE_CHROME = 10;
public static final int GOOGLE_DOCS = 11;
public static final int GOOGLE_GM = 12;
public static final int GOOGLE_MAP = 13;
public static final int GOOGLE_MUSIC = 14;
public static final int GOOGLE_PHOTOS = 15;
public static final int GOOGLEQUICKSEARCHBOX = 16;
public static final int GOOGLE_TACHYON = 17;
public static final int GOOGLE_VIDEOS = 18;
public static final int MSG = 19;
public static final int MUSIC = 20;
public static final int PHONE = 21;
public static final int RADIO = 22;
public static final int SAMWEATHERCLOCK = 23;
public static final int SETTING = 24;
public static final int SIM_CARD = 25;
public static final int SOUNDRECORDER = 26;
public static final int YOUTUBE = 27;
public static final int CALENDAR = 28;
public static final int GOOGLEPLAY = 29;
public static final String[] COMSTOM_ICONS = new String[] {
// .browser - > browser
"browser",
// .calculator - > calculator
"calculator",
// .camera - > camera
"camera",
// .contacts - > contacts
"contacts",
// .deskclock - > deskclock
"deskclock",
// .downloads - > downloads
"downloads",
// .email - > email
"email",
// .filemanager - > filemanager
"filemanager",
// .fm - > fm
"fm",
// .gallery - > gallery
"gallery",
// .google_chrome - > google_chrome
"google_chrome",
// .google_docs - > google_docs
"google_docs",
// .google_gm - > google_gm
"google_gm",
// .google_map - > google_map
"google_map",
// .google_music - > google_music
"google_music",
// .google_photos - > google_photos
"google_photos",
// .googlequicksearchbox - > googlequicksearchbox
"googlequicksearchbox",
// .google_tachyon - > google_tachyon
"google_tachyon",
// .google_videos - > google_videos
"google_videos",
// .msg - > msg
"msg",
// .music - > music
"music",
// .phone - > phone
"phone",
// .radio - > radio
"radio",
// .samweatherclock - > samweatherclock
"samweatherclock",
// .setting - > setting
"setting",
// .sim_card - > sim_card
"sim_card",
// .soundrecorder - > soundrecorder
"soundrecorder",
// .youtube - > youtube
"youtube",
// .calendar - > calendar
"calendar",
// .googleplay - > google_play
"google_play" };
public static HashMap<String, String> THEME_ICON_CONSTANT = new HashMap<String, String>();
static{//静态块
THEME_ICON_CONSTANT.put("com.android.browser", COMSTOM_ICONS[BROWSER]);
THEME_ICON_CONSTANT.put("com.android.calculator2", COMSTOM_ICONS[CALCULATOR]);
THEME_ICON_CONSTANT.put("com.mediatek.camera", COMSTOM_ICONS[CAMERA]);
THEME_ICON_CONSTANT.put("com.android.contacts", COMSTOM_ICONS[CONTACTS]);
THEME_ICON_CONSTANT.put("com.android.deskclock", COMSTOM_ICONS[DESKCLOCK]);
THEME_ICON_CONSTANT.put("com.android.documentsui", COMSTOM_ICONS[DOWNLOADS]);
THEME_ICON_CONSTANT.put("com.android.email", COMSTOM_ICONS[EMAIL]);
THEME_ICON_CONSTANT.put("com.mediatek.filemanager", COMSTOM_ICONS[FILEMANAGER]);
THEME_ICON_CONSTANT.put("com.android.fmradio", COMSTOM_ICONS[FM]);
THEME_ICON_CONSTANT.put("com.android.gallery3d", COMSTOM_ICONS[GALLERY]);
THEME_ICON_CONSTANT.put("com.android.chrome", COMSTOM_ICONS[GOOGLE_CHROME]);
THEME_ICON_CONSTANT.put("com.google.android.apps.docs", COMSTOM_ICONS[GOOGLE_DOCS]);
THEME_ICON_CONSTANT.put("com.google.android.gm", COMSTOM_ICONS[GOOGLE_GM]);
THEME_ICON_CONSTANT.put("com.google.android.apps.maps", COMSTOM_ICONS[GOOGLE_MAP]);
THEME_ICON_CONSTANT.put("com.google.android.music", COMSTOM_ICONS[GOOGLE_MUSIC]);
THEME_ICON_CONSTANT.put("com.google.android.apps.photos", COMSTOM_ICONS[GOOGLE_PHOTOS]);
THEME_ICON_CONSTANT.put("com.google.android.googlequicksearchbox", COMSTOM_ICONS[GOOGLEQUICKSEARCHBOX]);
THEME_ICON_CONSTANT.put("com.google.android.apps.tachyon", COMSTOM_ICONS[GOOGLE_TACHYON]);
THEME_ICON_CONSTANT.put("com.google.android.videos", COMSTOM_ICONS[GOOGLE_VIDEOS]);
THEME_ICON_CONSTANT.put("com.android.mms", COMSTOM_ICONS[MSG]);
THEME_ICON_CONSTANT.put("com.android.music", COMSTOM_ICONS[MUSIC]);
THEME_ICON_CONSTANT.put("com.android.dialer", COMSTOM_ICONS[PHONE]);
THEME_ICON_CONSTANT.put("com.android.soundrecorder", COMSTOM_ICONS[SAMWEATHERCLOCK]);
THEME_ICON_CONSTANT.put("com.android.settings", COMSTOM_ICONS[SETTING]);
THEME_ICON_CONSTANT.put("com.android.stk", COMSTOM_ICONS[SIM_CARD]);
THEME_ICON_CONSTANT.put("com.android.soundrecorder", COMSTOM_ICONS[SOUNDRECORDER]);
THEME_ICON_CONSTANT.put("com.google.android.youtube", COMSTOM_ICONS[YOUTUBE]);
THEME_ICON_CONSTANT.put("com.android.calendar", COMSTOM_ICONS[CALENDAR]);
THEME_ICON_CONSTANT.put("com.android.vending", COMSTOM_ICONS[GOOGLEPLAY]);
}
public static final boolean HAS_CUSTOM_SHORTICON = false;
private HashMap<String, Bitmap> mCustomShorticon = new HashMap<String, Bitmap>();
private static final String DRAWABLE = "drawable";
private static final String STRING = "string";
private static final String COLOR = "color";
private static final String DIMEN = "dimen";
private static final String THEME_NAME = "theme_name";
private static final String THEME_THUMB = "theme_thumb";
private static final String THEME_ROUND = "theme_round";
private static final String THEME_COLOR = "theme_shortcut_backgrand_color";
private static final String THEME_WALLPAPER = "theme_wallpaper";
// 添加两个状态,normal,delete,default-cant delete
public static final int THEME_FLAG_DEFAULT = 0;
public static final int THEME_FLAG_NORMAL = 1;
public static final int THEME_FLAG_DELETE = 2;
//name
private String mThemeName;
//and else .
//package name
private String mPackageName;
//theme resources
private Resources mResources;
//thumbnail
private int mThemeThumbnails;
//thumbnail
private Drawable mThemeThumbnailsDrawable;
//launcher true resouce. wallpaper
private int mThemeWallPaper;
//parameter in FastBitmapDrawable
//FastBitmapDrawable backgrand
private Drawable mThemeShortcutBackgrand;
//FastBitmapDrawable backgrand
private int mThemeShortcutBackgrandColor;
//Round angle
private float mThemeRound;
private int flag;
public ThemeInfo(String themeName, int themeThumbnails)
{
mThemeName = themeName;
mThemeThumbnails = themeThumbnails;
flag = THEME_FLAG_NORMAL;
}
public ThemeInfo(Drawable themeThumbnailsDrawable, String themeName)
{
mThemeThumbnailsDrawable = themeThumbnailsDrawable;
mThemeName = themeName;
flag = THEME_FLAG_NORMAL;
}
public ThemeInfo(String packageName, Resources resources)
{
mPackageName = packageName;
mResources = resources;
flag = THEME_FLAG_NORMAL;
//if get a resources,we need read again
readTheResources();
}
public String getThemeName()
{
return mThemeName;
}
public void setThemeName(String themeName)
{
mThemeName = themeName;
}
public int getThemeThumbnails()
{
return mThemeThumbnails;
}
public void setThemeThumbnails(int themeThumbnails)
{
mThemeThumbnails = themeThumbnails;
}
public String getPackageName()
{
return mPackageName;
}
public void setPackageName(String packageName)
{
mPackageName = packageName;
}
public Drawable getThemeThumbnailsDrawable()
{
return mThemeThumbnailsDrawable;
}
public void setThemeThumbnailsDrawable(Drawable themeThumbnailsDrawable)
{
mThemeThumbnailsDrawable = themeThumbnailsDrawable;
}
public Bitmap getThemeWallPaper() {
if (mResources == null) {
return null;
}
return ((BitmapDrawable) mResources.getDrawable(mResources
.getIdentifier(THEME_WALLPAPER, DRAWABLE, mPackageName)))
.getBitmap();
}
public Drawable getThemeShortcutBackgrand()
{
return mThemeShortcutBackgrand;
}
public void setThemeShortcutBackgrand(Drawable themeShortcutBackgrand)
{
mThemeShortcutBackgrand = themeShortcutBackgrand;
}
public int getThemeShortcutBackgrandColor()
{
return mThemeShortcutBackgrandColor;
}
public void setThemeShortcutBackgrandColor(int themeShortcutBackgrandColor)
{
mThemeShortcutBackgrandColor = themeShortcutBackgrandColor;
}
public float getThemeRound()
{
return mThemeRound;
}
public void setThemeRound(float themeRound)
{
mThemeRound = themeRound;
}
public void setCurrentTheme(Context context)
{
// set wallpaper
//set launcher Icon
//set shareperfence
}
public HashMap<String, Bitmap> getCustomShorticon()
{
return mCustomShorticon;
}
public int getFlag()
{
return flag;
}
public void setFlag(int flag)
{
this.flag = flag;
}
private void readTheResources(){
if (mResources != null){
mThemeRound = mResources.getDimension(mResources.getIdentifier(THEME_ROUND, DIMEN, mPackageName));
mThemeShortcutBackgrandColor = mResources.getColor(mResources.getIdentifier(THEME_COLOR, COLOR, mPackageName));
mThemeThumbnailsDrawable = mResources.getDrawable(mResources.getIdentifier(THEME_THUMB, DRAWABLE, mPackageName));
mThemeName = mResources.getString(mResources.getIdentifier(THEME_NAME, STRING, mPackageName));
mThemeWallPaper = mResources.getIdentifier(THEME_WALLPAPER, DRAWABLE, mPackageName);
if(HAS_CUSTOM_SHORTICON){
for (String comstomIcon : COMSTOM_ICONS)
{
Bitmap bitmap = ((BitmapDrawable)mResources.getDrawable(mResources.getIdentifier(comstomIcon, DRAWABLE, mPackageName))).getBitmap();
mCustomShorticon.put(comstomIcon,bitmap);
}
}
}
}
@Override
public String toString()
{
return "mThemeName = " + mThemeName + " - mPackageName =" + mPackageName;
}
}
添加GridSpacingItemDecoration.java,RecyclerView属性的封装类
package com.android.launcher3.theme;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration
{
private int spanCount;//列数
private int spacing;//间隔
private boolean includeEdge;//是否包含边缘
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge)
{
this.spanCount = spanCount;
this.spacing = spacing;
this.includeEdge = includeEdge;
}
/**
* getItemOffsets 中为 outRect 设置的4个方向的值,将被计算进所有 decoration 的尺寸中,而这个尺寸,被计入了 RecyclerView 每个 item view 的 padding 中
*
* @param outRect
* @param view
* @param parent
* @param state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
{
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
//Log.i("tyty", "l = " + outRect.left + " r = " + outRect.right + " t = " + outRect.top + " b = " + outRect.bottom + " position = " + position);
//此处 巧妙的 通过 计算,。。。,然 相邻的两个view之间 左右 之和 = 50 ,然后 保证 最右边等于五十,最左边也等于五十。
//特么的 其实 就让最边上的 固定,然后中间的均分 就好了! 不过。 这样会有档次些嘛 -0-!
if (includeEdge)
{
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount)
{ // top edge
outRect.top = spacing;
}
outRect.bottom = spacing; // item bottom
} else
{
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount)
{
outRect.top = spacing; // item top
}
}
// //clear rect
// outRect.left = 0;
// outRect.right = 0;
// outRect.top = 0;
// outRect.bottom = 0;
// // reset rect
// outRect.left = 0;
// Log.i("tyty", "new one " + "l = " + outRect.left + " r = " + outRect.right + " t = " + outRect.top + " b = " + outRect.bottom);
}
}
添加ThemeContainer.java,获取主题列表信息
package com.android.launcher3.theme;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import com.android.launcher3.R;
/**
* theme list
* @author zrx
*
*/
public class ThemeContainer {
private static final String THEME_INTENT_ACTION = "com.zrx.theme";
private ArrayList<Object> datas;
private static ThemeContainer mContainer;
private static final String TAG = "ThemeContainer";
public static ThemeContainer getInstance(){
if(mContainer == null){
mContainer = new ThemeContainer();
}
return mContainer;
}
public ArrayList<Object> getAllThemeList(Context context){
datas = queryTheme(context);
if(datas == null){
return createFakeData();
}
return datas;
}
private ArrayList<Object> createFakeData()
{
ArrayList<Object> temp = new ArrayList<Object>();
for (int i = 0; i < 20; i++)
{
ThemeInfo themeZhaH = new ThemeInfo("item" + (i + 1), R.drawable.music);
themeZhaH.setPackageName("packageName" + i);
temp.add(themeZhaH);
}
return temp;
}
private ArrayList<Object> queryTheme(Context context){
ArrayList<Object> items = new ArrayList<Object>();
PackageManager pm = context.getPackageManager();
Intent intent = new Intent();
intent.setAction(THEME_INTENT_ACTION);
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
for(ResolveInfo reInfo:resolveInfos){
ActivityInfo activityInfo = reInfo.activityInfo;
/*Bundle metaData = activityInfo.metaData;
String name = activityInfo.name;*/
try {
Context remoteContext = context.createPackageContext(activityInfo.packageName, Context.CONTEXT_IGNORE_SECURITY);//获得package的context
Resources resources = remoteContext.getResources();
ThemeInfo themeInfo = new ThemeInfo(activityInfo.packageName, resources);//获取第三方主题app的资源信息
items.add(themeInfo);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
return items;
}
}
添加ThemeInstaller.java,设置主题后壁纸处理类
package com.android.launcher3.theme;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Environment;
import com.android.launcher3.R;
/**
*
* @author zrx
*
*/
public class ThemeInstaller implements IThemeInstallImp{
@Override
public void installTheme(String packageName, PackageManager pm) {
// TODO Auto-generated method stub
//install theme
Uri mPackageURI = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + packageName));
int installFlags = 0;
/*
try
{
PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
if (pi != null)
{
installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;
}
} catch (PackageManager.NameNotFoundException e)
{
}
PackageInstallObserver observer = new PackageInstallObserver();
pm.installPackage(mPackageURI, observer, installFlags);
*/
//delete apk file ,if install.
}
@Override
public boolean uninstallTheme(String packageName, PackageManager pm,
Context context) {
// TODO Auto-generated method stub
//need system id
//pm.deletePackage(packageName, null, 0);
/*
// uninstall
String appPackage = packageName;
Intent intent = new Intent(mContext, mContext.getClass());
PendingIntent sender = PendingIntent.getActivity(mContext, 0, intent, 0);
PackageInstaller mPackageInstaller = mContext.getPackageManager().getPackageInstaller();
mPackageInstaller.uninstall(appPackage, sender.getIntentSender());
*/
//PackageInstaller packageInstaller = pm.getPackageInstaller();
/* reflection need permission.
Class<?> activityTherad;
Method method;
Class pmService = null;
try
{
activityTherad = Class.forName("android.app.ActivityThread");
Class<?> paramTypes[] = getParamTypes(activityTherad, "getPackageManager");
method = activityTherad.getMethod("getPackageManager", paramTypes);
Object PackageManagerService = null;
PackageManagerService = method.invoke(activityTherad);
pmService = PackageManagerService.getClass();
Class<?> paramTypes1[] = getParamTypes(pmService, "deletePackage");
method = pmService.getMethod("deletePackage", paramTypes1);
method.invoke(PackageManagerService, packageName, null,PackageManager.DELETE_ALL_USERS, 0);
} catch (ClassNotFoundException e)
{
e.printStackTrace();
} catch (NoSuchMethodException e)
{
e.printStackTrace();
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (InvocationTargetException e)
{
e.printStackTrace();
}
*/
Intent intent = new Intent(
Intent.ACTION_DELETE, Uri.fromParts("package", packageName, "MainActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(intent);
//test true
return true;
}
@Override
public boolean themeIsInstall(String packageName, PackageManager pm) {
// TODO Auto-generated method stub
boolean installed = false;
try
{
pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
installed = true;
} catch (PackageManager.NameNotFoundException e)
{
installed = false;
}
return installed;
}
@Override
public void setTheme(ThemeInfo themeZhaH, Context context)
throws IOException {
// TODO Auto-generated method stub
//wallpaper
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Bitmap bitmap = null;
bitmap = themeZhaH.getThemeWallPaper();
if (bitmap == null) {
bitmap = ((BitmapDrawable) context.getResources().getDrawable(
R.drawable.default_wallpaper)).getBitmap();
}
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(
byteArrayOutputStream.toByteArray());
wallpaperManager.setStream(byteArrayInputStream, null, true,
WallpaperManager.FLAG_SYSTEM);
byteArrayInputStream.close();
byteArrayOutputStream.close();
//theme_shortcut_backgrand
//icon?
}
/***
* 获取参数类型,返回值保存在Class[]中
*/
private Class<?>[] getParamTypes(Class<?> cls, String mName)
{
Class<?>[] clazz = null;
/**Note: 由于我们一般通过反射机制调用的方法,是非public方法
* 所以在此处使用了getDeclaredMethods()方法*/
Method[] mtd = cls.getDeclaredMethods();
for (int i = 0; i < mtd.length; i++)
{
if (!mtd[i].getName().equals(mName))
{
// 不是我们需要的方法,则进入下一次循环
continue;
}
clazz = mtd[i].getParameterTypes();
}
return clazz;
}
}
interface IThemeInstallImp
{
public void installTheme(String packageName, PackageManager pm);
public boolean uninstallTheme(String packageName, PackageManager pm,Context context);
public boolean themeIsInstall(String packageName, PackageManager pm);
public void setTheme(ThemeInfo themeZhaH, Context context) throws IOException;
}
添加ThemeBitmap.java,修改主题后获取app的icon
package com.android.launcher3.theme;
import java.util.HashMap;
import com.android.launcher3.LauncherAppState;
import android.R.drawable;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.LauncherActivityInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
public class ThemeBitmap {
private Context mContext;
private PackageManager pm;
private LauncherActivityInfo mLauncherActivityInfo;
public ThemeBitmap(LauncherActivityInfo launcherActivityInfo, Context context){
mLauncherActivityInfo = launcherActivityInfo;
mContext = context;
pm = mContext.getPackageManager();
}
private String getCurrenThemePKGName(Context context)
{
return ThemeSharePerfence.getThemeShareprefValue(context);
}
private void clearCurrentThemeData(Context context)
{
ThemeSharePerfence.setThemeShareprefValue(null, context);
}
public Bitmap getBitmap(){
Bitmap bitmap = null;
Resources resources = null;
String pacakgeName = getCurrenThemePKGName(mContext);
if(pacakgeName != null){
try {
Context remoteContext = mContext.createPackageContext(pacakgeName, Context.CONTEXT_IGNORE_SECURITY);
resources = remoteContext.getResources();
} catch (PackageManager.NameNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
clearCurrentThemeData(mContext);
return null;
}
}
HashMap<String, String> appNames = ThemeInfo.THEME_ICON_CONSTANT;
if(appNames.containsKey(mLauncherActivityInfo.getComponentName().getPackageName())){
Drawable drawable = null;
if(resources != null){
drawable = resources.getDrawable(resources.getIdentifier(ThemeInfo.THEME_ICON_CONSTANT.get(mLauncherActivityInfo.getComponentName().getPackageName()), "drawable", pacakgeName), null);
}
if(drawable != null){
return makeDefaultIcon(drawable, -1, 1.0f);
}
return null;
}
if(resources != null){
int color = resources.getColor(resources.getIdentifier("theme_shortcut_backgrand_color", "color", pacakgeName));
float round = resources.getDimension(resources.getIdentifier("theme_round", "dimen", pacakgeName))/(100*2);
return makeDefaultIcon(mLauncherActivityInfo.getApplicationInfo().loadIcon(pm), color, round);
}
return null;
}
private Bitmap makeDefaultIcon(Drawable d, int color, float scalf)
{
Bitmap bitmap = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1), Math.max(d.getIntrinsicHeight(), 1), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = 24;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.save();
Matrix matrix = new Matrix();
matrix.setScale(scalf, scalf);
float margin = (1.0f - scalf) / 2;
//Log.i("tyty","margin = "+margin);
matrix.preTranslate(bitmap.getWidth() * margin, bitmap.getHeight() * margin);
canvas.setMatrix(matrix);
paint.setColor(color);
if (color != -1)
{
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
}
canvas.restore();
d.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
d.draw(canvas);
canvas.setBitmap(null);
return bitmap;
}
}
laucher图标处理,修改Launcher3/src/com/android/launcher3/IconCache.java
import com.android.launcher3.theme.ThemeBitmap;//zrx add
private synchronized void getTitleAndIcon(
@NonNull ItemInfoWithIcon infoInOut,
@NonNull Provider<LauncherActivityInfo> activityInfoProvider,
boolean usePkgIcon, boolean useLowResIcon) {
CacheEntry entry = cacheLocked(infoInOut.getTargetComponent(), activityInfoProvider,
infoInOut.user, usePkgIcon, useLowResIcon);
//zrx add start
ThemeBitmap themeBitmap = new ThemeBitmap(activityInfoProvider.get(), mContext);
Bitmap bitmap = themeBitmap.getBitmap();
if(bitmap != null){
entry.icon = bitmap;
}
//zrx add end
applyCacheEntry(entry, infoInOut);
}
三:theme控件点击事件
修改Launcher.java
import com.android.launcher3.theme.ThemePickActivity;
private void startThemePickActivity()
{
Intent intent = new Intent(this,ThemePickActivity.class);
startActivity(intent);
}
private void setupOverviewPanel() {
mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
//zrx add start
View themeButton = findViewById(R.id.theme_button);
new OverviewButtonClickListener(ControlType.THEME_BUTTON) {
@Override
public void handleViewClick(View view) {
startThemePickActivity();
}
}.attachTo(themeButton);
//zrx add end
// Bind wallpaper button actions
View wallpaperButton = findViewById(R.id.wallpaper_button);
new OverviewButtonClickListener(ControlType.WALLPAPER_BUTTON) {
@Override
public void handleViewClick(View view) {
onClickWallpaperPicker(view);
}
}.attachTo(wallpaperButton);
// Bind widget button actions
mWidgetsButton = findViewById(R.id.widget_button);
new OverviewButtonClickListener(ControlType.WIDGETS_BUTTON) {
@Override
public void handleViewClick(View view) {
onClickAddWidgetButton(view);
}
}.attachTo(mWidgetsButton);
// Bind settings actions
View settingsButton = findViewById(R.id.settings_button);
boolean hasSettings = hasSettings();
if (hasSettings) {
new OverviewButtonClickListener(ControlType.SETTINGS_BUTTON) {
@Override
public void handleViewClick(View view) {
onClickSettingsButton(view);
}
}.attachTo(settingsButton);
} else {
settingsButton.setVisibility(View.GONE);
}
mOverviewPanel.setAlpha(0f);
}
修改Launcher3/protos/launcher_log.proto
enum ControlType {
DEFAULT_CONTROLTYPE = 0;
ALL_APPS_BUTTON = 1;
WIDGETS_BUTTON = 2;
WALLPAPER_BUTTON = 3;
SETTINGS_BUTTON = 4;
REMOVE_TARGET = 5;
UNINSTALL_TARGET = 6;
APPINFO_TARGET = 7;
RESIZE_HANDLE = 8;
VERTICAL_SCROLL = 9;
HOME_INTENT = 10; // Deprecated, use enum Command instead
BACK_BUTTON = 11; // Deprecated, use enum Command instead
// GO_TO_PLAYSTORE
THEME_BUTTON = 12; //zrx add
}
四:AndroidManifest.xml修改,声明ThemePickActivity和权限添加
<!--add by zrx theme-->
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- add end-->
<activity
android:label="@string/theme_title"
android:name="com.android.launcher3.theme.ThemePickActivity"
android:screenOrientation="portrait"
>
</activity>
五:主题apk创建,在AndroidManifest.xml的Activity元素下添加
<intent-filter>
<action android:name="com.zrx.theme"/>
</intent-filter>
把资源文件放到res目录下,资源命令规则查看ThemeInfo.java
五:运行效果