diff --git a/android/build.gradle b/android/build.gradle index 099e2d2ac5d..86eb3e36fad 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -11,6 +11,7 @@ buildscript { repositories { google() mavenCentral() + maven { url 'https://jitpack.io' } } dependencies { classpath 'com.android.tools.build:gradle:8.8.2' diff --git a/android/modules/ui/res/values/attrs.xml b/android/modules/ui/res/values/attrs.xml index dbc78c01f74..c8bf2371b06 100644 --- a/android/modules/ui/res/values/attrs.xml +++ b/android/modules/ui/res/values/attrs.xml @@ -10,4 +10,7 @@ + + + diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/BlurViewProxy.java b/android/modules/ui/src/java/ti/modules/titanium/ui/BlurViewProxy.java new file mode 100644 index 00000000000..54f91b85861 --- /dev/null +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/BlurViewProxy.java @@ -0,0 +1,37 @@ + /** + * Titanium SDK + * Copyright TiDev, Inc. 04/07/2022-Present + * Licensed under the terms of the Apache Public License + * Please see the LICENSE included with this distribution for details. + */ +package ti.modules.titanium.ui; + +import android.app.Activity; + +import org.appcelerator.kroll.annotations.Kroll; +import org.appcelerator.titanium.proxy.TiViewProxy; +import org.appcelerator.titanium.view.TiUIView; + +import ti.modules.titanium.ui.widget.TiUIBlurView; + +@Kroll.proxy(creatableInModule = UIModule.class) +public class BlurViewProxy extends TiViewProxy +{ + public BlurViewProxy() + { + super(); + } + + @Override + public TiUIView createView(Activity activity) + { + return new TiUIBlurView(this); + } + + @Override + public String getApiName() + { + return "Ti.UI.BlurView"; + } +} + diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/UIModule.java b/android/modules/ui/src/java/ti/modules/titanium/ui/UIModule.java index 19d77525a1f..684f8d0fb94 100644 --- a/android/modules/ui/src/java/ti/modules/titanium/ui/UIModule.java +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/UIModule.java @@ -220,6 +220,14 @@ public class UIModule extends KrollModule implements TiApplication.Configuration @Kroll.constant public static final int BUTTON_STYLE_OPTION_NEUTRAL = 6; + // BlurView styles (cross-platform constants) + @Kroll.constant + public static final int BLUR_EFFECT_STYLE_EXTRA_LIGHT = 0; + @Kroll.constant + public static final int BLUR_EFFECT_STYLE_LIGHT = 1; + @Kroll.constant + public static final int BLUR_EFFECT_STYLE_DARK = 2; + @Kroll.constant public static final int DATE_PICKER_STYLE_AUTOMATIC = 1; @Kroll.constant diff --git a/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBlurView.java b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBlurView.java new file mode 100644 index 00000000000..3e5bf99b9c0 --- /dev/null +++ b/android/modules/ui/src/java/ti/modules/titanium/ui/widget/TiUIBlurView.java @@ -0,0 +1,227 @@ +/** +* Titanium SDK +* Copyright TiDev, Inc. 04/07/2022-Present +* Licensed under the terms of the Apache Public License +* Please see the LICENSE included with this distribution for details. +*/ +package ti.modules.titanium.ui.widget; + +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import org.appcelerator.kroll.KrollDict; +import org.appcelerator.kroll.KrollProxy; +import org.appcelerator.kroll.common.Log; +import org.appcelerator.titanium.TiC; +import org.appcelerator.titanium.proxy.TiViewProxy; +import org.appcelerator.titanium.util.TiConvert; +import org.appcelerator.titanium.view.TiCompositeLayout; +import org.appcelerator.titanium.view.TiUIView; + +import androidx.annotation.Nullable; + +import eightbitlab.com.blurview.BlurView; +import ti.modules.titanium.ui.UIModule; + +public class TiUIBlurView extends TiUIView +{ + private static final String TAG = "TiUIBlurView"; + + // Custom property keys (Android-only) + private static final String PROPERTY_EFFECT = "effect"; // Number (Ti.UI.BLUR_EFFECT_STYLE_*) + private static final String PROPERTY_BLUR_RADIUS = "blurRadius"; // Number + private static final String PROPERTY_OVERLAY_COLOR = "overlayColor"; // String color + + private final BlurView blurView; + private final TiCompositeLayout contentLayout; + + // Current config + private float blurRadius = 16f; + private int overlayColor = 0x00FFFFFF; // transparent by default + private int effectStyle = -1; // unset + + private class BlurContainer extends FrameLayout + { + final TiCompositeLayout layout; + final BlurView innerBlurView; + + BlurContainer() + { + super(proxy.getActivity()); + + // Determine arrangement from proxy + TiCompositeLayout.LayoutArrangement arrangement = TiCompositeLayout.LayoutArrangement.DEFAULT; + Object layoutValue = proxy.getProperty(TiC.PROPERTY_LAYOUT); + if (TiC.LAYOUT_VERTICAL.equals(layoutValue)) { + arrangement = TiCompositeLayout.LayoutArrangement.VERTICAL; + } else if (TiC.LAYOUT_HORIZONTAL.equals(layoutValue)) { + arrangement = TiCompositeLayout.LayoutArrangement.HORIZONTAL; + } + + // Create blur and content layout + innerBlurView = new BlurView(proxy.getActivity()); + innerBlurView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + layout = new TiCompositeLayout(proxy.getActivity(), arrangement, proxy); + layout.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + // Add in order: blur background then content overlay + super.addView(innerBlurView); + super.addView(layout); + } + + TiCompositeLayout getLayout() + { + return layout; + } + + BlurView getBlurView() + { + return innerBlurView; + } + + @Override + public void addView(View child, ViewGroup.LayoutParams params) + { + // Route adds to TiCompositeLayout to ensure correct LayoutParams + layout.addView(child, params); + } + } + + public TiUIBlurView(TiViewProxy proxy) + { + super(proxy); + + BlurContainer container = new BlurContainer(); + this.blurView = container.getBlurView(); + this.contentLayout = container.getLayout(); + + setNativeView(container); + // Defer setup until we know the target (if provided) + setupOrUpdateBlur(); + } + + @Override + public void processProperties(KrollDict d) + { + super.processProperties(d); + + if (d.containsKey(PROPERTY_BLUR_RADIUS)) { + this.blurRadius = TiConvert.toFloat(d, PROPERTY_BLUR_RADIUS, this.blurRadius); + } + if (d.containsKey(PROPERTY_OVERLAY_COLOR)) { + this.overlayColor = TiConvert.toColor(d.get(PROPERTY_OVERLAY_COLOR), proxy.getActivity()); + } + if (d.containsKey(PROPERTY_EFFECT)) { + this.effectStyle = TiConvert.toInt(d.get(PROPERTY_EFFECT), -1); + applyEffectPresetIfAny(); + } + + setupOrUpdateBlur(); + } + + @Override + public void propertyChanged(String key, Object oldValue, Object newValue, KrollProxy proxy) + { + if (Log.isDebugModeEnabled()) { + Log.d(TAG, "Property changed: " + key + ", new: " + newValue, Log.DEBUG_MODE); + } + + if (PROPERTY_BLUR_RADIUS.equals(key)) { + this.blurRadius = TiConvert.toFloat(newValue, this.blurRadius); + if (blurView != null) { + blurView.setBlurRadius(this.blurRadius); + } + } else if (PROPERTY_OVERLAY_COLOR.equals(key)) { + this.overlayColor = TiConvert.toColor(newValue, proxy.getActivity()); + if (blurView != null) { + blurView.setOverlayColor(this.overlayColor); + } + } else if (PROPERTY_EFFECT.equals(key)) { + this.effectStyle = TiConvert.toInt(newValue, -1); + applyEffectPresetIfAny(); + if (blurView != null) { + blurView.setOverlayColor(this.overlayColor); + blurView.setBlurRadius(this.blurRadius); + } + } else { + super.propertyChanged(key, oldValue, newValue, proxy); + } + } + + private void setupOrUpdateBlur() + { + if (blurView == null) { + return; + } + + Drawable windowBackground = getWindowBackground(); + if (windowBackground == null) { + windowBackground = new ColorDrawable(Color.TRANSPARENT); + } + + // Legacy API: setup with root content view, using RenderScriptBlur for <31 + ViewGroup rootView = getRootContentView(); + if (rootView == null) { + return; + } + + blurView.setupWith(rootView) + .setFrameClearDrawable(windowBackground) + .setBlurRadius(this.blurRadius) + .setOverlayColor(this.overlayColor); + } + + private void applyEffectPresetIfAny() + { + // Map iOS-like styles to Android parameters. + // These constants are defined on Ti.UI.* + // Default values already set on fields. + switch (this.effectStyle) { + case UIModule.BLUR_EFFECT_STYLE_EXTRA_LIGHT: + this.blurRadius = 16f; + this.overlayColor = 0x66FFFFFF; // strongest white tint + break; + case UIModule.BLUR_EFFECT_STYLE_LIGHT: + this.blurRadius = 16f; + this.overlayColor = 0x44FFFFFF; // medium white tint + break; + case UIModule.BLUR_EFFECT_STYLE_DARK: + this.blurRadius = 16f; + this.overlayColor = 0x66000000; // dark tint + break; + default: + // leave custom values as-is when unknown + break; + } + } + + @Nullable + private ViewGroup getRootContentView() + { + if (proxy == null || proxy.getActivity() == null || proxy.getActivity().getWindow() == null) { + return null; + } + View decor = proxy.getActivity().getWindow().getDecorView(); + View content = decor.findViewById(android.R.id.content); + if (content instanceof ViewGroup) { + return (ViewGroup) content; + } + return null; + } + + @Nullable + private Drawable getWindowBackground() + { + if (proxy == null || proxy.getActivity() == null || proxy.getActivity().getWindow() == null) { + return null; + } + return proxy.getActivity().getWindow().getDecorView().getBackground(); + } +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/BlurAlgorithm.java b/android/titanium/src/java/eightbitlab/com/blurview/BlurAlgorithm.java new file mode 100644 index 00000000000..0c2b458197f --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/BlurAlgorithm.java @@ -0,0 +1,43 @@ +package eightbitlab.com.blurview; + +import android.graphics.Bitmap; +import android.graphics.Canvas; + +import androidx.annotation.NonNull; + +public interface BlurAlgorithm { + /** + * @param bitmap bitmap to be blurred + * @param blurRadius blur radius + * @return blurred bitmap + */ + Bitmap blur(@NonNull Bitmap bitmap, @NonNull float blurRadius); + + /** + * Frees allocated resources + */ + void destroy(); + + /** + * @return true if this algorithm returns the same instance of bitmap as it accepted + * false if it creates a new instance. + *

+ * If you return false from this method, you'll be responsible to swap bitmaps in your + * {@link BlurAlgorithm#blur(Bitmap, float)} implementation + * (assign input bitmap to your field and return the instance algorithm just blurred). + */ + boolean canModifyBitmap(); + + /** + * Retrieve the {@link android.graphics.Bitmap.Config} on which the {@link BlurAlgorithm} + * can actually work. + * + * @return bitmap config supported by the given blur algorithm. + */ + @NonNull + Bitmap.Config getSupportedBitmapConfig(); + + float scaleFactor(); + + void render(@NonNull Canvas canvas, @NonNull Bitmap bitmap); +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/BlurController.java b/android/titanium/src/java/eightbitlab/com/blurview/BlurController.java new file mode 100644 index 00000000000..059bdd9b815 --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/BlurController.java @@ -0,0 +1,26 @@ +package eightbitlab.com.blurview; + +import android.graphics.Canvas; + +public interface BlurController extends BlurViewFacade { + + float DEFAULT_SCALE_FACTOR = 6f; + float DEFAULT_BLUR_RADIUS = 16f; + + /** + * Draws blurred content on given canvas + * + * @return true if BlurView should proceed with drawing itself and its children + */ + boolean draw(Canvas canvas); + + /** + * Must be used to notify Controller when BlurView's size has changed + */ + void updateBlurViewSize(); + + /** + * Frees allocated resources + */ + void destroy(); +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/BlurView.java b/android/titanium/src/java/eightbitlab/com/blurview/BlurView.java new file mode 100644 index 00000000000..f75c140cd9c --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/BlurView.java @@ -0,0 +1,171 @@ +package eightbitlab.com.blurview; + +import static eightbitlab.com.blurview.PreDrawBlurController.TRANSPARENT; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import org.appcelerator.titanium.R; + +/** + * FrameLayout that blurs its underlying content. + * Can have children and draw them over blurred background. + */ +public class BlurView extends FrameLayout +{ + + private static final String TAG = BlurView.class.getSimpleName(); + + BlurController blurController = new NoOpController(); + + @ColorInt + private int overlayColor; + + public BlurView(Context context) + { + super(context); + init(null, 0); + } + + public BlurView(Context context, AttributeSet attrs) + { + super(context, attrs); + init(attrs, 0); + } + + public BlurView(Context context, AttributeSet attrs, int defStyleAttr) + { + super(context, attrs, defStyleAttr); + init(attrs, defStyleAttr); + } + + private void init(AttributeSet attrs, int defStyleAttr) + { + TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BlurView, defStyleAttr, 0); + overlayColor = a.getColor(R.styleable.BlurView_blurOverlayColor, TRANSPARENT); + a.recycle(); + } + + @Override + public void draw(Canvas canvas) + { + boolean shouldDraw = blurController.draw(canvas); + if (shouldDraw) { + super.draw(canvas); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) + { + super.onSizeChanged(w, h, oldw, oldh); + blurController.updateBlurViewSize(); + } + + @Override + protected void onDetachedFromWindow() + { + super.onDetachedFromWindow(); + blurController.setBlurAutoUpdate(false); + } + + @Override + protected void onAttachedToWindow() + { + super.onAttachedToWindow(); + if (!isHardwareAccelerated()) { + Log.e(TAG, "BlurView can't be used in not hardware-accelerated window!"); + } else { + blurController.setBlurAutoUpdate(true); + } + } + + /** + * @param rootView root to start blur from. + * Can be Activity's root content layout (android.R.id.content) + * or (preferably) some of your layouts. The lower amount of Views are in the root, the better for performance. + * @param algorithm sets the blur algorithm + * @return {@link BlurView} to setup needed params. + */ + public BlurViewFacade setupWith(@NonNull ViewGroup rootView, BlurAlgorithm algorithm) + { + this.blurController.destroy(); + BlurController blurController = new PreDrawBlurController(this, rootView, overlayColor, algorithm); + this.blurController = blurController; + + return blurController; + } + + /** + * @param rootView root to start blur from. + * Can be Activity's root content layout (android.R.id.content) + * or (preferably) some of your layouts. The lower amount of Views are in the root, the better for performance. + *

+ * BlurAlgorithm is automatically picked based on the API version. + * It uses RenderEffectBlur on API 31+, and RenderScriptBlur on older versions. + * @return {@link BlurView} to setup needed params. + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + public BlurViewFacade setupWith(@NonNull ViewGroup rootView) + { + return setupWith(rootView, getBlurAlgorithm()); + } + + // Setters duplicated to be able to conveniently change these settings outside of setupWith chain + + /** + * @see BlurViewFacade#setBlurRadius(float) + */ + public BlurViewFacade setBlurRadius(float radius) + { + return blurController.setBlurRadius(radius); + } + + /** + * @see BlurViewFacade#setOverlayColor(int) + */ + public BlurViewFacade setOverlayColor(@ColorInt int overlayColor) + { + this.overlayColor = overlayColor; + return blurController.setOverlayColor(overlayColor); + } + + /** + * @see BlurViewFacade#setBlurAutoUpdate(boolean) + */ + public BlurViewFacade setBlurAutoUpdate(boolean enabled) + { + return blurController.setBlurAutoUpdate(enabled); + } + + /** + * @see BlurViewFacade#setBlurEnabled(boolean) + */ + public BlurViewFacade setBlurEnabled(boolean enabled) + { + return blurController.setBlurEnabled(enabled); + } + + @NonNull + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + private BlurAlgorithm getBlurAlgorithm() + { + BlurAlgorithm algorithm; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + algorithm = new RenderEffectBlur(); + } else { + algorithm = new RenderScriptBlur(getContext()); + } + return algorithm; + } +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/BlurViewCanvas.java b/android/titanium/src/java/eightbitlab/com/blurview/BlurViewCanvas.java new file mode 100644 index 00000000000..8d3c12459d4 --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/BlurViewCanvas.java @@ -0,0 +1,16 @@ +package eightbitlab.com.blurview; + +import android.graphics.Bitmap; +import android.graphics.Canvas; + +import androidx.annotation.NonNull; + +// Serves purely as a marker of a Canvas used in BlurView +// to skip drawing itself and other BlurViews on the View hierarchy snapshot +public class BlurViewCanvas extends Canvas +{ + public BlurViewCanvas(@NonNull Bitmap bitmap) + { + super(bitmap); + } +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/BlurViewFacade.java b/android/titanium/src/java/eightbitlab/com/blurview/BlurViewFacade.java new file mode 100644 index 00000000000..814eef2a20e --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/BlurViewFacade.java @@ -0,0 +1,48 @@ +package eightbitlab.com.blurview; + +import android.graphics.drawable.Drawable; + +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; + +public interface BlurViewFacade { + + /** + * Enables/disables the blur. Enabled by default + * + * @param enabled true to enable, false otherwise + * @return {@link BlurViewFacade} + */ + BlurViewFacade setBlurEnabled(boolean enabled); + + /** + * Can be used to stop blur auto update or resume if it was stopped before. + * Enabled by default. + * + * @return {@link BlurViewFacade} + */ + BlurViewFacade setBlurAutoUpdate(boolean enabled); + + /** + * @param frameClearDrawable sets the drawable to draw before view hierarchy. + * Can be used to draw Activity's window background if your root layout doesn't provide any background + * Optional, by default frame is cleared with a transparent color. + * @return {@link BlurViewFacade} + */ + BlurViewFacade setFrameClearDrawable(@Nullable Drawable frameClearDrawable); + + /** + * @param radius sets the blur radius + * Default value is {@link BlurController#DEFAULT_BLUR_RADIUS} + * @return {@link BlurViewFacade} + */ + BlurViewFacade setBlurRadius(float radius); + + /** + * Sets the color overlay to be drawn on top of blurred content + * + * @param overlayColor int color + * @return {@link BlurViewFacade} + */ + BlurViewFacade setOverlayColor(@ColorInt int overlayColor); +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/NoOpController.java b/android/titanium/src/java/eightbitlab/com/blurview/NoOpController.java new file mode 100644 index 00000000000..b62a0bf7912 --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/NoOpController.java @@ -0,0 +1,56 @@ +package eightbitlab.com.blurview; + +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; + +import androidx.annotation.Nullable; + +// Used in edit mode and in case if no BlurController was set +public class NoOpController implements BlurController +{ + @Override + public boolean draw(Canvas canvas) + { + return true; + } + + @Override + public void updateBlurViewSize() + { + } + + @Override + public void destroy() + { + } + + @Override + public BlurViewFacade setBlurRadius(float radius) + { + return this; + } + + @Override + public BlurViewFacade setOverlayColor(int overlayColor) + { + return this; + } + + @Override + public BlurViewFacade setFrameClearDrawable(@Nullable Drawable windowBackground) + { + return this; + } + + @Override + public BlurViewFacade setBlurEnabled(boolean enabled) + { + return this; + } + + @Override + public BlurViewFacade setBlurAutoUpdate(boolean enabled) + { + return this; + } +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/PreDrawBlurController.java b/android/titanium/src/java/eightbitlab/com/blurview/PreDrawBlurController.java new file mode 100644 index 00000000000..29da15c99f3 --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/PreDrawBlurController.java @@ -0,0 +1,253 @@ +package eightbitlab.com.blurview; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Blur Controller that handles all blur logic for the attached View. + * It honors View size changes, View animation and Visibility changes. + *

+ * The basic idea is to draw the view hierarchy on a bitmap, excluding the attached View, + * then blur and draw it on the system Canvas. + *

+ * It uses {@link ViewTreeObserver.OnPreDrawListener} to detect when + * blur should be updated. + *

+ */ +public final class PreDrawBlurController implements BlurController +{ + + @ColorInt + public static final int TRANSPARENT = 0; + + private float blurRadius = DEFAULT_BLUR_RADIUS; + + private final BlurAlgorithm blurAlgorithm; + private BlurViewCanvas internalCanvas; + private Bitmap internalBitmap; + + @SuppressWarnings("WeakerAccess") + final View blurView; + private int overlayColor; + private final ViewGroup rootView; + private final int[] rootLocation = new int[2]; + private final int[] blurViewLocation = new int[2]; + + private final ViewTreeObserver.OnPreDrawListener drawListener = new ViewTreeObserver.OnPreDrawListener() + { + @Override + public boolean onPreDraw() + { + // Not invalidating a View here, just updating the Bitmap. + // This relies on the HW accelerated bitmap drawing behavior in Android + // If the bitmap was drawn on HW accelerated canvas, it holds a reference to it and on next + // drawing pass the updated content of the bitmap will be rendered on the screen + updateBlur(); + return true; + } + }; + + private boolean blurEnabled = true; + private boolean initialized; + + @Nullable + private Drawable frameClearDrawable; + + /** + * @param blurView View which will draw it's blurred underlying content + * @param rootView Root View where blurView's underlying content starts drawing. + * Can be Activity's root content layout (android.R.id.content) + * @param algorithm sets the blur algorithm + */ + public PreDrawBlurController(@NonNull View blurView, @NonNull ViewGroup rootView, + @ColorInt int overlayColor, BlurAlgorithm algorithm) + { + this.rootView = rootView; + this.blurView = blurView; + this.overlayColor = overlayColor; + this.blurAlgorithm = algorithm; + if (algorithm instanceof RenderEffectBlur) { + // noinspection NewApi + ((RenderEffectBlur) algorithm).setContext(blurView.getContext()); + } + + int measuredWidth = blurView.getMeasuredWidth(); + int measuredHeight = blurView.getMeasuredHeight(); + + init(measuredWidth, measuredHeight); + } + + @SuppressWarnings("WeakerAccess") + void init(int measuredWidth, int measuredHeight) + { + setBlurAutoUpdate(true); + SizeScaler sizeScaler = new SizeScaler(blurAlgorithm.scaleFactor()); + if (sizeScaler.isZeroSized(measuredWidth, measuredHeight)) { + // Will be initialized later when the View reports a size change + blurView.setWillNotDraw(true); + return; + } + + blurView.setWillNotDraw(false); + SizeScaler.Size bitmapSize = sizeScaler.scale(measuredWidth, measuredHeight); + internalBitmap = Bitmap.createBitmap(bitmapSize.width, bitmapSize.height, + blurAlgorithm.getSupportedBitmapConfig()); + internalCanvas = new BlurViewCanvas(internalBitmap); + initialized = true; + // Usually it's not needed, because `onPreDraw` updates the blur anyway. + // But it handles cases when the PreDraw listener is attached to a different Window, for example + // when the BlurView is in a Dialog window, but the root is in the Activity. + // Previously it was done in `draw`, but it was causing potential side effects and Jetpack Compose crashes + updateBlur(); + } + + @SuppressWarnings("WeakerAccess") + void updateBlur() + { + if (!blurEnabled || !initialized) { + return; + } + + if (frameClearDrawable == null) { + internalBitmap.eraseColor(Color.TRANSPARENT); + } else { + frameClearDrawable.draw(internalCanvas); + } + + internalCanvas.save(); + setupInternalCanvasMatrix(); + rootView.draw(internalCanvas); + internalCanvas.restore(); + + blurAndSave(); + } + + /** + * Set up matrix to draw starting from blurView's position + */ + private void setupInternalCanvasMatrix() + { + rootView.getLocationOnScreen(rootLocation); + blurView.getLocationOnScreen(blurViewLocation); + + int left = blurViewLocation[0] - rootLocation[0]; + int top = blurViewLocation[1] - rootLocation[1]; + + // https://github.com/Dimezis/BlurView/issues/128 + float scaleFactorH = (float) blurView.getHeight() / internalBitmap.getHeight(); + float scaleFactorW = (float) blurView.getWidth() / internalBitmap.getWidth(); + + float scaledLeftPosition = -left / scaleFactorW; + float scaledTopPosition = -top / scaleFactorH; + + internalCanvas.translate(scaledLeftPosition, scaledTopPosition); + internalCanvas.scale(1 / scaleFactorW, 1 / scaleFactorH); + } + + @Override + public boolean draw(Canvas canvas) + { + if (!blurEnabled || !initialized) { + return true; + } + // Not blurring itself or other BlurViews to not cause recursive draw calls + // Related: https://github.com/Dimezis/BlurView/issues/110 + if (canvas instanceof BlurViewCanvas) { + return false; + } + + // https://github.com/Dimezis/BlurView/issues/128 + float scaleFactorH = (float) blurView.getHeight() / internalBitmap.getHeight(); + float scaleFactorW = (float) blurView.getWidth() / internalBitmap.getWidth(); + + canvas.save(); + canvas.scale(scaleFactorW, scaleFactorH); + blurAlgorithm.render(canvas, internalBitmap); + canvas.restore(); + if (overlayColor != TRANSPARENT) { + canvas.drawColor(overlayColor); + } + return true; + } + + private void blurAndSave() + { + internalBitmap = blurAlgorithm.blur(internalBitmap, blurRadius); + if (!blurAlgorithm.canModifyBitmap()) { + internalCanvas.setBitmap(internalBitmap); + } + } + + @Override + public void updateBlurViewSize() + { + int measuredWidth = blurView.getMeasuredWidth(); + int measuredHeight = blurView.getMeasuredHeight(); + + init(measuredWidth, measuredHeight); + } + + @Override + public void destroy() + { + setBlurAutoUpdate(false); + blurAlgorithm.destroy(); + initialized = false; + } + + @Override + public BlurViewFacade setBlurRadius(float radius) + { + this.blurRadius = radius; + return this; + } + + @Override + public BlurViewFacade setFrameClearDrawable(@Nullable Drawable frameClearDrawable) + { + this.frameClearDrawable = frameClearDrawable; + return this; + } + + @Override + public BlurViewFacade setBlurEnabled(boolean enabled) + { + this.blurEnabled = enabled; + setBlurAutoUpdate(enabled); + blurView.invalidate(); + return this; + } + + public BlurViewFacade setBlurAutoUpdate(final boolean enabled) + { + rootView.getViewTreeObserver().removeOnPreDrawListener(drawListener); + blurView.getViewTreeObserver().removeOnPreDrawListener(drawListener); + if (enabled) { + rootView.getViewTreeObserver().addOnPreDrawListener(drawListener); + // Track changes in the blurView window too, for example if it's in a bottom sheet dialog + if (rootView.getWindowId() != blurView.getWindowId()) { + blurView.getViewTreeObserver().addOnPreDrawListener(drawListener); + } + } + return this; + } + + @Override + public BlurViewFacade setOverlayColor(int overlayColor) + { + if (this.overlayColor != overlayColor) { + this.overlayColor = overlayColor; + blurView.invalidate(); + } + return this; + } +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/RenderEffectBlur.java b/android/titanium/src/java/eightbitlab/com/blurview/RenderEffectBlur.java new file mode 100644 index 00000000000..e4f06f33bb8 --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/RenderEffectBlur.java @@ -0,0 +1,103 @@ +package eightbitlab.com.blurview; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.RenderEffect; +import android.graphics.RenderNode; +import android.graphics.Shader; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +/** + * Leverages the new RenderEffect.createBlurEffect API to perform blur. + * Hardware accelerated. + * Blur is performed on a separate thread - native RenderThread. + * It doesn't block the Main thread, however it can still cause an FPS drop, + * because it's just in a different part of the rendering pipeline. + */ +@RequiresApi(Build.VERSION_CODES.S) +public class RenderEffectBlur implements BlurAlgorithm +{ + + private final RenderNode node = new RenderNode("BlurViewNode"); + + private int height, width; + private float lastBlurRadius = 1f; + + @Nullable + public BlurAlgorithm fallbackAlgorithm; + private Context context; + + public RenderEffectBlur() + { + } + + @Override + public Bitmap blur(@NonNull Bitmap bitmap, float blurRadius) + { + lastBlurRadius = blurRadius; + + if (bitmap.getHeight() != height || bitmap.getWidth() != width) { + height = bitmap.getHeight(); + width = bitmap.getWidth(); + node.setPosition(0, 0, width, height); + } + Canvas canvas = node.beginRecording(); + canvas.drawBitmap(bitmap, 0, 0, null); + node.endRecording(); + node.setRenderEffect(RenderEffect.createBlurEffect(blurRadius, blurRadius, Shader.TileMode.MIRROR)); + // returning not blurred bitmap, because the rendering relies on the RenderNode + return bitmap; + } + + @Override + public void destroy() + { + node.discardDisplayList(); + if (fallbackAlgorithm != null) { + fallbackAlgorithm.destroy(); + } + } + + @Override + public boolean canModifyBitmap() + { + return true; + } + + @NonNull + @Override + public Bitmap.Config getSupportedBitmapConfig() + { + return Bitmap.Config.ARGB_8888; + } + + @Override + public float scaleFactor() + { + return BlurController.DEFAULT_SCALE_FACTOR; + } + + @Override + public void render(@NonNull Canvas canvas, @NonNull Bitmap bitmap) + { + if (canvas.isHardwareAccelerated()) { + canvas.drawRenderNode(node); + } else { + if (fallbackAlgorithm == null) { + fallbackAlgorithm = new RenderScriptBlur(context); + } + fallbackAlgorithm.blur(bitmap, lastBlurRadius); + fallbackAlgorithm.render(canvas, bitmap); + } + } + + void setContext(@NonNull Context context) + { + this.context = context; + } +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/RenderScriptBlur.java b/android/titanium/src/java/eightbitlab/com/blurview/RenderScriptBlur.java new file mode 100644 index 00000000000..e1db404cc6b --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/RenderScriptBlur.java @@ -0,0 +1,116 @@ +package eightbitlab.com.blurview; + +import static eightbitlab.com.blurview.BlurController.DEFAULT_SCALE_FACTOR; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.Build; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicBlur; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +/** + * Blur using RenderScript, processed on GPU when device drivers support it. + * Requires API 17+ + * + * @deprecated because RenderScript is deprecated and its hardware acceleration is not guaranteed. + * RenderEffectBlur is the best alternative at the moment. + */ +@Deprecated +public class RenderScriptBlur implements BlurAlgorithm +{ + private final Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG); + private final RenderScript renderScript; + private final ScriptIntrinsicBlur blurScript; + private Allocation outAllocation; + + private int lastBitmapWidth = -1; + private int lastBitmapHeight = -1; + + /** + * @param context Context to create the {@link RenderScript} + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + public RenderScriptBlur(@NonNull Context context) + { + renderScript = RenderScript.create(context); + blurScript = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript)); + } + + private boolean canReuseAllocation(@NonNull Bitmap bitmap) + { + return bitmap.getHeight() == lastBitmapHeight && bitmap.getWidth() == lastBitmapWidth; + } + + /** + * @param bitmap bitmap to blur + * @param blurRadius blur radius (1..25) + * @return blurred bitmap + */ + @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1) + @Override + public Bitmap blur(@NonNull Bitmap bitmap, float blurRadius) + { + //Allocation will use the same backing array of pixels as bitmap if created with USAGE_SHARED flag + Allocation inAllocation = Allocation.createFromBitmap(renderScript, bitmap); + + if (!canReuseAllocation(bitmap)) { + if (outAllocation != null) { + outAllocation.destroy(); + } + outAllocation = Allocation.createTyped(renderScript, inAllocation.getType()); + lastBitmapWidth = bitmap.getWidth(); + lastBitmapHeight = bitmap.getHeight(); + } + + blurScript.setRadius(blurRadius); + blurScript.setInput(inAllocation); + //do not use inAllocation in forEach. it will cause visual artifacts on blurred Bitmap + blurScript.forEach(outAllocation); + outAllocation.copyTo(bitmap); + + inAllocation.destroy(); + return bitmap; + } + + @Override + public final void destroy() + { + blurScript.destroy(); + renderScript.destroy(); + if (outAllocation != null) { + outAllocation.destroy(); + } + } + + @Override + public boolean canModifyBitmap() + { + return true; + } + + @NonNull + @Override + public Bitmap.Config getSupportedBitmapConfig() + { + return Bitmap.Config.ARGB_8888; + } + + @Override + public float scaleFactor() + { + return DEFAULT_SCALE_FACTOR; + } + + @Override + public void render(@NonNull Canvas canvas, @NonNull Bitmap bitmap) + { + canvas.drawBitmap(bitmap, 0f, 0f, paint); + } +} diff --git a/android/titanium/src/java/eightbitlab/com/blurview/SizeScaler.java b/android/titanium/src/java/eightbitlab/com/blurview/SizeScaler.java new file mode 100644 index 00000000000..7949f6d4f27 --- /dev/null +++ b/android/titanium/src/java/eightbitlab/com/blurview/SizeScaler.java @@ -0,0 +1,97 @@ +package eightbitlab.com.blurview; + +/** + * Scales width and height by [scaleFactor], + * and then rounds the size proportionally so the width is divisible by [ROUNDING_VALUE] + */ +public class SizeScaler +{ + + // Bitmap size should be divisible by ROUNDING_VALUE to meet stride requirement. + // This will help avoiding an extra bitmap allocation when passing the bitmap to RenderScript for blur. + // Usually it's 16, but on Samsung devices it's 64 for some reason. + private static final int ROUNDING_VALUE = 64; + private final float scaleFactor; + + public SizeScaler(float scaleFactor) + { + this.scaleFactor = scaleFactor; + } + + Size scale(int width, int height) + { + int nonRoundedScaledWidth = downscaleSize(width); + int scaledWidth = roundSize(nonRoundedScaledWidth); + //Only width has to be aligned to ROUNDING_VALUE + float roundingScaleFactor = (float) width / scaledWidth; + //Ceiling because rounding or flooring might leave empty space on the View's bottom + int scaledHeight = (int) Math.ceil(height / roundingScaleFactor); + + return new Size(scaledWidth, scaledHeight, roundingScaleFactor); + } + + boolean isZeroSized(int measuredWidth, int measuredHeight) + { + return downscaleSize(measuredHeight) == 0 || downscaleSize(measuredWidth) == 0; + } + + /** + * Rounds a value to the nearest divisible by {@link #ROUNDING_VALUE} to meet stride requirement + */ + private int roundSize(int value) + { + if (value % ROUNDING_VALUE == 0) { + return value; + } + return value - (value % ROUNDING_VALUE) + ROUNDING_VALUE; + } + + private int downscaleSize(float value) + { + return (int) Math.ceil(value / scaleFactor); + } + + static class Size + { + + final int width; + final int height; + // TODO this is probably not needed anymore + final float scaleFactor; + + Size(int width, int height, float scaleFactor) + { + this.width = width; + this.height = height; + this.scaleFactor = scaleFactor; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Size size = (Size) o; + + if (width != size.width) return false; + if (height != size.height) return false; + return Float.compare(size.scaleFactor, scaleFactor) == 0; + } + + @Override + public int hashCode() + { + int result = width; + result = 31 * result + height; + result = 31 * result + (scaleFactor != +0.0f ? Float.floatToIntBits(scaleFactor) : 0); + return result; + } + + @Override + public String toString() + { + return "Size{ width=" + width + ", height=" + height + ", scaleFactor=" + scaleFactor + '}'; + } + } +} diff --git a/apidoc/Titanium/UI/BlurView.yml b/apidoc/Titanium/UI/BlurView.yml new file mode 100644 index 00000000000..c7b8f8c8d03 --- /dev/null +++ b/apidoc/Titanium/UI/BlurView.yml @@ -0,0 +1,177 @@ +--- +name: Titanium.UI.BlurView +summary: | + A blur view providing a background blur effect similar to iOS' UIVisualEffectView. + + The blur is applied to content behind the blur view within the window. Place it above + other views to blur anything visible underneath within its bounds. +extends: Titanium.UI.View +platforms: [android, iphone, ipad, macos] +since: { android: "13.0.0" } +properties: + + - name: effect + summary: The blur effect to apply to the effect view. + description: | + On iOS, you can also use the Titanium.UI.iOS.BLUR_EFFECT_STYLE_* effects + that are platform-specific. + On Android it will add a default blurRadius of 16px. If you want to change the value you have to use `blurRadius` and `overlayColor` instead of the `effect` preset. + type: Number + constants: Titanium.UI.BLUR_EFFECT_STYLE_* + default: undefined (no preset; uses current `blurRadius`/`overlayColor`) on Android + + - name: blurRadius + summary: Blur radius intensity in pixels. + type: Number + default: 16 + platforms: [android] + + - name: overlayColor + summary: Color overlay/tint drawn over the blur. + description: Hex string, color name, or rgba() string. + type: String + default: transparent + platforms: [android] + + - name: glassEffect + summary: The glass effect configuration to apply to the effect view. + description: | + Glass effects are an alternative to classic blur views introduced in iOS 26, providing + a more modern and visually appealing blur effect with additional customization options. + + When the glass effect is enabled, it will override any value set to the `effect` property. + See for the available configuration options. + type: GlassEffectConfiguration + platforms: [iphone, ipad, macos] + osver: {ios: {min: "26.0"}} + +examples: + - title: Basic Blur View + example: | + ``` js + const win = Ti.UI.createWindow({ backgroundColor: '#fff' }); + + // Content behind the blur + const bg = Ti.UI.createImageView({ + image: '/default_app_logo.png' + }); + + // Blur view placed above content, blurs everything behind within its bounds + const blur = Ti.UI.createBlurView({ + width: 200, + height: 200, + effect: Ti.UI.BLUR_EFFECT_STYLE_LIGHT + }); + + win.add([bg, blur]); + win.open(); + ``` + + - title: Full Featured Blur View (different styles) + example: | + The following example shows how to create a simple blur view: + + ``` js + const win = Ti.UI.createWindow({ backgroundColor: '#fff' }); + const target = Ti.UI.createView({ width: Ti.UI.FILL, height: Ti.UI.FILL }); + + target.add(Ti.UI.createImageView({ + image: '/DefaultIcon.png' + })); + + const effects = [ + Ti.UI.BLUR_EFFECT_STYLE_EXTRA_LIGHT, + Ti.UI.BLUR_EFFECT_STYLE_LIGHT, + Ti.UI.BLUR_EFFECT_STYLE_DARK, + ]; + + const blur = Ti.UI.createBlurView({ + width: 200, + height: 200, + effect: effects[0] + }); + + // Create circular animation to live-test the blur effect + const density = Ti.Platform.osname === 'android' ? Ti.Platform.displayCaps.logicalDensityFactor : 1; + const centerX = Ti.Platform.displayCaps.platformWidth / density / 2; + const centerY = Ti.Platform.displayCaps.platformHeight / density / 2; + + const radius = 100; + let angle = 0; + + const animateCircle = () => { + angle += 0.1; + const x = centerX + Math.cos(angle) * radius - 100; // subtract half width + const y = centerY + Math.sin(angle) * radius - 100; // subtract half height + + blur.animate({ + left: x, + top: y, + duration: 50 + }, () => { + animateCircle(); + }); + }; + + animateCircle(); + + const tabs = Ti.UI.createTabbedBar({ + labels: ['Extra light', 'Light', 'Dark'], + bottom: 100 + }); + + tabs.addEventListener('click', ({ index }) => { + blur.effect = effects[index]; + }); + + win.add([target, blur, tabs]); + win.open(); + ``` + + - title: Glass Effect View (iOS 26+) + example: | + The following example shows how to create a blur view with the new glass effect: + + ``` js + const win = Ti.UI.createWindow({ + backgroundColor: "#fff" + }); + + const backgroundView = Ti.UI.createImageView({ + image: 'https://s1.directupload.eu/images/250817/ufgix5hy.jpg', + width: Ti.UI.FILL, + height: Ti.UI.FILL, + scalingMode: Ti.Media.IMAGE_SCALING_ASPECT_FILL + }); + + const effectView = Ti.UI.createBlurView({ + width: 250, + height: 250, + glassEffect: { + style: Ti.UI.iOS.GLASS_EFFECT_STYLE_CLEAR, + interactive: true + } + }); + + backgroundView.add(effectView); + + const toggleButton = Ti.UI.createButton({ + title: "Toggle Glass Effect", + bottom: 50 + }); + + let isEnabled = true; + toggleButton.addEventListener("click", () => { + isEnabled = !isEnabled; + if (isEnabled) { + backgroundView.add(effectView) + } else { + backgroundView.remove(effectView) + } + }); + + win.add(backgroundView); + win.add(toggleButton); + win.open(); + ``` + diff --git a/apidoc/Titanium/UI/UI.yml b/apidoc/Titanium/UI/UI.yml index c65fc46c6e8..8143cffab56 100644 --- a/apidoc/Titanium/UI/UI.yml +++ b/apidoc/Titanium/UI/UI.yml @@ -2667,6 +2667,31 @@ properties: permission: read-only since: "2.0.0" + - name: BLUR_EFFECT_STYLE_EXTRA_LIGHT + summary: Use with [BlurView.effect](Titanium.UI.BlurView.effect) to specify a blur effect. + description: | + Creates a blurring effect in the view. The area of the view is lighter in hue than the underlying view. + type: Number + permission: read-only + since: "13.0.0" + + - name: BLUR_EFFECT_STYLE_LIGHT + summary: Use with [BlurView.effect](Titanium.UI.BlurView.effect) to specify a blur effect. + description: | + Creates a blurring effect in the view. The area of the view is the same approximate hue + of the underlying view. + type: Number + permission: read-only + since: "13.0.0" + + - name: BLUR_EFFECT_STYLE_DARK + summary: Use with [BlurView.effect](Titanium.UI.BlurView.effect) to specify a blur effect. + description: | + Creates a blurring effect in the view. The area of the view is darker in hue than the underlying view. + type: Number + permission: read-only + since: "13.0.0" + - name: UNKNOWN summary: Orientation constant representing an unknown orientation. description: | diff --git a/apidoc/Titanium/UI/iOS/BlurView.yml b/apidoc/Titanium/UI/iOS/BlurView.yml index 36e8b09a849..dd1627fc62e 100644 --- a/apidoc/Titanium/UI/iOS/BlurView.yml +++ b/apidoc/Titanium/UI/iOS/BlurView.yml @@ -11,6 +11,11 @@ summary: | extends: Titanium.UI.View platforms: [iphone, ipad, macos] since: {iphone: "5.4.0", ipad: "5.4.0", macos: "9.2.0"} +deprecated: + since: "13.0.0" + removed: "14.0.0" + notes: Use the API instead - it has the exact same API for iOS and also supports Android! + properties: - name: effect summary: The blur effect to apply to the effect view. diff --git a/apidoc/Titanium/UI/iOS/iOS.yml b/apidoc/Titanium/UI/iOS/iOS.yml index 23da962be6f..93bb8047984 100644 --- a/apidoc/Titanium/UI/iOS/iOS.yml +++ b/apidoc/Titanium/UI/iOS/iOS.yml @@ -161,6 +161,10 @@ properties: type: Number permission: read-only since: "5.4.0" + deprecated: + since: "13.0.0" + removed: "14.0.0" + notes: Use instead. - name: BLUR_EFFECT_STYLE_LIGHT summary: | @@ -171,6 +175,10 @@ properties: type: Number permission: read-only since: "5.4.0" + deprecated: + since: "13.0.0" + removed: "14.0.0" + notes: Use instead. - name: BLUR_EFFECT_STYLE_DARK summary: | @@ -180,6 +188,10 @@ properties: type: Number permission: read-only since: "5.4.0" + deprecated: + since: "13.0.0" + removed: "14.0.0" + notes: Use instead. - name: BLUR_EFFECT_STYLE_REGULAR summary: | diff --git a/iphone/Classes/TiUIiOSBlurView.h b/iphone/Classes/TiUIBlurView.h similarity index 85% rename from iphone/Classes/TiUIiOSBlurView.h rename to iphone/Classes/TiUIBlurView.h index 39b9c04305d..c3db829f32b 100644 --- a/iphone/Classes/TiUIiOSBlurView.h +++ b/iphone/Classes/TiUIBlurView.h @@ -4,10 +4,10 @@ * Licensed under the terms of the Apache Public License * Please see the LICENSE included with this distribution for details. */ -#ifdef USE_TI_UIIOSBLURVIEW +#ifdef USE_TI_UIBLURVIEW #import -@interface TiUIiOSBlurView : TiUIView { +@interface TiUIBlurView : TiUIView { UIVisualEffectView *blurView; TiDimension width; diff --git a/iphone/Classes/TiUIiOSBlurView.m b/iphone/Classes/TiUIBlurView.m similarity index 97% rename from iphone/Classes/TiUIiOSBlurView.m rename to iphone/Classes/TiUIBlurView.m index 13d13d29339..7deff6b962a 100644 --- a/iphone/Classes/TiUIiOSBlurView.m +++ b/iphone/Classes/TiUIBlurView.m @@ -5,11 +5,11 @@ * Please see the LICENSE included with this distribution for details. */ -#ifdef USE_TI_UIIOSBLURVIEW -#import "TiUIiOSBlurView.h" -#import "TiUIiOSBlurViewProxy.h" +#ifdef USE_TI_UIBLURVIEW +#import "TiUIBlurView.h" +#import "TiUIBlurViewProxy.h" -@implementation TiUIiOSBlurView +@implementation TiUIBlurView - (UIVisualEffectView *)blurView { diff --git a/iphone/Classes/TiUIiOSBlurViewProxy.h b/iphone/Classes/TiUIBlurViewProxy.h similarity index 78% rename from iphone/Classes/TiUIiOSBlurViewProxy.h rename to iphone/Classes/TiUIBlurViewProxy.h index 095959db2d1..b119c98e32d 100644 --- a/iphone/Classes/TiUIiOSBlurViewProxy.h +++ b/iphone/Classes/TiUIBlurViewProxy.h @@ -4,10 +4,10 @@ * Licensed under the terms of the Apache Public License * Please see the LICENSE included with this distribution for details. */ -#ifdef USE_TI_UIIOSBLURVIEW +#ifdef USE_TI_UIBLURVIEW #import -@interface TiUIiOSBlurViewProxy : TiViewProxy { +@interface TiUIBlurViewProxy : TiViewProxy { } @end diff --git a/iphone/Classes/TiUIiOSBlurViewProxy.m b/iphone/Classes/TiUIBlurViewProxy.m similarity index 63% rename from iphone/Classes/TiUIiOSBlurViewProxy.m rename to iphone/Classes/TiUIBlurViewProxy.m index 95e3d53e019..e531462e28e 100644 --- a/iphone/Classes/TiUIiOSBlurViewProxy.m +++ b/iphone/Classes/TiUIBlurViewProxy.m @@ -5,18 +5,18 @@ * Please see the LICENSE included with this distribution for details. */ -#ifdef USE_TI_UIIOSBLURVIEW -#import "TiUIiOSBlurViewProxy.h" -#import "TiUIiOSBlurView.h" +#ifdef USE_TI_UIBLURVIEW +#import "TiUIBlurViewProxy.h" +#import "TiUIBlurView.h" #import -@implementation TiUIiOSBlurViewProxy +@implementation TiUIBlurViewProxy #pragma mark Proxy lifecycle - (NSString *)apiName { - return @"Ti.UI.iOS.BlurView"; + return @"Ti.UI.BlurView"; } - (void)dealloc @@ -26,9 +26,9 @@ - (void)dealloc #pragma mark Public APIs -- (TiUIiOSBlurView *)blurView +- (TiUIBlurView *)blurView { - return (TiUIiOSBlurView *)self.view; + return (TiUIBlurView *)self.view; } @end diff --git a/iphone/Classes/TiUIiOSProxy.h b/iphone/Classes/TiUIiOSProxy.h index b238e1762b5..3dee633b3c6 100644 --- a/iphone/Classes/TiUIiOSProxy.h +++ b/iphone/Classes/TiUIiOSProxy.h @@ -156,12 +156,6 @@ @property (nonatomic, readonly) NSNumber *MODAL_TRANSITION_STYLE_PARTIAL_CURL; -#ifdef USE_TI_UIIOSBLURVIEW -@property (nonatomic, readonly) NSNumber *BLUR_EFFECT_STYLE_EXTRA_LIGHT; -@property (nonatomic, readonly) NSNumber *BLUR_EFFECT_STYLE_LIGHT; -@property (nonatomic, readonly) NSNumber *BLUR_EFFECT_STYLE_DARK; -#endif - @property (nonatomic, readonly) NSNumber *LARGE_TITLE_DISPLAY_MODE_AUTOMATIC; @property (nonatomic, readonly) NSNumber *LARGE_TITLE_DISPLAY_MODE_ALWAYS; @property (nonatomic, readonly) NSNumber *LARGE_TITLE_DISPLAY_MODE_NEVER; @@ -220,7 +214,7 @@ #ifdef USE_TI_UIIOSMENUPOPUP - (id)createMenuPopup:(id)args; #endif -#ifdef USE_TI_UIIOSBLURVIEW +#ifdef USE_TI_UIBLURVIEW - (id)createBlurView:(id)args; #endif #ifdef USE_TI_UIIOSAPPLICATIONSHORTCUTS diff --git a/iphone/Classes/TiUIiOSProxy.m b/iphone/Classes/TiUIiOSProxy.m index d6fa78c6bd7..f48837aae2b 100644 --- a/iphone/Classes/TiUIiOSProxy.m +++ b/iphone/Classes/TiUIiOSProxy.m @@ -76,8 +76,8 @@ #import #endif -#ifdef USE_TI_UIIOSBLURVIEW -#import "TiUIiOSBlurViewProxy.h" +#ifdef USE_TI_UIBLURVIEW +#import "TiUIBlurViewProxy.h" #endif #ifdef USE_TI_UIIOSSTEPPER @@ -467,9 +467,9 @@ - (void)setAppSupportsShakeToEdit:(NSNumber *)shake END_UI_THREAD_PROTECTED_VALUE(appSupportsShakeToEdit) #ifdef USE_TI_UIIOSBLURVIEW -MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_EXTRA_LIGHT, UIBlurEffectStyleExtraLight); -MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_LIGHT, UIBlurEffectStyleLight); -MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_DARK, UIBlurEffectStyleDark); +MAKE_SYSTEM_PROP_DEPRECATED_REPLACED(BLUR_EFFECT_STYLE_EXTRA_LIGHT, UIBlurEffectStyleExtraLight, @"UI.iOS.BLUR_EFFECT_STYLE_EXTRA_LIGHT", @"13.0.0", @"UI.BLUR_EFFECT_STYLE_EXTRA_LIGHT"); +MAKE_SYSTEM_PROP_DEPRECATED_REPLACED(BLUR_EFFECT_STYLE_LIGHT, UIBlurEffectStyleLight, @"UI.iOS.BLUR_EFFECT_STYLE_LIGHT", @"13.0.0", @"UI.BLUR_EFFECT_STYLE_LIGHT"); +MAKE_SYSTEM_PROP_DEPRECATED_REPLACED(BLUR_EFFECT_STYLE_DARK, UIBlurEffectStyleDark, @"UI.iOS.BLUR_EFFECT_STYLE_DARK", @"13.0.0", @"UI.BLUR_EFFECT_STYLE_DARK"); MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_REGULAR, UIBlurEffectStyleRegular); MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_PROMINENT, UIBlurEffectStyleProminent); MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_SYSTEM_ULTRA_THIN_MATERIAL, UIBlurEffectStyleSystemUltraThinMaterial); @@ -578,10 +578,12 @@ - (id)createMenuPopup:(id)args } #endif -#ifdef USE_TI_UIIOSBLURVIEW +#ifdef USE_TI_UIBLURVIEW - (id)createBlurView:(id)args { - return [[[TiUIiOSBlurViewProxy alloc] _initWithPageContext:[self executionContext] args:args] autorelease]; + DEPRECATED_REPLACED(@"UI.iOS.BlurView", @"13.0.0", @"UI.BlurView (now cross platform!)"); + + return [[[TiUIBlurViewProxy alloc] _initWithPageContext:[self executionContext] args:args] autorelease]; } #endif diff --git a/iphone/Classes/UIModule.m b/iphone/Classes/UIModule.m index 41d1c8ed3ee..9957e523d38 100644 --- a/iphone/Classes/UIModule.m +++ b/iphone/Classes/UIModule.m @@ -276,6 +276,12 @@ - (TiUIActivityIndicatorStyleProxy *)ActivityIndicatorStyle MAKE_SYSTEM_PROP(LIST_ACCESSORY_TYPE_DETAIL, UITableViewCellAccessoryDetailDisclosureButton); MAKE_SYSTEM_PROP(LIST_ACCESSORY_TYPE_DISCLOSURE, UITableViewCellAccessoryDisclosureIndicator); +#ifdef USE_TI_UIBLURVIEW +MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_EXTRA_LIGHT, UIBlurEffectStyleExtraLight); +MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_LIGHT, UIBlurEffectStyleLight); +MAKE_SYSTEM_PROP(BLUR_EFFECT_STYLE_DARK, UIBlurEffectStyleDark); +#endif + - (void)setBackgroundColor:(id)color { TiRootViewController *controller = [[TiApp app] controller]; diff --git a/iphone/iphone/Titanium.xcodeproj/project.pbxproj b/iphone/iphone/Titanium.xcodeproj/project.pbxproj index df66078f38d..b327f388c0b 100644 --- a/iphone/iphone/Titanium.xcodeproj/project.pbxproj +++ b/iphone/iphone/Titanium.xcodeproj/project.pbxproj @@ -166,8 +166,8 @@ 3A527EB327E0F77700A470D6 /* TiUITableViewScrollPositionProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A527EB227E0F77700A470D6 /* TiUITableViewScrollPositionProxy.m */; }; 3A5AD7261BB9A6E4005B408B /* TiUIiOSPreviewActionGroupProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A5AD7251BB9A6E4005B408B /* TiUIiOSPreviewActionGroupProxy.m */; }; 3A5AD7291BB9BEA8005B408B /* TiUIiOSPreviewContextProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A5AD7281BB9BEA8005B408B /* TiUIiOSPreviewContextProxy.m */; }; - 3A811CD01C2C21E50023468C /* TiUIiOSBlurViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A811CCF1C2C21E50023468C /* TiUIiOSBlurViewProxy.m */; }; - 3A811CD31C2C21FE0023468C /* TiUIiOSBlurView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A811CD21C2C21FE0023468C /* TiUIiOSBlurView.m */; }; + 3A811CD01C2C21E50023468C /* TiUIBlurViewProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A811CCF1C2C21E50023468C /* TiUIBlurViewProxy.m */; }; + 3A811CD31C2C21FE0023468C /* TiUIBlurView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A811CD21C2C21FE0023468C /* TiUIBlurView.m */; }; 3AA4EC262320352B00703A20 /* TiUIListView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AA4EC1D2320352B00703A20 /* TiUIListView.m */; }; 3AA4EC272320352B00703A20 /* TiUIListItemProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AA4EC212320352B00703A20 /* TiUIListItemProxy.m */; }; 3AA4EC282320352B00703A20 /* TiUIListSectionProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AA4EC232320352B00703A20 /* TiUIListSectionProxy.m */; }; @@ -620,10 +620,10 @@ 3A5AD7251BB9A6E4005B408B /* TiUIiOSPreviewActionGroupProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIiOSPreviewActionGroupProxy.m; sourceTree = ""; }; 3A5AD7271BB9BEA8005B408B /* TiUIiOSPreviewContextProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIiOSPreviewContextProxy.h; sourceTree = ""; }; 3A5AD7281BB9BEA8005B408B /* TiUIiOSPreviewContextProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIiOSPreviewContextProxy.m; sourceTree = ""; }; - 3A811CCE1C2C21E50023468C /* TiUIiOSBlurViewProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIiOSBlurViewProxy.h; sourceTree = ""; }; - 3A811CCF1C2C21E50023468C /* TiUIiOSBlurViewProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIiOSBlurViewProxy.m; sourceTree = ""; }; - 3A811CD11C2C21FE0023468C /* TiUIiOSBlurView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIiOSBlurView.h; sourceTree = ""; }; - 3A811CD21C2C21FE0023468C /* TiUIiOSBlurView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIiOSBlurView.m; sourceTree = ""; }; + 3A811CCE1C2C21E50023468C /* TiUIBlurViewProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIBlurViewProxy.h; sourceTree = ""; }; + 3A811CCF1C2C21E50023468C /* TiUIBlurViewProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIBlurViewProxy.m; sourceTree = ""; }; + 3A811CD11C2C21FE0023468C /* TiUIBlurView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIBlurView.h; sourceTree = ""; }; + 3A811CD21C2C21FE0023468C /* TiUIBlurView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIBlurView.m; sourceTree = ""; }; 3AA4EC1C2320352B00703A20 /* TiUIListView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIListView.h; sourceTree = ""; }; 3AA4EC1D2320352B00703A20 /* TiUIListView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TiUIListView.m; sourceTree = ""; }; 3AA4EC1E2320352B00703A20 /* TiUIListItemProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TiUIListItemProxy.h; sourceTree = ""; }; @@ -1085,6 +1085,7 @@ isa = PBXGroup; children = ( DBF4B138200FD90600777136 /* Application Shortcuts */, + 3A811CCD1C2C21CA0023468C /* Blur View */, DB4E9B7020B2E9510064ADF9 /* Navigation Window */, DB0D54921F3B491300E2B771 /* Toolbar */, BBDD81351A2C71E5003CDA10 /* AttributedString */, @@ -1460,7 +1461,6 @@ isa = PBXGroup; children = ( 0BF933871CA0929A0091C7EC /* Stepper */, - 3A811CCD1C2C21CA0023468C /* BlurView */, 3A0B56721C0CD7FE00709DA4 /* LivePhotoView */, 3A0E54341BE81111003EE654 /* MenuPopup */, CAC225581BB79659008A332B /* ApplicationShortcuts */, @@ -1719,15 +1719,15 @@ name = MenuPopup; sourceTree = ""; }; - 3A811CCD1C2C21CA0023468C /* BlurView */ = { + 3A811CCD1C2C21CA0023468C /* Blur View */ = { isa = PBXGroup; children = ( - 3A811CCE1C2C21E50023468C /* TiUIiOSBlurViewProxy.h */, - 3A811CCF1C2C21E50023468C /* TiUIiOSBlurViewProxy.m */, - 3A811CD11C2C21FE0023468C /* TiUIiOSBlurView.h */, - 3A811CD21C2C21FE0023468C /* TiUIiOSBlurView.m */, + 3A811CCE1C2C21E50023468C /* TiUIBlurViewProxy.h */, + 3A811CCF1C2C21E50023468C /* TiUIBlurViewProxy.m */, + 3A811CD11C2C21FE0023468C /* TiUIBlurView.h */, + 3A811CD21C2C21FE0023468C /* TiUIBlurView.m */, ); - name = BlurView; + name = "Blur View"; sourceTree = ""; }; 3AA4EC1B232034FE00703A20 /* ListView */ = { @@ -2167,7 +2167,7 @@ 24CA8B7C111161FE0084E2DE /* TiUITextArea.m in Sources */, 3A5AD7261BB9A6E4005B408B /* TiUIiOSPreviewActionGroupProxy.m in Sources */, 24CA8B7D111161FE0084E2DE /* TiUITabProxy.m in Sources */, - 3A811CD01C2C21E50023468C /* TiUIiOSBlurViewProxy.m in Sources */, + 3A811CD01C2C21E50023468C /* TiUIBlurViewProxy.m in Sources */, 3A3BBAF51D3E2F0F008450DF /* TiAppiOSUserNotificationCenterProxy.m in Sources */, CA0D39E51B7F55C6009D534C /* TiAppiOSSearchableItemAttributeSetProxy.m in Sources */, 24CA8B80111161FE0084E2DE /* TiUITableViewProxy.m in Sources */, @@ -2289,7 +2289,7 @@ 24E50E1C1160666300AF54AF /* LauncherView.m in Sources */, 24E50E27116066A800AF54AF /* TiUIDashboardViewProxy.m in Sources */, 24E50E30116066B400AF54AF /* TiUIDashboardView.m in Sources */, - 3A811CD31C2C21FE0023468C /* TiUIiOSBlurView.m in Sources */, + 3A811CD31C2C21FE0023468C /* TiUIBlurView.m in Sources */, 3A0858432915C3C5002D5E3A /* TiActivityAttributes.swift in Sources */, 24E50F911160792D00AF54AF /* TiUIDashboardItemProxy.m in Sources */, 24596694118E70D300519F79 /* ApplicationRouting.m in Sources */, diff --git a/iphone/iphone/project.xcconfig b/iphone/iphone/project.xcconfig index 8df768982a5..2b4c9f65e82 100644 --- a/iphone/iphone/project.xcconfig +++ b/iphone/iphone/project.xcconfig @@ -1,5 +1,5 @@ TI_VERSION=0.0.0 JSCORE_LD_FLAGS=-weak_framework JavaScriptCore GCC_DEFINITIONS= -TI_SYMBOL_MACROS=USE_JSCORE_FRAMEWORK USE_TI_STREAM USE_TI_CODEC USE_TI_UTILS USE_TI_XML USE_TI_ACCELEROMETER USE_TI_API USE_TI_APP USE_TI_APPTRACKUSERINTERACTION USE_TI_CALENDAR USE_TI_CONTACTS USE_TI_DATABASE USE_TI_FILESYSTEM USE_TI_GEOLOCATION USE_TI_GESTURE USE_TI_MEDIA USE_TI_NETWORK USE_TI_NETWORKSOCKET USE_TI_PLATFORM USE_TI_PLATFORMIDENTIFIERFORADVERTISING USE_TI_PLATFORMGETIDENTIFIERFORADVERTISING USE_TI_WATCHSESSION USE_TI_UI USE_TI_UITAB USE_TI_UILABEL USE_TI_UIBUTTON USE_TI_UIPROGRESSBAR USE_TI_UISEARCHBAR USE_TI_UIACTIVITYINDICATOR USE_TI_UIOPTIONBAR USE_TI_UISLIDER USE_TI_UISWITCH USE_TI_UIPICKER USE_TI_UITEXTAREA USE_TI_UITEXTFIELD USE_TI_UIIMAGEVIEW USE_TI_UIMASKEDIMAGE USE_TI_UIWEBVIEW USE_TI_UIWINDOW USE_TI_UIVIEW USE_TI_UIOPTIONDIALOG USE_TI_UIEMAILDIALOG USE_TI_UIDASHBOARDVIEW USE_TI_UISCROLLVIEW USE_TI_UISCROLLABLEVIEW USE_TI_UITABLEVIEW USE_TI_UILISTVIEW USE_TI_UIANIMATION USE_TI_UIATTRIBUTEDSTRING USE_TI_UIACTIVITYINDICATORSTYLE USE_TI_UITOOLBAR USE_TI_UITABBEDBAR USE_TI_UIAPPLICATIONSHORTCUTS USE_TI_UINAVIGATIONWINDOW USE_TI_UICLIPBOARD USE_TI_UIIPAD USE_TI_UIIPADPOPOVER USE_TI_UIIPADSPLITWINDOW USE_TI_UIIPADSPLITWINDOWBUTTON USE_TI_UIIOS USE_TI_UIIOSADVIEW USE_TI_UIIOSCOVERFLOWVIEW USE_TI_UIIOSTOOLBAR USE_TI_UIIOSTABBEDBAR USE_TI_UIIOSDOCUMENTVIEWER USE_TI_UIIOSNAVIGATIONWINDOW USE_TI_UIIOSSPLITWINDOW USE_TI_UIIOSPREVIEWCONTEXT USE_TI_UIIOSMENUPOPUP USE_TI_UIIOSLIVEPHOTOVIEW USE_TI_UIIOSLIVEPHOTOBADGE USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_OVER_CONTENT USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_LIVE_OFF USE_TI_UIIOSALERTDIALOGSTYLE USE_TI_UIIOSANIMATIONSTYLE USE_TI_UIIOSLISTVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWSCROLLPOSITION USE_TI_UIIOSLISTVIEWSCROLLPOSITION USE_TI_UIIOSTABLEVIEWSTYLE USE_TI_UIIOSLISTVIEWSTYLE USE_TI_UIIOSPROGRESSBARSTYLE USE_TI_UIIOSROWANIMATIONSTYLE USE_TI_UIIOSSCROLLINDICATORSTYLE USE_TI_UIIOSSTATUSBAR USE_TI_UIIOSBUTTONCONFIGURATION USE_TI_UIIOSSYSTEMBUTTON USE_TI_UIIOSSYSTEMICON USE_TI_UIIOSFEEDBACKGENERATOR USE_TI_UIIOSSTEPPER USE_TI_APPIOS USE_TI_APPIOSSEARCHABLEINDEX USE_TI_APPIOSSEARCHABLEITEM USE_TI_APPIOSSEARCHABLEITEMATTRIBUTESET USE_TI_APPIOSSEARCHQUERY USE_TI_APPIOSUSERACTIVITY USE_TI_APPIOSUSERNOTIFICATIONCENTER USE_TI_UIIOSANIMATOR USE_TI_UIIOSSNAPBEHAVIOR USE_TI_UIIOSPUSHBEHAVIOR USE_TI_UIIOSGRAVITYBEHAVIOR USE_TI_UIIOSANCHORATTACHMENTBEHAVIOR USE_TI_UIIOSVIEWATTACHMENTBEHAVIOR USE_TI_UIIOSCOLLISIONBEHAVIOR USE_TI_UIIOSDYNAMICITEMBEHAVIOR USE_TI_UIIOSTRANSITIONANIMATION USE_TI_UIREFRESHCONTROL USE_TI_UIIOSAPPLICATIONSHORTCUTS USE_TI_UISHORTCUT USE_TI_UISHORTCUTITEM USE_TI_UIIOSBLURVIEW USE_TI_NETWORKREGISTERFORPUSHNOTIFICATIONS USE_TI_SILENTPUSH USE_TI_FETCH USE_TI_MEDIASHOWCAMERA USE_TI_MEDIAHIDECAMERA USE_TI_MEDIAOPENPHOTOGALLERY USE_TI_MEDIATAKEPICTURE USE_TI_MEDIASTARTVIDEOCAPTURE USE_TI_MEDIASTOPVIDEOCAPTURE USE_TI_MEDIASWITCHCAMERA USE_TI_MEDIAREQUESTCAMERAPERMISSIONS USE_TI_MEDIAHASCAMERAPERMISSIONS USE_TI_MEDIAHASPHOTOGALLERYPERMISSIONS USE_TI_MEDIAREQUESTPHOTOGALLERYPERMISSIONS USE_TI_MEDIAOPENMUSICLIBRARY USE_TI_MEDIAHIDEMUSICLIBRARY USE_TI_MEDIAQUERYMUSICLIBRARY USE_TI_MEDIAREQUESTAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIOPERMISSIONS USE_TI_MEDIAHASMUSICLIBRARYPERMISSIONS USE_TI_MEDIAREQUESTMUSICLIBRARYPERMISSIONS USE_TI_MEDIACANRECORD USE_TI_MEDIAISCAMERASUPPORTED USE_TI_MEDIAISMEDIATYPESUPPORTED USE_TI_MEDIASAVETOPHOTOGALLERY USE_TI_MEDIASTARTVIDEOEDITING USE_TI_MEDIASTOPVIDEOEDITING USE_TI_MEDIAAUDIOPLAYER USE_TI_MEDIAAUDIORECORDER USE_TI_MEDIAMUSICPLAYER USE_TI_MEDIASYSTEMMUSICPLAYER USE_TI_MEDIASYSTEMALERT USE_TI_MEDIAGETSYSTEMMUSICPLAYER USE_TI_MEDIAAPPMUSICPLAYER USE_TI_MEDIAGETAPPMUSICPLAYER USE_TI_MEDIAVIDEOPLAYER USE_TI_MEDIASOUND USE_TI_MEDIACAMERA_AUTHORIZATION_AUTHORIZED USE_TI_MEDIACAMERA_AUTHORIZATION_DENIED USE_TI_MEDIACAMERA_AUTHORIZATION_RESTRICTED USE_TI_MEDIACAMERA_AUTHORIZATION_UNKNOWN USE_TI_MEDIACAMERA_FRONT USE_TI_MEDIACAMERA_REAR USE_TI_MEDIACAMERA_FLASH_OFF USE_TI_MEDIACAMERA_FLASH_AUTO USE_TI_MEDIACAMERA_FLASH_ON USE_TI_MEDIACAMERAFLASHMODE USE_TI_MEDIAAVAILABLECAMERAMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOGALLERYMEDIATYPES USE_TI_MEDIAAVAILABLECAMERAS USE_TI_MEDIACAMERAAUTHORIZATION USE_TI_MEDIAVOLUME USE_TI_MEDIAAUDIOPLAYING USE_TI_MEDIACURRENTROUTE USE_TI_MEDIAVIBRATE USE_TI_MEDIABEEP USE_TI_MEDIASTARTMICROPHONEMONITOR USE_TI_MEDIASTOPMICROPHONEMONITOR USE_TI_MEDIAPEAKMICROPHONEPOWER USE_TI_MEDIAGETPEAKMICROPHONEPOWER USE_TI_MEDIAAVERAGEMICROPHONEPOWER USE_TI_MEDIAGETAVERAGEMICROPHONEPOWER USE_TI_UITABLEVIEWSCROLLPOSITION USE_TI_UILISTVIEWSCROLLPOSITION USE_TI_PLATFORMUPTIME +TI_SYMBOL_MACROS=USE_JSCORE_FRAMEWORK USE_TI_STREAM USE_TI_CODEC USE_TI_UTILS USE_TI_XML USE_TI_ACCELEROMETER USE_TI_API USE_TI_APP USE_TI_APPTRACKUSERINTERACTION USE_TI_CALENDAR USE_TI_CONTACTS USE_TI_DATABASE USE_TI_FILESYSTEM USE_TI_GEOLOCATION USE_TI_GESTURE USE_TI_MEDIA USE_TI_NETWORK USE_TI_NETWORKSOCKET USE_TI_PLATFORM USE_TI_PLATFORMIDENTIFIERFORADVERTISING USE_TI_PLATFORMGETIDENTIFIERFORADVERTISING USE_TI_WATCHSESSION USE_TI_UI USE_TI_UITAB USE_TI_UILABEL USE_TI_UIBUTTON USE_TI_UIPROGRESSBAR USE_TI_UISEARCHBAR USE_TI_UIACTIVITYINDICATOR USE_TI_UIOPTIONBAR USE_TI_UISLIDER USE_TI_UISWITCH USE_TI_UIPICKER USE_TI_UITEXTAREA USE_TI_UITEXTFIELD USE_TI_UIIMAGEVIEW USE_TI_UIMASKEDIMAGE USE_TI_UIWEBVIEW USE_TI_UIWINDOW USE_TI_UIVIEW USE_TI_UIOPTIONDIALOG USE_TI_UIEMAILDIALOG USE_TI_UIDASHBOARDVIEW USE_TI_UISCROLLVIEW USE_TI_UISCROLLABLEVIEW USE_TI_UITABLEVIEW USE_TI_UILISTVIEW USE_TI_UIANIMATION USE_TI_UIATTRIBUTEDSTRING USE_TI_UIACTIVITYINDICATORSTYLE USE_TI_UITOOLBAR USE_TI_UITABBEDBAR USE_TI_UIAPPLICATIONSHORTCUTS USE_TI_UINAVIGATIONWINDOW USE_TI_UICLIPBOARD USE_TI_UIIPAD USE_TI_UIIPADPOPOVER USE_TI_UIIPADSPLITWINDOW USE_TI_UIIPADSPLITWINDOWBUTTON USE_TI_UIIOS USE_TI_UIIOSADVIEW USE_TI_UIIOSCOVERFLOWVIEW USE_TI_UIIOSTOOLBAR USE_TI_UIIOSTABBEDBAR USE_TI_UIIOSDOCUMENTVIEWER USE_TI_UIIOSNAVIGATIONWINDOW USE_TI_UIIOSSPLITWINDOW USE_TI_UIIOSPREVIEWCONTEXT USE_TI_UIIOSMENUPOPUP USE_TI_UIIOSLIVEPHOTOVIEW USE_TI_UIIOSLIVEPHOTOBADGE USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_OVER_CONTENT USE_TI_UIIOSLIVEPHOTO_BADGE_OPTIONS_LIVE_OFF USE_TI_UIIOSALERTDIALOGSTYLE USE_TI_UIIOSANIMATIONSTYLE USE_TI_UIIOSLISTVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWCELLSELECTIONSTYLE USE_TI_UIIOSTABLEVIEWSCROLLPOSITION USE_TI_UIIOSLISTVIEWSCROLLPOSITION USE_TI_UIIOSTABLEVIEWSTYLE USE_TI_UIIOSLISTVIEWSTYLE USE_TI_UIIOSPROGRESSBARSTYLE USE_TI_UIIOSROWANIMATIONSTYLE USE_TI_UIIOSSCROLLINDICATORSTYLE USE_TI_UIIOSSTATUSBAR USE_TI_UIIOSBUTTONCONFIGURATION USE_TI_UIIOSSYSTEMBUTTON USE_TI_UIIOSSYSTEMICON USE_TI_UIIOSFEEDBACKGENERATOR USE_TI_UIIOSSTEPPER USE_TI_APPIOS USE_TI_APPIOSSEARCHABLEINDEX USE_TI_APPIOSSEARCHABLEITEM USE_TI_APPIOSSEARCHABLEITEMATTRIBUTESET USE_TI_APPIOSSEARCHQUERY USE_TI_APPIOSUSERACTIVITY USE_TI_APPIOSUSERNOTIFICATIONCENTER USE_TI_UIIOSANIMATOR USE_TI_UIIOSSNAPBEHAVIOR USE_TI_UIIOSPUSHBEHAVIOR USE_TI_UIIOSGRAVITYBEHAVIOR USE_TI_UIIOSANCHORATTACHMENTBEHAVIOR USE_TI_UIIOSVIEWATTACHMENTBEHAVIOR USE_TI_UIIOSCOLLISIONBEHAVIOR USE_TI_UIIOSDYNAMICITEMBEHAVIOR USE_TI_UIIOSTRANSITIONANIMATION USE_TI_UIREFRESHCONTROL USE_TI_UIIOSAPPLICATIONSHORTCUTS USE_TI_UISHORTCUT USE_TI_UISHORTCUTITEM USE_TI_UIIOSBLURVIEW USE_TI_UIBLURVIEW USE_TI_NETWORKREGISTERFORPUSHNOTIFICATIONS USE_TI_SILENTPUSH USE_TI_FETCH USE_TI_MEDIASHOWCAMERA USE_TI_MEDIAHIDECAMERA USE_TI_MEDIAOPENPHOTOGALLERY USE_TI_MEDIATAKEPICTURE USE_TI_MEDIASTARTVIDEOCAPTURE USE_TI_MEDIASTOPVIDEOCAPTURE USE_TI_MEDIASWITCHCAMERA USE_TI_MEDIAREQUESTCAMERAPERMISSIONS USE_TI_MEDIAHASCAMERAPERMISSIONS USE_TI_MEDIAHASPHOTOGALLERYPERMISSIONS USE_TI_MEDIAREQUESTPHOTOGALLERYPERMISSIONS USE_TI_MEDIAOPENMUSICLIBRARY USE_TI_MEDIAHIDEMUSICLIBRARY USE_TI_MEDIAQUERYMUSICLIBRARY USE_TI_MEDIAREQUESTAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIORECORDERPERMISSIONS USE_TI_MEDIAHASAUDIOPERMISSIONS USE_TI_MEDIAHASMUSICLIBRARYPERMISSIONS USE_TI_MEDIAREQUESTMUSICLIBRARYPERMISSIONS USE_TI_MEDIACANRECORD USE_TI_MEDIAISCAMERASUPPORTED USE_TI_MEDIAISMEDIATYPESUPPORTED USE_TI_MEDIASAVETOPHOTOGALLERY USE_TI_MEDIASTARTVIDEOEDITING USE_TI_MEDIASTOPVIDEOEDITING USE_TI_MEDIAAUDIOPLAYER USE_TI_MEDIAAUDIORECORDER USE_TI_MEDIAMUSICPLAYER USE_TI_MEDIASYSTEMMUSICPLAYER USE_TI_MEDIASYSTEMALERT USE_TI_MEDIAGETSYSTEMMUSICPLAYER USE_TI_MEDIAAPPMUSICPLAYER USE_TI_MEDIAGETAPPMUSICPLAYER USE_TI_MEDIAVIDEOPLAYER USE_TI_MEDIASOUND USE_TI_MEDIACAMERA_AUTHORIZATION_AUTHORIZED USE_TI_MEDIACAMERA_AUTHORIZATION_DENIED USE_TI_MEDIACAMERA_AUTHORIZATION_RESTRICTED USE_TI_MEDIACAMERA_AUTHORIZATION_UNKNOWN USE_TI_MEDIACAMERA_FRONT USE_TI_MEDIACAMERA_REAR USE_TI_MEDIACAMERA_FLASH_OFF USE_TI_MEDIACAMERA_FLASH_AUTO USE_TI_MEDIACAMERA_FLASH_ON USE_TI_MEDIACAMERAFLASHMODE USE_TI_MEDIAAVAILABLECAMERAMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOMEDIATYPES USE_TI_MEDIAAVAILABLEPHOTOGALLERYMEDIATYPES USE_TI_MEDIAAVAILABLECAMERAS USE_TI_MEDIACAMERAAUTHORIZATION USE_TI_MEDIAVOLUME USE_TI_MEDIAAUDIOPLAYING USE_TI_MEDIACURRENTROUTE USE_TI_MEDIAVIBRATE USE_TI_MEDIABEEP USE_TI_MEDIASTARTMICROPHONEMONITOR USE_TI_MEDIASTOPMICROPHONEMONITOR USE_TI_MEDIAPEAKMICROPHONEPOWER USE_TI_MEDIAGETPEAKMICROPHONEPOWER USE_TI_MEDIAAVERAGEMICROPHONEPOWER USE_TI_MEDIAGETAVERAGEMICROPHONEPOWER USE_TI_UITABLEVIEWSCROLLPOSITION USE_TI_UILISTVIEWSCROLLPOSITION USE_TI_PLATFORMUPTIME