/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * 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 com.pixate.freestyle; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import android.annotation.TargetApi; import android.app.ActionBar; import android.app.Activity; import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.app.Fragment; import android.content.Context; import android.os.Build; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.Adapter; import android.widget.AdapterView; import com.pixate.freestyle.styling.PXStyleUtils; import com.pixate.freestyle.styling.PXStylesheet; import com.pixate.freestyle.styling.PXStylesheet.PXStyleSheetOrigin; import com.pixate.freestyle.util.PXLog; import com.pixate.freestyle.util.ViewUtil; /** * <p> * Application developers use the static methods in this class to initialize and * use Pixate in their applications. * </p> * <p> * If the developer targets only Ice Cream Sandwich and above, he/she can * initialize Pixate simply be calling {@link #init(Context) init} one time in * the {@link android.app.Application#onCreate() onCreate()} of his * {@link android.app.Application Application} class. For example: * </p> * * <pre> * public void onCreate() { * PixateFreestyle.init(this); * } * </pre> * <p> * If targeting all versions of Android, then {@link #init(Context) init} needs * to be called in each {@link android.app.Activity Activity}'s * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} method. For * example: * </p> * * <pre> * public void onCreate(Bundle savedInstanceState) { * PixateFreestyle.init(this); * } * </pre> * <p> * Views defined in layout XML files can use the attributes <code>class</code>, * <code>android:style</code> and <code>android:id</code> to emulate HTML * <code>class</code>, <code>style</code> and <code>id</code> element * attributes. Note that <code>class</code> does not contain the * <code>android:</code> prefix. Example: * </p> * * <pre> * <Button * android:id="@+id/myButton" * class="critical" * (etc.) * /> * </pre> * <p> * When the layout is inflated, Pixate intercepts the inflation and then has an * opportunity to apply the appropriate styling based on the values of those * attributes. * </p> * <p> * If a view is created at runtime - and is therefore not going to be styled via * Pixate's interception of the layout inflater - the application developer can * call {@link #init(View, String, String, String)} to tell Pixate the id, class * and style of the view, or set them individually via * {@link #setStyleId(View, String)}, {@link #setStyleClass(View, String)} * and/or {@link #setStyle(View, String)}. * </p> * * @author Bill Dawson */ public class PixateFreestyle { private static final String TAG = PixateFreestyle.class.getSimpleName(); public static final boolean ICS_OR_BETTER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH; private static String DEFAULT_CSS = "default.css"; private static AtomicReference<String> currentCSS = new AtomicReference<String>(); private static Object mLifecycleCallbacks = null; private static boolean mAppInited = false; private static Context mAppContext = null; /** * Initialize Pixate with the given {@link Context}, using the styles in default.css. * * @param context */ public static void init(Context context) { init(context, DEFAULT_CSS); } /** * Initialize Pixate with the given {@link Context} and CSS file. * * @param context * @param cssFileName The CSS file to load styles from */ public static void init(Context context, String cssFileName) { if (mAppContext == null) { mAppContext = context.getApplicationContext(); // log a version Log.i(TAG, String.format("Pixate Freestyle version %s (API version %d)", getVersion(), getApiVersion())); } if (cssFileName != currentCSS.getAndSet(cssFileName)) { // try to load the default CSS ones. PXStylesheet stylesheet = PXStylesheet.getStyleSheetFromFilePath( context.getApplicationContext(), cssFileName, PXStyleSheetOrigin.APPLICATION); if (stylesheet != null) { logErrors(stylesheet.getErrors()); } } // Disabled, because we may not even need a class loader. // CustomClassLoader.useFor(context); if (ICS_OR_BETTER && !mAppInited) { initApp(context); } if (context instanceof Activity) { Activity activity = (Activity) context; WrappedInflaterFactory.applyTo(activity); // Grab the 'decorView' which contains the ActionBar and the // content. View decorView = activity.getWindow().getDecorView(); ViewUtil.prepareViewGroupListeners((ViewGroup) decorView); // We have to get to the tabs in case the ActionBar mode has them // enabled. ActionBar actionBar = activity.getActionBar(); // Call the style whatever we can using the ActionBar instance // itself (a non-view styling). style(actionBar); } } /** * Initialize Pixate with the given Fragment. * * @param fragment */ public static void init(Fragment fragment) { init(fragment.getActivity()); } public static void init(View view, String cssId, String cssClass, String cssStyle) { ViewUtil.initView(view, cssId, cssClass, cssStyle); } /** * Returns the Pixate Freestyle product version. * * @return The product version. */ public static String getVersion() { return Version.PIXATE_FREESTYLE_VERSION; } /** * Returns the Pixate Freestyle API version. * * @return The API version. */ public static int getApiVersion() { return Version.PIXATE_FREESTYLE_API_VERSION; } public static void setStyleId(View view, String cssId) { ViewUtil.setStyleId(view, cssId); } public static void setStyleId(View view, String cssId, boolean restyle) { ViewUtil.setStyleId(view, cssId, restyle); } public static String getStyleId(View view) { return ViewUtil.getStyleId(view); } public static void setStyleClass(View view, String cssClass) { ViewUtil.setStyleClass(view, cssClass); } public static void setStyleClass(View view, String cssClass, boolean restyle) { ViewUtil.setStyleClass(view, cssClass, restyle); } public static String getStyleClass(View view) { return ViewUtil.getStyleClass(view); } public static void setStyle(View view, String cssStyle) { ViewUtil.setStyle(view, cssStyle); } public static Context getAppContext() { return mAppContext; } /** * Returns the {@link Adapter} that is nested in the given * {@link AdapterView}. In case the adapter is 'proxied', try to extract the * original {@link Adapter} from the proxy instance. * * @param view * @return The original {@link Adapter} that was set for the * {@link AdapterView} (can be null) */ public static Adapter getAdapter(AdapterView<?> view) { Adapter adapter = view.getAdapter(); if (adapter != null && Proxy.isProxyClass(adapter.getClass())) { InvocationHandler handler = Proxy.getInvocationHandler(adapter); if (handler instanceof PXAdapterInvocationHandler) { adapter = ((PXAdapterInvocationHandler) handler).getOriginal(); } } return adapter; } /** * Styles a styleable. * * @param styleable * @see #style(Object, boolean) */ protected static void style(Object styleable) { // Assumes view has already been init'd and its // css class / id / style properties set for // Pixate to read. PXStyleUtils.updateStyles(styleable, true); } /** * Style a styleable instance (usually a View, but can be other instances, * like ActionBar). * * @param styleable * @param styleChildren In case <code>true</code>, styles the children. */ protected static void style(Object styleable, boolean styleChildren) { PXStyleUtils.updateStyles(styleable, styleChildren); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private static void initApp(Context context) { Application application = (Application) mAppContext; if (application != null) { mAppInited = true; if (mLifecycleCallbacks == null) { mLifecycleCallbacks = new PXLifecycleCallbacks(); } application .unregisterActivityLifecycleCallbacks((ActivityLifecycleCallbacks) mLifecycleCallbacks); application .registerActivityLifecycleCallbacks((ActivityLifecycleCallbacks) mLifecycleCallbacks); } } private static void logErrors(List<String> errors) { if (PXLog.isLogging() && errors != null) { for (String e : errors) { PXLog.e(TAG, e); } } } }