NativeScript / android

NativeScript for Android using v8
https://docs.nativescript.org/guide/android-marshalling
Apache License 2.0
525 stars 135 forks source link

Cannot compile this class with Android SDK > API 23 #645

Closed NickIliev closed 7 years ago

NickIliev commented 7 years ago

From @alexrainman on November 30, 2016 23:49

I have this class:

var CirclePageIndicatorClass;
function ensureCirclePageIndicatorClass() {

    if (CirclePageIndicatorClass) {
        return;
    }

    class CirclePageIndicatorInner extends android.view.View
    {
        HORIZONTAL : number = 0;
        VERTICAL : number = 1;

        private mViewPager : android.support.v4.view.ViewPager;
        private mPageSize : number;

        public mCurrentPage : number;
        public mSnapPage : number;
        public mCurrentOffset : number;
        public mScrollState : number;

        public mCentered : boolean;
        public mPaintFill : android.graphics.Paint;
        public mPaintPageFill : android.graphics.Paint;
        public mOrientation : number;
        public mRadius : number;
        public mSnap : boolean;
        public mPaintStroke : android.graphics.Paint;

        constructor(context: android.content.Context)
        {
            super(context, null);

            this.mCentered = true;

            this.mPaintFill = new android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG);
            this.mPaintFill.setStyle(android.graphics.Paint.Style.FILL);
            this.mPaintFill.setColor(android.graphics.Color.parseColor("#808080"));

            this.mPaintPageFill = new android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG);
            this.mPaintPageFill.setStyle(android.graphics.Paint.Style.FILL);
            this.mPaintPageFill.setColor(android.graphics.Color.parseColor("#c0c0c0"));

            this.mOrientation = this.HORIZONTAL;
            this.mRadius = this.dpToPx(3);
            this.mSnap = true;

            this.mPaintStroke = new android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG);
            this.mPaintStroke.setStyle(android.graphics.Paint.Style.STROKE);
            this.mPaintStroke.setColor(android.graphics.Color.parseColor("#FFFFFFFF"));
            this.mPaintStroke.setStrokeWidth(0);

            let p = this.dpToPx(15);
            global.__native(this).setPadding(p,p,p,p);
            global.__native(this).setBackgroundColor(android.graphics.Color.TRANSPARENT);

            return global.__native(this);
        }

        public SetViewPager(view: android.support.v4.view.ViewPager, initialPosition: number) : void
        {
            if (view.getAdapter() == null)
            {
                throw new java.lang.IllegalStateException("ViewPager does not have adapter instance.");
            }
            this.mViewPager = view;
            this.UpdatePageSize();

            this.SetCurrentItem(initialPosition);
        }

        public SetCurrentItem(item: number) : void
        {
            if (this.mViewPager == null)
            {
                throw new java.lang.IllegalStateException("ViewPager has not been bound.");
            }
            this.mViewPager.setCurrentItem(item);
            this.mCurrentPage = item;
            this.invalidate();
        }

        public UpdatePageSize() : void
        {
            if (this.mViewPager != null)
            {
                this.mPageSize = (this.mOrientation == this.HORIZONTAL) ? this.mViewPager.getWidth() : this.mViewPager.getHeight();
            }
        }

        public dpToPx(dp: number) : number
        {
            var res = android.content.res.Resources;
            return dp * res.getSystem().getDisplayMetrics().density;
        }

        protected onDraw(canvas: android.graphics.Canvas): void
        {
            super.onDraw(canvas);

            if (this.mViewPager == null)
            {
                return;
            }
            let count = this.mViewPager.getAdapter().getCount();
            if (count == 0)
            {
                return;
            }

            if (this.mCurrentPage >= count)
            {
                this.SetCurrentItem(count - 1);
                return;
            }

            let longSize;
            let longPaddingBefore;
            let longPaddingAfter;
            let shortPaddingBefore;
            if (this.mOrientation == this.HORIZONTAL)
            {
                longSize = this.getWidth();
                longPaddingBefore = this.getPaddingLeft();
                longPaddingAfter = this.getPaddingRight();
                shortPaddingBefore = this.getPaddingTop();
            }
            else {
                longSize = this.getHeight();
                longPaddingBefore = this.getPaddingTop();
                longPaddingAfter = this.getPaddingBottom();
                shortPaddingBefore = this.getPaddingLeft();
            }

            let threeRadius = this.mRadius * 4; // dots separation
            let shortOffset = shortPaddingBefore + this.mRadius;
            let longOffset = longPaddingBefore + this.mRadius;
            if (this.mCentered)
            {
                longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0) - ((count * threeRadius) / 2.0);
            }

            let dX;
            let dY;

            let pageFillRadius = this.mRadius;
            if (this.mPaintStroke.getStrokeWidth() > 0)
            {
                pageFillRadius -= this.mPaintStroke.getStrokeWidth() / 2.0;
            }

            //Draw stroked circles
            for (var iLoop = 0; iLoop < count; iLoop++)
            {
                let drawLong = longOffset + (iLoop * threeRadius);
                if (this.mOrientation == this.HORIZONTAL)
                {
                    dX = drawLong;
                    dY = shortOffset;
                }
                else {
                    dX = shortOffset;
                    dY = drawLong;
                }
                // Only paint fill if not completely transparent
                if (this.mPaintPageFill.getAlpha() > 0)
                {
                    canvas.drawCircle(dX, dY, pageFillRadius, this.mPaintPageFill);
                }

                // Only paint stroke if a stroke width was non-zero
                if (pageFillRadius != this.mRadius)
                {
                    canvas.drawCircle(dX, dY, this.mRadius, this.mPaintStroke);
                }
            }

            //Draw the filled circle according to the current scroll
            let cx = (this.mSnap ? this.mSnapPage : this.mCurrentPage) * threeRadius;
            if (!this.mSnap && (this.mPageSize != 0))
            {
                cx += (this.mCurrentOffset * 1.0 / this.mPageSize) * threeRadius;
            }
            if (this.mOrientation == this.HORIZONTAL)
            {
                dX = longOffset + cx;
                dY = shortOffset;
            }
            else {
                dX = shortOffset;
                dY = longOffset + cx;
            }
            canvas.drawCircle(dX, dY, this.mRadius, this.mPaintFill);
        }

        protected onMeasure(widthMeasureSpec: number, heightMeasureSpec: number): void
        {
            if (this.mOrientation == this.HORIZONTAL)
            {
                this.setMeasuredDimension(this.MeasureLong(widthMeasureSpec), this.MeasureShort(heightMeasureSpec));
            }
            else {
                this.setMeasuredDimension(this.MeasureShort(widthMeasureSpec), this.MeasureLong(heightMeasureSpec));
            }
        }

        private MeasureLong(measureSpec: number) : number
        {
            let result = 0;
            var specMode = android.view.View.MeasureSpec.getMode(measureSpec);
            var specSize = android.view.View.MeasureSpec.getSize(measureSpec);

            if ((specMode == android.view.View.MeasureSpec.EXACTLY) || (this.mViewPager == null) || (this.mViewPager.getAdapter() == null))
            {
                //We were told how big to be
                result = specSize;
            }
            else {
                //Calculate the width according the views count
                let count = this.mViewPager.getAdapter().getCount();
                result = <number>(this.getPaddingLeft() + this.getPaddingRight()
                        + (count * 2 * this.mRadius) + (count - 1) * this.mRadius + 1);
                //Respect AT_MOST value if that was what is called for by measureSpec
                if (specMode == android.view.View.MeasureSpec.AT_MOST)
                {
                    result = java.lang.Math.min(result, specSize);
                }
            }
            return result;
        }

        private MeasureShort(measureSpec : number) : number
        {
            let result = 0;
            var specMode = android.view.View.MeasureSpec.getMode(measureSpec);
            var specSize = android.view.View.MeasureSpec.getSize(measureSpec);

            if (specMode == android.view.View.MeasureSpec.EXACTLY)
            {
                //We were told how big to be
                result = specSize;
            }
            else {
                //Measure the height
                result = <number>(2 * this.mRadius + this.getPaddingTop() + this.getPaddingBottom() + 1);
                //Respect AT_MOST value if that was what is called for by measureSpec
                if (specMode == android.view.View.MeasureSpec.AT_MOST)
                {
                    result = java.lang.Math.min(result, specSize);
                }
            }
            return result;
        }

        protected onRestoreInstanceState(state: android.os.IParcelable) : void
        {
            try
            {
                var bundle = state as android.os.Bundle;
                if (bundle != null)
                {
                    var superState = <android.os.IParcelable>bundle.getParcelable("base");
                    if (superState != null)
                        super.onRestoreInstanceState(superState);
                    this.mCurrentPage = bundle.getInt("mCurrentPage", 0);
                    this.mSnapPage = bundle.getInt("mCurrentPage", 0);
                }
            }
            catch(e)
            {
                super.onRestoreInstanceState(state);
                // Ignore, this needs to support IParcelable...
            }

            this.requestLayout();
        }

        protected onSaveInstanceState() : android.os.IParcelable
        {
            var superState = super.onSaveInstanceState();
            var state = new android.os.Bundle();
            state.putParcelable("base", superState);
            state.putInt("mCurrentPage", this.mCurrentPage);

            return state;
        }
    }

    CirclePageIndicatorClass = CirclePageIndicatorInner;
}

But i cannot use it with any Android SDK > API23:

ensureCirclePageIndicatorClass();
var dots = new CirclePageIndicatorClass(application.android.currentContext);
var layoutParams = new android.widget.RelativeLayout.LayoutParams(android.widget.RelativeLayout.LayoutParams.MATCH_PARENT, android.widget.RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(android.widget.RelativeLayout.ALIGN_PARENT_BOTTOM);
dots.SetViewPager(this._viewPager, this.position);
dots.mSnapPage = this.position;
this._android.addView(dots, layoutParams);

Then i get this exception:

An uncaught Exception occurred on "main" thread.
java.lang.RuntimeException: Unable to resume activity {org.nativescript.CarouselViewDemo/com.tns.NativeScriptActivity}: com.tns.NativeScriptException: 
Calling js method onCreateView failed

Error: java.lang.NoSuchFieldError: no "I" field "DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION" in class "Landroid/view/View;" or its superclasses
    com.tns.Runtime.callJSMethodNative(Native Method)
    com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1022)
    com.tns.Runtime.callJSMethodImpl(Runtime.java:907)
    com.tns.Runtime.callJSMethod(Runtime.java:895)
    com.tns.Runtime.callJSMethod(Runtime.java:879)
    com.tns.Runtime.callJSMethod(Runtime.java:871)
    com.tns.FragmentClass.onCreateView(android.app.Fragment.java)
    android.app.Fragment.performCreateView(Fragment.java:2220)
    android.app.FragmentManagerImpl.moveToState(FragmentManager.java:973)
    android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1148)
    android.app.BackStackRecord.run(BackStackRecord.java:793)
    android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1535)
    android.app.FragmentController.execPendingActions(FragmentController.java:325)
    android.app.Activity.performResume(Activity.java:6334)
    android.app.ActivityThread.performResumeActivity(ActivityThread.java:3092)
    android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3134)
    android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2481)
    android.app.ActivityThread.-wrap11(ActivityThread.java)
    android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
    android.os.Handler.dispatchMessage(Handler.java:102)
    android.os.Looper.loop(Looper.java:148)
    android.app.ActivityThread.main(ActivityThread.java:5417)
    java.lang.reflect.Method.invoke(Native Method)
    Frame: function:'View.onLoaded', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/core/view.js', line: 83, column: 35
    Frame: function:'Page.onLoaded', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/page/page-common.js', line: 53, column: 35
    Frame: function:'View._addViewCore', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/core/view-common.js', line: 1173, column: 18
    Frame: function:'View._addViewCore', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/core/view.js', line: 128, column: 39
    Frame: function:'View._addView', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/core/view-common.js', line: 1163, column: 14
    Frame: function:'onFragmentShown', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/frame/frame.js', line: 40, column: 11
    Frame: function:'FragmentCallbacksImplementation.onCreateView', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/frame/frame.js', line: 612, column: 13
    Frame: function:'FragmentClass.onCreateView', file:'/data/data/org.nativescript.CarouselViewDemo/files/app/tns_modules/ui/frame/fragment.js', line: 23, column: 38

    at com.tns.Runtime.callJSMethodNative(Native Method)
    at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1022)
    at com.tns.Runtime.callJSMethodImpl(Runtime.java:907)
    at com.tns.Runtime.callJSMethod(Runtime.java:895)
    at com.tns.Runtime.callJSMethod(Runtime.java:879)
    at com.tns.Runtime.callJSMethod(Runtime.java:871)
    at com.tns.FragmentClass.onCreateView(android.app.Fragment.java)
    at android.app.Fragment.performCreateView(Fragment.java:2220)
    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:973)
    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1148)
    at android.app.BackStackRecord.run(BackStackRecord.java:793)
    at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1535)
    at android.app.FragmentController.execPendingActions(FragmentController.java:325)
    at android.app.Activity.performResume(Activity.java:6334)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3092)
    ... 10 more
Caused by: java.lang.NoSuchFieldError: no "I" field "DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION" in class "Landroid/view/View;" or its superclasses
    ... 25 more

Any ideas?

Copied from original issue: NativeScript/NativeScript#3216

NickIliev commented 7 years ago

This issue was moved to NativeScript/android-runtime#644

NickIliev commented 7 years ago

duplicate to https://github.com/NativeScript/android-runtime/issues/644