앱 테마 만들기 마지막 시간입니다. 테마를 정의하고 설치, 업데이트하는 코드를 정리하겠습니다.
ThemeKey 현재는 ASSETS에서 zip 파일 형태로 내장되어 있는 경우만 구현했습니다.
public class ThemeKey {
public enum Type {
RESOURCE,
ASSETS, //extract zip file from asset
URL // download from url and extract zip file
}
private final int themeId;
private final int version;
private final String name;
private final Type type;
private final String uri; //asset zip filename or url
@StringRes
private int nameResourceId;
public ThemeKey(int themeId, String name, int version) {
this(themeId, name, version, Type.RESOURCE, null);
}
public ThemeKey(int themeId, String name, int version, Type type, String uri) {
this.themeId = themeId;
this.version = version;
this.name = name;
this.type = type;
this.uri = uri;
}
public boolean hasFiles() {
return type != Type.RESOURCE;
}
public int getThemeId() {
return themeId;
}
public String getName() {
if (nameResourceId != 0) {
return ApplicationKeeper.get().getString(nameResourceId);
}
return name;
}
public int getVersion() {
return version;
}
public Type getType() {
return type;
}
public String getUri() {
return uri;
}
public ThemeKey nameResourceId(@StringRes int nameResourceId) {
this.nameResourceId = nameResourceId;
return this;
}
}
Theme 정의하기 계산기 앱에서 실제로 정의된 테마입니다. 마지막 TEST 테마는 Debug 모드에서만 사용가능합니다.
public class Theme {
public static final ThemeKey DEFAULT = new ThemeKey(0, "Default theme", 1).nameResourceId(R.string.default_theme);
public static final ThemeKey BROWN = new ThemeKey(1, "Brown theme", 2, ThemeKey.Type.ASSETS, "brown_theme.zip").nameResourceId(R.string.brown_theme);
public static final ThemeKey GREY = new ThemeKey(2, "Grey theme", 2, ThemeKey.Type.ASSETS, "grey_theme.zip").nameResourceId(R.string.grey_theme);
public static final ThemeKey TEST = new ThemeKey(21, "Test theme", 6, ThemeKey.Type.ASSETS, "test.zip");
public static final ThemeKey[] THEME_KEYS = {
DEFAULT, BROWN, GREY, TEST
};
public static ThemeKey valueOf(int id) {
for (ThemeKey themeKey : THEME_KEYS) {
if (themeKey.getThemeId() == id) {
return themeKey;
}
}
return DEFAULT;
}
테마 설치, 업데이트하기 에셋에서 zip 파일을 풀어서 테마를 설치하고, 테마가 업데이트될 경우를 체크해서 다시 설치하는 코드입니다. 테마 업데이트 체크는 스플래시 Activity나 Main activity에서 하면 됩니다.
public class ThemeUtils {
public static final SettingInt currentThemeId = new SettingInt("currentThemeId", 0);
private static final SettingInt InstallFailedLastThemeId = new SettingInt("InstallFailedLastThemeId", -1);
public static void installThemeAndRestart(final Activity activity, final ThemeKey themeKey) {
if (themeKey.hasFiles()) {
ExtractThemeFilesTask task = new ExtractThemeFilesTask(activity, themeKey);
task.showProgressDialog(activity, 30);
task.execute();
} else {
changeThemeAndRestart(activity, themeKey);
}
}
static private class ExtractThemeFilesTask extends AsyncTaskEx.ResultTask<Boolean> {
Activity activity;
ThemeKey themeKey;
ExtractThemeFilesTask(Activity activity, ThemeKey themeKey) {
this.activity = activity;
this.themeKey = themeKey;
}
@Override
protected Boolean doInBackground() {
return ThemeUtils.extractThemeFiles(activity, themeKey);
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
changeThemeAndRestart(activity, themeKey);
InstallFailedLastThemeId.setValue(-1);
} else {
InstallFailedLastThemeId.setValue(themeKey.getThemeId());
ToastUtils.show(R.string.error_unknown);
DebugUtils.notReached("ExtractThemeFilesTask failed");
}
}
}
private static boolean extractThemeFiles(Context context, ThemeKey themeKey) {
if (themeKey.getType() != ThemeKey.Type.ASSETS) {
return true;
}
if (Decompress.unzipFromAssets(context, themeKey.getUri(), getThemeDirectoryPath(context, themeKey.getThemeId()))) {
return writeVersionJsonFile(context, themeKey);
}
return false;
}
public static boolean extractThemeFiles(Context context, File source, ThemeKey themeKey) {
if (Decompress.unzip(source.getAbsolutePath(), getThemeDirectoryPath(context, themeKey.getThemeId()))) {
return writeVersionJsonFile(context, themeKey);
}
return false;
}
public static void changeThemeAndRestart(Context context, ThemeKey themeKey) {
ThemeManager.getInstance().changeTheme(themeKey, true);
Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
}
public static void maybeChangeTheme(Activity activity, final ThemeKey themeKey) {
if (themeKey.getThemeId() != ThemeManager.getInstance().getThemeId()) {
if (ThemeUtils.isThemeFilesUpdated(activity, themeKey)) {
ThemeUtils.installThemeAndRestart(activity, themeKey);
} else {
ThemeUtils.changeThemeAndRestart(activity, themeKey);
}
}
}
public static boolean checkThemeFileUpdated(final Activity activity, final ThemeKey currentThemeKey) {
// avoid infinite alert
if (InstallFailedLastThemeId.getValue() == currentThemeKey.getThemeId()) {
return false;
}
boolean fileUpdated = ThemeUtils.isThemeFilesUpdated(activity, currentThemeKey);
if (fileUpdated) {
DialogHelper.showConfirm(activity, activity.getString(R.string.current_theme_updated, currentThemeKey.getName()), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ThemeUtils.installThemeAndRestart(activity, currentThemeKey);
}
}).setCancelable(false);
}
return fileUpdated;
}
public static boolean isThemeFilesUpdated(Context context, ThemeKey themeKey) {
if (themeKey.hasFiles()) {
if (themeKey.getVersion() != getVersion(context, themeKey.getThemeId())) {
return true;
}
}
return false;
}
// error -1
public static int getVersion(Context context, int themeId) {
String content = FileUtils.readFileContent(getVersionJsonPath(context, themeId));
if (TextUtils.isEmpty(content)) {
return -1;
}
try {
JSONObject jsonObject = new JSONObject(content);
int version = jsonObject.getInt("version");
return version;
} catch (Exception ex) {
DebugUtils.notReached(ex);
return -2;
}
}
private static boolean writeVersionJsonFile(Context context, ThemeKey themeKey) {
String path = getVersionJsonPath(context, themeKey.getThemeId());
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", themeKey.getName());
jsonObject.put("version", themeKey.getVersion());
return FileUtils.writeFileContent(path, jsonObject.toString());
} catch (Exception ex) {
DebugUtils.notReached(ex);
return false;
}
}
public static String getThemeDirectoryPath(Context context, int themeId) {
String destination = context.getFilesDir().getAbsolutePath();
destination += "/themes/" + themeId + "/";
return destination;
}
public static String getVersionJsonPath(Context context, int themeId) {
return getThemeDirectoryPath(context, themeId) + "version.json";
}
public static String getThemeJsonPath(Context context, int themeId) {
return getThemeDirectoryPath(context, themeId) + "theme.json";
}