aboutsummaryrefslogtreecommitdiffstats
path: root/sdk/src/java/cyanogenmod/themes
diff options
context:
space:
mode:
Diffstat (limited to 'sdk/src/java/cyanogenmod/themes')
-rw-r--r--sdk/src/java/cyanogenmod/themes/IThemeChangeListener.aidl23
-rw-r--r--sdk/src/java/cyanogenmod/themes/IThemeProcessingListener.aidl22
-rw-r--r--sdk/src/java/cyanogenmod/themes/IThemeService.aidl44
-rw-r--r--sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.aidl19
-rw-r--r--sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.java322
-rw-r--r--sdk/src/java/cyanogenmod/themes/ThemeManager.java391
6 files changed, 821 insertions, 0 deletions
diff --git a/sdk/src/java/cyanogenmod/themes/IThemeChangeListener.aidl b/sdk/src/java/cyanogenmod/themes/IThemeChangeListener.aidl
new file mode 100644
index 0000000..0700eb6
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/themes/IThemeChangeListener.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014-2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.themes;
+
+/** {@hide} */
+oneway interface IThemeChangeListener {
+ void onProgress(int progress);
+ void onFinish(boolean isSuccess);
+}
diff --git a/sdk/src/java/cyanogenmod/themes/IThemeProcessingListener.aidl b/sdk/src/java/cyanogenmod/themes/IThemeProcessingListener.aidl
new file mode 100644
index 0000000..648e1a9
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/themes/IThemeProcessingListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014-2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.themes;
+
+/** {@hide} */
+oneway interface IThemeProcessingListener {
+ void onFinishedProcessing(String pkgName);
+}
diff --git a/sdk/src/java/cyanogenmod/themes/IThemeService.aidl b/sdk/src/java/cyanogenmod/themes/IThemeService.aidl
new file mode 100644
index 0000000..fa186e9
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/themes/IThemeService.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014-2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.themes;
+
+import cyanogenmod.themes.IThemeChangeListener;
+import cyanogenmod.themes.IThemeProcessingListener;
+import cyanogenmod.themes.ThemeChangeRequest;
+
+import java.util.Map;
+
+/** {@hide} */
+interface IThemeService {
+ oneway void requestThemeChangeUpdates(in IThemeChangeListener listener);
+ oneway void removeUpdates(in IThemeChangeListener listener);
+
+ oneway void requestThemeChange(in ThemeChangeRequest request, boolean removePerAppThemes);
+ oneway void applyDefaultTheme();
+ boolean isThemeApplying();
+ int getProgress();
+
+ boolean processThemeResources(String themePkgName);
+ boolean isThemeBeingProcessed(String themePkgName);
+ oneway void registerThemeProcessingListener(in IThemeProcessingListener listener);
+ oneway void unregisterThemeProcessingListener(in IThemeProcessingListener listener);
+
+ oneway void rebuildResourceCache();
+
+ long getLastThemeChangeTime();
+ int getLastThemeChangeRequestType();
+}
diff --git a/sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.aidl b/sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.aidl
new file mode 100644
index 0000000..e1d9e4f
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015-2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.themes;
+
+parcelable ThemeChangeRequest;
diff --git a/sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.java b/sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.java
new file mode 100644
index 0000000..f8eeeb5
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/themes/ThemeChangeRequest.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2015-2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package cyanogenmod.themes;
+
+import android.content.pm.ThemeUtils;
+import android.content.res.ThemeConfig;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import cyanogenmod.os.Concierge;
+import cyanogenmod.os.Concierge.ParcelInfo;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static cyanogenmod.providers.ThemesContract.ThemesColumns.*;
+
+public final class ThemeChangeRequest implements Parcelable {
+ public static final int DEFAULT_WALLPAPER_ID = -1;
+
+ private final Map<String, String> mThemeComponents = new HashMap<>();
+ private final Map<String, String> mPerAppOverlays = new HashMap<>();
+ private RequestType mRequestType;
+ private long mWallpaperId = -1;
+
+ public String getOverlayThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_OVERLAYS);
+ }
+
+ public String getStatusBarThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_STATUS_BAR);
+ }
+
+ public String getNavBarThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_NAVIGATION_BAR);
+ }
+
+ public String getFontThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_FONTS);
+ }
+
+ public String getIconsThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_ICONS);
+ }
+
+ public String getBootanimationThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_BOOT_ANIM);
+ }
+
+ public String getWallpaperThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_LAUNCHER);
+ }
+
+ public String getLockWallpaperThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_LOCKSCREEN);
+ }
+
+ public String getAlarmThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_ALARMS);
+ }
+
+ public String getNotificationThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_NOTIFICATIONS);
+ }
+
+ public String getRingtoneThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_RINGTONES);
+ }
+
+ public String getLiveLockScreenThemePackageName() {
+ return getThemePackageNameForComponent(MODIFIES_LIVE_LOCK_SCREEN);
+ }
+
+ public final Map<String, String> getThemeComponentsMap() {
+ return Collections.unmodifiableMap(mThemeComponents);
+ }
+
+ public long getWallpaperId() {
+ return mWallpaperId;
+ }
+
+ /**
+ * Get the mapping for per app themes
+ * @return A mapping of apps and the theme to apply for each one. or null if none set.
+ */
+ public final Map<String, String> getPerAppOverlays() {
+ return Collections.unmodifiableMap(mPerAppOverlays);
+ }
+
+ public int getNumChangesRequested() {
+ return mThemeComponents.size() + mPerAppOverlays.size();
+ }
+
+ public RequestType getReqeustType() {
+ return mRequestType;
+ }
+
+ private String getThemePackageNameForComponent(String componentName) {
+ return mThemeComponents.get(componentName);
+ }
+
+ private ThemeChangeRequest(Map<String, String> components, Map<String, String> perAppThemes,
+ RequestType requestType, long wallpaperId) {
+ if (components != null) {
+ mThemeComponents.putAll(components);
+ }
+ if (perAppThemes != null) {
+ mPerAppOverlays.putAll(perAppThemes);
+ }
+ mRequestType = requestType;
+ mWallpaperId = wallpaperId;
+ }
+
+ private ThemeChangeRequest(Parcel source) {
+ // Read parcelable version via the Concierge
+ ParcelInfo parcelInfo = Concierge.receiveParcel(source);
+ int parcelableVersion = parcelInfo.getParcelVersion();
+
+ int numComponents = source.readInt();
+ for (int i = 0; i < numComponents; i++) {
+ mThemeComponents.put(source.readString(), source.readString());
+ }
+
+ numComponents = source.readInt();
+ for (int i = 0 ; i < numComponents; i++) {
+ mPerAppOverlays.put(source.readString(), source.readString());
+ }
+ mRequestType = RequestType.values()[source.readInt()];
+ mWallpaperId = source.readLong();
+
+ // Complete parcel info for the concierge
+ parcelInfo.complete();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ // Tell the concierge to prepare the parcel
+ ParcelInfo parcelInfo = Concierge.prepareParcel(dest);
+
+ dest.writeInt(mThemeComponents.size());
+ for (String component : mThemeComponents.keySet()) {
+ dest.writeString(component);
+ dest.writeString(mThemeComponents.get(component));
+ }
+ dest.writeInt((mPerAppOverlays.size()));
+ for (String appPkgName : mPerAppOverlays.keySet()) {
+ dest.writeString(appPkgName);
+ dest.writeString(mPerAppOverlays.get(appPkgName));
+ }
+ dest.writeInt(mRequestType.ordinal());
+ dest.writeLong(mWallpaperId);
+
+ // Complete the parcel info for the concierge
+ parcelInfo.complete();
+ }
+
+ public static final Parcelable.Creator<ThemeChangeRequest> CREATOR =
+ new Parcelable.Creator<ThemeChangeRequest>() {
+ @Override
+ public ThemeChangeRequest createFromParcel(Parcel source) {
+ return new ThemeChangeRequest(source);
+ }
+
+ @Override
+ public ThemeChangeRequest[] newArray(int size) {
+ return new ThemeChangeRequest[size];
+ }
+ };
+
+ public enum RequestType {
+ USER_REQUEST,
+ USER_REQUEST_MIXNMATCH,
+ THEME_UPDATED,
+ THEME_REMOVED,
+ THEME_RESET
+ }
+
+ public static class Builder {
+ Map<String, String> mThemeComponents = new HashMap<>();
+ Map<String, String> mPerAppOverlays = new HashMap<>();
+ RequestType mRequestType = RequestType.USER_REQUEST;
+ long mWallpaperId;
+
+ public Builder() {}
+
+ public Builder(ThemeConfig themeConfig) {
+ if (themeConfig != null) {
+ buildChangeRequestFromThemeConfig(themeConfig);
+ }
+ }
+
+ public Builder setOverlay(String pkgName) {
+ return setComponent(MODIFIES_OVERLAYS, pkgName);
+ }
+
+ public Builder setStatusBar(String pkgName) {
+ return setComponent(MODIFIES_STATUS_BAR, pkgName);
+ }
+
+ public Builder setNavBar(String pkgName) {
+ return setComponent(MODIFIES_NAVIGATION_BAR, pkgName);
+ }
+
+ public Builder setFont(String pkgName) {
+ return setComponent(MODIFIES_FONTS, pkgName);
+ }
+
+ public Builder setIcons(String pkgName) {
+ return setComponent(MODIFIES_ICONS, pkgName);
+ }
+
+ public Builder setBootanimation(String pkgName) {
+ return setComponent(MODIFIES_BOOT_ANIM, pkgName);
+ }
+
+ public Builder setWallpaper(String pkgName) {
+ return setComponent(MODIFIES_LAUNCHER, pkgName);
+ }
+
+ // Used in the case that more than one wallpaper exists for a given pkg name
+ public Builder setWallpaperId(long id) {
+ mWallpaperId = id;
+ return this;
+ }
+
+ public Builder setLockWallpaper(String pkgName) {
+ return setComponent(MODIFIES_LOCKSCREEN, pkgName);
+ }
+
+ public Builder setAlarm(String pkgName) {
+ return setComponent(MODIFIES_ALARMS, pkgName);
+ }
+
+ public Builder setNotification(String pkgName) {
+ return setComponent(MODIFIES_NOTIFICATIONS, pkgName);
+ }
+
+ public Builder setRingtone(String pkgName) {
+ return setComponent(MODIFIES_RINGTONES, pkgName);
+ }
+
+ public Builder setLiveLockScreen(String pkgName) {
+ return setComponent(MODIFIES_LIVE_LOCK_SCREEN, pkgName);
+ }
+
+ public Builder setComponent(String component, String pkgName) {
+ if (pkgName != null) {
+ mThemeComponents.put(component, pkgName);
+ } else {
+ mThemeComponents.remove(component);
+ }
+ return this;
+ }
+
+ public Builder setAppOverlay(String appPkgName, String themePkgName) {
+ if (appPkgName != null) {
+ if (themePkgName != null) {
+ mPerAppOverlays.put(appPkgName, themePkgName);
+ } else {
+ mPerAppOverlays.remove(appPkgName);
+ }
+ }
+
+ return this;
+ }
+
+ public Builder setRequestType(RequestType requestType) {
+ mRequestType = requestType != null ? requestType : RequestType.USER_REQUEST;
+ return this;
+ }
+
+ public ThemeChangeRequest build() {
+ return new ThemeChangeRequest(mThemeComponents, mPerAppOverlays,
+ mRequestType, mWallpaperId);
+ }
+
+ private void buildChangeRequestFromThemeConfig(ThemeConfig themeConfig) {
+ if (themeConfig.getFontPkgName() != null) {
+ this.setFont(themeConfig.getFontPkgName());
+ }
+ if (themeConfig.getIconPackPkgName() != null) {
+ this.setIcons(themeConfig.getIconPackPkgName());
+ }
+ if (themeConfig.getOverlayPkgName() != null) {
+ this.setOverlay(themeConfig.getOverlayPkgName());
+ }
+ if (themeConfig.getOverlayForStatusBar() != null) {
+ this.setStatusBar(themeConfig.getOverlayForStatusBar());
+ }
+ if (themeConfig.getOverlayForNavBar() != null) {
+ this.setNavBar(themeConfig.getOverlayForNavBar());
+ }
+
+ // Check if there are any per-app overlays using this theme
+ final Map<String, ThemeConfig.AppTheme> themes = themeConfig.getAppThemes();
+ for (String appPkgName : themes.keySet()) {
+ if (ThemeUtils.isPerAppThemeComponent(appPkgName)) {
+ this.setAppOverlay(appPkgName, themes.get(appPkgName).getOverlayPkgName());
+ }
+ }
+ }
+ }
+}
diff --git a/sdk/src/java/cyanogenmod/themes/ThemeManager.java b/sdk/src/java/cyanogenmod/themes/ThemeManager.java
new file mode 100644
index 0000000..5fbbde7
--- /dev/null
+++ b/sdk/src/java/cyanogenmod/themes/ThemeManager.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2014-2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.themes;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArraySet;
+import android.util.Log;
+
+import cyanogenmod.app.CMContextConstants;
+import cyanogenmod.themes.ThemeChangeRequest.RequestType;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manages changing and applying of themes.
+ * <p>Get an instance of this class by calling blah blah blah</p>
+ */
+public class ThemeManager {
+ private static final String TAG = ThemeManager.class.getName();
+ private static IThemeService sService;
+ private static ThemeManager sInstance;
+ private static Handler mHandler;
+
+ private Set<ThemeChangeListener> mChangeListeners = new ArraySet<>();
+
+ private Set<ThemeProcessingListener> mProcessingListeners = new ArraySet<>();
+
+ private ThemeManager(Context context) {
+ sService = getService();
+ if (context.getPackageManager().hasSystemFeature(
+ CMContextConstants.Features.THEMES) && sService == null) {
+ throw new RuntimeException("Unable to get ThemeManagerService. The service either" +
+ " crashed, was not started, or the interface has been called to early in" +
+ " SystemServer init");
+ }
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ public static ThemeManager getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new ThemeManager(context);
+ }
+
+ return sInstance;
+ }
+
+ /** @hide */
+ public static IThemeService getService() {
+ if (sService != null) {
+ return sService;
+ }
+ IBinder b = ServiceManager.getService(CMContextConstants.CM_THEME_SERVICE);
+ if (b != null) {
+ sService = IThemeService.Stub.asInterface(b);
+ return sService;
+ }
+ return null;
+ }
+
+ private final IThemeChangeListener mThemeChangeListener = new IThemeChangeListener.Stub() {
+ @Override
+ public void onProgress(final int progress) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mChangeListeners) {
+ List<ThemeChangeListener> listenersToRemove = new ArrayList<>();
+ for (ThemeChangeListener listener : mChangeListeners) {
+ try {
+ listener.onProgress(progress);
+ } catch (Throwable e) {
+ Log.w(TAG, "Unable to update theme change progress", e);
+ listenersToRemove.add(listener);
+ }
+ }
+ if (listenersToRemove.size() > 0) {
+ for (ThemeChangeListener listener : listenersToRemove) {
+ mChangeListeners.remove(listener);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onFinish(final boolean isSuccess) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mChangeListeners) {
+ List<ThemeChangeListener> listenersToRemove = new ArrayList<>();
+ for (ThemeChangeListener listener : mChangeListeners) {
+ try {
+ listener.onFinish(isSuccess);
+ } catch (Throwable e) {
+ Log.w(TAG, "Unable to update theme change listener", e);
+ listenersToRemove.add(listener);
+ }
+ }
+ if (listenersToRemove.size() > 0) {
+ for (ThemeChangeListener listener : listenersToRemove) {
+ mChangeListeners.remove(listener);
+ }
+ }
+ }
+ }
+ });
+ }
+ };
+
+ private final IThemeProcessingListener mThemeProcessingListener =
+ new IThemeProcessingListener.Stub() {
+ @Override
+ public void onFinishedProcessing(final String pkgName) throws RemoteException {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mProcessingListeners) {
+ List<ThemeProcessingListener> listenersToRemove = new ArrayList<>();
+ for (ThemeProcessingListener listener : mProcessingListeners) {
+ try {
+ listener.onFinishedProcessing(pkgName);
+ } catch (Throwable e) {
+ Log.w(TAG, "Unable to update theme change progress", e);
+ listenersToRemove.add(listener);
+ }
+ }
+ if (listenersToRemove.size() > 0) {
+ for (ThemeProcessingListener listener : listenersToRemove) {
+ mProcessingListeners.remove(listener);
+ }
+ }
+ }
+ }
+ });
+ }
+ };
+
+
+ /**
+ * @deprecated Use {@link ThemeManager#registerThemeChangeListener(ThemeChangeListener)} instead
+ */
+ public void addClient(ThemeChangeListener listener) {
+ registerThemeChangeListener(listener);
+ }
+
+ /**
+ * @deprecated Use {@link ThemeManager#unregisterThemeChangeListener(ThemeChangeListener)}
+ * instead
+ */
+ public void removeClient(ThemeChangeListener listener) {
+ unregisterThemeChangeListener(listener);
+ }
+
+ /**
+ * @deprecated Use {@link ThemeManager#unregisterThemeChangeListener(ThemeChangeListener)}
+ * instead
+ */
+ public void onClientPaused(ThemeChangeListener listener) {
+ unregisterThemeChangeListener(listener);
+ }
+
+ /**
+ * @deprecated Use {@link ThemeManager#registerThemeChangeListener(ThemeChangeListener)} instead
+ */
+ public void onClientResumed(ThemeChangeListener listener) {
+ registerThemeChangeListener(listener);
+ }
+
+ /**
+ * @deprecated Use {@link ThemeManager#unregisterThemeChangeListener(ThemeChangeListener)}
+ * instead
+ */
+ public void onClientDestroyed(ThemeChangeListener listener) {
+ unregisterThemeChangeListener(listener);
+ }
+
+ /**
+ * Register a {@link ThemeChangeListener} to be notified when a theme is done being processed.
+ * @param listener {@link ThemeChangeListener} to register
+ */
+ public void registerThemeChangeListener(ThemeChangeListener listener) {
+ synchronized (mChangeListeners) {
+ if (mChangeListeners.contains(listener)) {
+ throw new IllegalArgumentException("Listener already registered");
+ }
+ if (mChangeListeners.size() == 0) {
+ try {
+ sService.requestThemeChangeUpdates(mThemeChangeListener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to register listener", e);
+ }
+ }
+ mChangeListeners.add(listener);
+ }
+ }
+
+ /**
+ * Unregister a {@link ThemeChangeListener}
+ * @param listener {@link ThemeChangeListener} to unregister
+ */
+ public void unregisterThemeChangeListener(ThemeChangeListener listener) {
+ synchronized (mChangeListeners) {
+ mChangeListeners.remove(listener);
+ if (mChangeListeners.size() == 0) {
+ try {
+ sService.removeUpdates(mThemeChangeListener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to unregister listener", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Register a {@link ThemeProcessingListener} to be notified when a theme is done being
+ * processed.
+ * @param listener {@link ThemeProcessingListener} to register
+ */
+ public void registerProcessingListener(ThemeProcessingListener listener) {
+ synchronized (mProcessingListeners) {
+ if (mProcessingListeners.contains(listener)) {
+ throw new IllegalArgumentException("Listener already registered");
+ }
+ if (mProcessingListeners.size() == 0) {
+ try {
+ sService.registerThemeProcessingListener(mThemeProcessingListener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to register listener", e);
+ }
+ }
+ mProcessingListeners.add(listener);
+ }
+ }
+
+ /**
+ * Unregister a {@link ThemeProcessingListener}.
+ * @param listener {@link ThemeProcessingListener} to unregister
+ */
+ public void unregisterProcessingListener(ThemeProcessingListener listener) {
+ synchronized (mProcessingListeners) {
+ mProcessingListeners.remove(listener);
+ if (mProcessingListeners.size() == 0) {
+ try {
+ sService.unregisterThemeProcessingListener(mThemeProcessingListener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to unregister listener", e);
+ }
+ }
+ }
+ }
+
+ public void requestThemeChange(String pkgName, List<String> components) {
+ requestThemeChange(pkgName, components, true);
+ }
+
+ public void requestThemeChange(String pkgName, List<String> components,
+ boolean removePerAppThemes) {
+ Map<String, String> componentMap = new HashMap<>(components.size());
+ for (String component : components) {
+ componentMap.put(component, pkgName);
+ }
+ requestThemeChange(componentMap, removePerAppThemes);
+ }
+
+ public void requestThemeChange(Map<String, String> componentMap) {
+ requestThemeChange(componentMap, true);
+ }
+
+ public void requestThemeChange(Map<String, String> componentMap, boolean removePerAppThemes) {
+ ThemeChangeRequest.Builder builder = new ThemeChangeRequest.Builder();
+ for (String component : componentMap.keySet()) {
+ builder.setComponent(component, componentMap.get(component));
+ }
+
+ requestThemeChange(builder.build(), removePerAppThemes);
+ }
+
+ public void requestThemeChange(ThemeChangeRequest request, boolean removePerAppThemes) {
+ try {
+ sService.requestThemeChange(request, removePerAppThemes);
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+ }
+
+ public void applyDefaultTheme() {
+ try {
+ sService.applyDefaultTheme();
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+ }
+
+ public boolean isThemeApplying() {
+ try {
+ return sService.isThemeApplying();
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+
+ return false;
+ }
+
+ public boolean isThemeBeingProcessed(String themePkgName) {
+ try {
+ return sService.isThemeBeingProcessed(themePkgName);
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+ return false;
+ }
+
+ public int getProgress() {
+ try {
+ return sService.getProgress();
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+ return -1;
+ }
+
+ public boolean processThemeResources(String themePkgName) {
+ try {
+ return sService.processThemeResources(themePkgName);
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+ return false;
+ }
+
+ public long getLastThemeChangeTime() {
+ try {
+ return sService.getLastThemeChangeTime();
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+ return 0;
+ }
+
+ public ThemeChangeRequest.RequestType getLastThemeChangeRequestType() {
+ try {
+ int type = sService.getLastThemeChangeRequestType();
+ return (type >= 0 && type < RequestType.values().length)
+ ? RequestType.values()[type]
+ : null;
+ } catch (RemoteException e) {
+ logThemeServiceException(e);
+ }
+
+ return null;
+ }
+
+ private void logThemeServiceException(Exception e) {
+ Log.w(TAG, "Unable to access ThemeService", e);
+ }
+
+ public interface ThemeChangeListener {
+ void onProgress(int progress);
+ void onFinish(boolean isSuccess);
+ }
+
+ public interface ThemeProcessingListener {
+ void onFinishedProcessing(String pkgName);
+ }
+}
+