-->

分類

顯示具有 Android 標籤的文章。 顯示所有文章
顯示具有 Android 標籤的文章。 顯示所有文章

2016年8月25日 星期四

Android Local unit test/Instrumentation Test

reference 1
reference for unit test

basic:

differences between Local unit test and Instrumentation Test:
Local unit test: can just run on JVM, doesn't need Android framework
Instrumentation Test: can test Android component(Activity, Service...) and UI

create Class for testing(both local unit test and instrumentation test) in app/src/androidTest/java/<package_name>

set build.gradle:
dependencies {
    androidTestCompile 'junit:junit:4.12'
    androidTestCompile 'com.android.support.test:runner:0.4'
    // Set this dependency to use JUnit 4 rules
    androidTestCompile 'com.android.support.test:rules:0.4'
    androidTestCompile 'com.android.support:support-annotations:23.1.1'
}

mokito for mocking context seems useless if we use the sample code snippet here (the Context is null and MockitoAnnotations.initMocks can't work

sample Local unit test

import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class MyUnitTest {
    @Test
    public void testOne() {

        assertTrue("123".equals("123"));
    }
}


sample Instrumentation test
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.widget.Toast;

//JUnit4
public class ActivityFunctionTest extends ActivityInstrumentationTestCase2 {
    public ActivityFunctionTest(Class activityClass) {
        super(activityClass);
    }

    public ActivityFunctionTest(){
        super(MainMenuAcitivity.class);
    }

    public void testSetText() throws Exception {

        // set text
        getActivity().runOnUiThread(new Runnable() {

            @Override
            public void run() {
                MainMenuAcitivity activity = getActivity();
                Toast.makeText(activity, "test toast 2", Toast.LENGTH_SHORT).show();
            }
        });

        getInstrumentation().waitForIdleSync();

    }

    @UiThreadTest
    public void testSetTextWithAnnotation() throws Exception {

        MainMenuAcitivity activity = getActivity();
        Toast.makeText(activity, "test toast", Toast.LENGTH_SHORT).show();

    }
}

monkey command:
adb shell monkey -p <package_name> -v <number_of_events>

2016年7月25日 星期一

Android view static inner class in layout

    <view xmlns:android="http://schemas.android.com/apk/res/android"
          class="com.package.OuterClassName$InnerViewClassName">
</view>

2016年7月5日 星期二

some old stuff


bump mapping by GLSL, but I didn't understance the relations between tangent, normal and binormal that time...


an image rendered by ray tracing, totally forgot  how I made photon mapping work...


3D drawing on Android


2016年6月3日 星期五

Unity Android native crash debug

reference
1. unpack the .apk you build with zip or 7-zip, assume it unpack to the folder "YourProject"
2. copy "YourProject/lib/armeabi-v7a/libmono.so" to you Android ndk objdump tool folder(e.g.: "Android_NDK/android-ndk-r10e/toolchains/aarch64-linux-android-4.9/prebuilt/windows/aarch64-linux-android/bin")
3. use terminal or cmd in Windows : objdump.exe -S libmono.so >> out.txt
4. check out.txt

2016年5月30日 星期一

Android pull down for list

Activity

package com.example.james_lue;

import android.app.Activity;
import android.content.Context;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;

public class MorePullDowTest extends Activity {
    private static final String LOG_TAG = MorePullDowTest.class.getSimpleName();
    MyListAdapter mListAdapter;
    MyScrollView myScrollView;
    ListView mListView;
    TextView mUpTextView;
    private int mUpHeight;
    private boolean isListTop = true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_more_pull_dow_test);
        mListAdapter = new MyListAdapter(this);

        myScrollView = (MyScrollView) findViewById(R.id.main_scroll_view);
        myScrollView.setMorePullTest(this);
        mListView = (ListView)findViewById(R.id.main_list_view);

        mUpTextView = (TextView)findViewById(R.id.text_up);

        mListView.setAdapter(mListAdapter);
        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                switch (scrollState) {
                    case SCROLL_STATE_IDLE:
                        View c = mListView.getChildAt(0);
                        int scrollY = -c.getTop() + mListView.getFirstVisiblePosition() * c.getHeight();
                        if (scrollY == 0) {
                            isListTop = true;
                        }

                        Log.d(LOG_TAG, "scroll: " + scrollState + ", top: " + scrollY);
                        break;
                    case SCROLL_STATE_TOUCH_SCROLL:
                    case SCROLL_STATE_FLING:
                        isListTop = false;
                        break;
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });

        myScrollView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

            @Override
            public void onGlobalLayout() {
                // Ensure you call it only once :
                myScrollView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                // Here you can get the size :)
                ViewGroup.LayoutParams params = mListView.getLayoutParams();
                params.height = myScrollView.getHeight();
                params.width = myScrollView.getWidth();
                mUpHeight = mUpTextView.getHeight();
                mListView.setLayoutParams(params);
                myScrollView.requestLayout();
            }
        });

    }

    private static class MyListAdapter implements ListAdapter {

        private Context mContext;
        private MyListAdapter(Context context) {
            mContext = context;
        }
        @Override
        public boolean areAllItemsEnabled() {
            return true;
        }

        @Override
        public boolean isEnabled(int position) {
            return true;
        }

        @Override
        public void registerDataSetObserver(DataSetObserver observer) {

        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {

        }

        @Override
        public int getCount() {
            Log.d(LOG_TAG, "getCount");
            return 50;
        }

        @Override
        public Object getItem(int position) {
            return position + ": item";
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView textView;
            if(convertView == null) {
                textView = new TextView(mContext);
                textView.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
                textView.setHeight(100);
                textView.setTextSize(25.0f);

            } else {
                textView = (TextView)convertView;
            }
            textView.setText(position + ": item");
            //Log.d(LOG_TAG, "po: " + position);
            return textView;
        }

        @Override
        public int getItemViewType(int position) {
            return 0;
        }

        @Override
        public int getViewTypeCount() {
            return 1;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }
    }

    public static class MyScrollView extends ScrollView{
        private float mInitialY;
        private MorePullDowTest mActivity;
        public MyScrollView(Context context) {
            super(context, null);
            setFadingEdgeLength(0);
        }

        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setFadingEdgeLength(0);
        }

        public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            setFadingEdgeLength(0);
        }

        public void setMorePullTest(MorePullDowTest activity) {
            mActivity = activity;
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    mInitialY = ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float y = ev.getY();
                    float yD = y - mInitialY;

                    boolean shouldScrollBack = (yD > 0 && mActivity.isListTop);
                    Log.d(LOG_TAG, "yD: " + yD + "shouldScrollBack: " + shouldScrollBack + ", isTop: " + mActivity.isListTop);
                    if(shouldScrollBack) {
                        smoothScrollBy(0, 100 * (int) -yD);
                        mInitialY = y;
                        return true;
                    }
                    break;
            }
            Log.d(LOG_TAG, "intercept: " + ev.getActionMasked());
            return false;
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:

                    if (this.getScrollY() <= mActivity.mUpHeight) {
                        smoothScrollTo(0, mActivity.mUpHeight);
                        //mActivity.canScrollChild = true;
                    }

            }
            Log.d(LOG_TAG, "touch: " + event.getActionMasked());
            return super.onTouchEvent(event);
        }
    }
}


layout


    

        
            
            

2016年5月25日 星期三

Android View tricks

expand child of ScrollView to match ScorllView height:
adding android:fillViewport="true"to your ScrollView
link

2016年4月21日 星期四

Android recycler view inflation error

stack overflow
I meet this problem today. And solved it.

first step:keep the support-libs you used are same version

compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.1'


second step:you should add recyclerView to your proguard files

-keep class android.support.v7.widget.** {*;}
// I`ve just keep all widgets

2016年3月31日 星期四

Android ViewPager


來做個一頁可以有三個page然後換頁用翻的transform
MainActivity
public class MainActivity extends AppCompatActivity implements ScreenSlidePageFragment.OnFragmentInteractionListener{
    private static final int NUM_PAGES = 5;
    private float widthScale = 0.6f;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mPager = (ViewPager) findViewById(R.id.pager);
        mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);
        //mPager.setPageTransformer(false, new ZoomOutPageTransformer());
        mPager.setPageTransformer(false, new FlipPageTransformer());

        windowWidth = width;
        //mPager.setPageMargin(-(int)getResources().getDimension(R.dimen.viewpager_margin));

        //set page margin to be half of (viewpager's width - page's width)
        //page's width should be widthScale * viewPagerWidth
        float viewPagerWidth = getResources().getDimension(R.dimen.viewpager_width);
        mPager.setPageMargin((int) ((1.0f - widthScale) * viewPagerWidth / 2.0f));

        mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                if(position == NUM_PAGES - 1) {
                    //avoid to get stuck at last page
                    mPager.setCurrentItem(NUM_PAGES - 2, true);
                    return;
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        };


        mPager.addOnPageChangeListener(mOnPageChangeListener);
    }

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            final ScreenSlidePageFragment returnFragment;
            if(position % 2 == 0){
                String arg0 = "even";
                String arg1 = "isLast";
                if(position == NUM_PAGES - 1){
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, arg1);
                } else {
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, "");
                }

            } else {
                String arg0 = "odd";
                String arg1 = "isLast";
                if(position == NUM_PAGES - 1){
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, arg1);
                } else {
                    returnFragment = ScreenSlidePageFragment.newInstance(arg0, "");
                }
            }

            //return new ScreenSlidePageFragment();
            return returnFragment;
        }

        @Override
        public int getCount() {
            return NUM_PAGES;
        }


        @Override
        public float getPageWidth(int position) {
            return widthScale;
        }


    }

    public class FlipPageTransformer implements ViewPager.PageTransformer {
        private static final float MIN_SCALE = 0.85f;
        private static final float MIN_ALPHA = 0.5f;

        public void transformPage(View view, float position) {
            int pageWidth = view.getWidth();
            int pageHeight = view.getHeight();
            float pageFullWidth = (float)pageWidth / widthScale;
            //I guess this the page's movement from 1 -> 0 or 0 -> -1 will travel the distance of (pageFullWidth - (float)pageWidth) / 2.0f...
            float movement = (pageFullWidth - (float)pageWidth) / 2.0f;
            //float approx equal threshold
            float threshold = 0.0005f;
            if("isLast".equals(view.getTag())){
                //do not display last page
                view.setAlpha(0);
                return;
            }
            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0);

            } else if (position <= 1) { // [-1,1]
                view.setAlpha(1);

                //pageFullWidth * -position: move page's left edge to align viewpager's left edge
                //(1.0f + position) * movement: move page back to viewpager's left edge(if -1 < position < 0)
                // or view pager's right edge(if 0 < position < 1), but we still need page margin to help...
                view.setTranslationX(pageFullWidth * -position + (1.0f + position) * movement);

                if(position < -threshold){
                    view.setPivotX(0);
                    view.setRotationY(-position * 90.0f);
                } else if(position > threshold){
                    view.setPivotX(pageWidth);
                    view.setRotationY(-position * 90.0f);
                } else {//position == 0.0f
                    view.setPivotX(0);
                    //make page to be at center of viewpager
                    view.setTranslationX(movement);
                    view.setRotationY(0);
                }



            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0);
            }
        }
    }

    @Override
    public void onFragmentInteraction(Uri uri) {

    }
}

ScreenSlidePageFragment
public class ScreenSlidePageFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";
    public static final String VIEW_TAG_LAST = "last";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    private OnFragmentInteractionListener mListener;

    public ScreenSlidePageFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment ScreenSlidePageFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static ScreenSlidePageFragment newInstance(String param1, String param2) {
        ScreenSlidePageFragment fragment = new ScreenSlidePageFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
        View view = inflater.inflate(R.layout.fragment_screen_slide_page, container, false);
        if("even".equals(mParam1)){
            view.setBackgroundColor(getResources().getColor(R.color.primary_purple));
        }
        if("isLast".equals(mParam2)){
            view.setTag("isLast");
        }
        return view;
    }

    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(Uri uri) {
        if (mListener != null) {
            mListener.onFragmentInteraction(uri);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * * See the Android Training lesson Communicating with Other Fragments for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
}

res/layout/activity_main.xml

    


res/layout/fragment_screen_slid_page.xml

    



res/values/strings.xml

    AAA
    Hello blank fragment


res/values/colors.xml

    #3F51B5
    #303F9F
    #FF4081
    #52bf90
    #398564
    #FF4081
    #e4e5e5
    #e0ac69
    #c68642

    #673AB7
    #512DA8
    #D1C4E9
    #FF9800
    #212121
    #727272
    #FFFFFF
    #B6B6B6
    #374046


res/values/dimens.xml

    
    16dp
    16dp

    240dp

2016年3月24日 星期四

Android OverScroller, SplineScroller

source code
SplinerScroller做了一個類似在算Cubic Bezier Curve的東西, 只是這邊他不是算B(t)的值而是從已知B(x)的值去算t, 這裡利用t是strictly ascending的特性所以x min可以不用reset而x max需每次reset成1.0, 利用x = (x min + x max) / 2代入cubic bezier curve去看B(x)是否夠接近所求之值, 不夠則根據算出的B(x)修改x min, x max, 求出之各x存起來做為Spline interpolation之用(這段就看不太懂了)
(Wiki裡的t就是x)
Wiki Bezier

static {
            float x_min = 0.0f;
            float y_min = 0.0f;
            for (int i = 0; i < NB_SAMPLES; i++) {
                final float alpha = (float) i / NB_SAMPLES;

                float x_max = 1.0f;
                float x, tx, coef;
                while (true) {
                    x = x_min + (x_max - x_min) / 2.0f;
                    coef = 3.0f * x * (1.0f - x);
                    tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x;
                    if (Math.abs(tx - alpha) < 1E-5) break;
                    if (tx > alpha) x_max = x;
                    else x_min = x;
                }
                SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x;

                float y_max = 1.0f;
                float y, dy;
                while (true) {
                    y = y_min + (y_max - y_min) / 2.0f;
                    coef = 3.0f * y * (1.0f - y);
                    dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y;
                    if (Math.abs(dy - alpha) < 1E-5) break;
                    if (dy > alpha) y_max = y;
                    else y_min = y;
                }
                SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y;
            }
            SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
        }

實際使用OverScroller時就是view在computeScroll時不斷invalidate直到scroller認為scroll結束

@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            // This is called at drawing time by ViewGroup.  We use
            // this method to keep the fling animation going through
            // to completion.
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();

            scrollTo(x, y);

            // Keep on drawing until the animation has finished.
            postInvalidate();
        }
    }

2016年3月21日 星期一

Android touch, customized scroll

link

requestDisallowTouchIntercept: child阻止parent繼續intercept touch event,使用時機: drag, cleared when ACTION_CANCEL
interceptTouchEvent: parent收到touch event決定是否要再dispatch給children, 使用時機: scroll

Activity.onTouchEvent: 如果touch event都沒有被consumed, 最後會回到Activity.onTouchEvent

Touch Slop: scroll和touch的分界值

---------------
customized scroll

code link
大陸人sample

2016年3月17日 星期四

unity android plugin

link

unity doc

Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes

sdk path : Edit -> Preference -> Externel tool -> Android
C:/Users/XXX/AppData/Local/Android/sdk

Android build:
exportJar: in gradle tasks "other"
built jar: in ProjectName/app/release

trouble shooting:

just copy Android manifest to Unity
delete Theme light, Theme Dark related resources
copy (Android) app/res to (Unity)Plugins/Android
remove unused lib(jar) in Android

一、编译apk时。================================================
1.编译输出apk时报错。
Error building Player: CommandInvokationFailure: Failed to re-package resources. See the Console for details.
D:\android-sdk-windows\build-tools\19.1.0\aapt.exe package --auto-add-overlay -v -f -m -J gen -M AndroidManifest.xml -S "res" -I "D:/android-sdk-windows/platforms/android-21\android.jar" -F bin/resources.ap_
stderr[
res\values\styles.xml:7: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light'.
res\values-v11\styles.xml:7: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light'.
res\values-v14\styles.xml:8: error: Error retrieving parent for item: No resource found that matches the given name 'Theme.AppCompat.Light.DarkActionBar'.
]
答:在styles.xml没有 'Theme.AppCompat.Light 和 Theme.AppCompat.Light.DarkActionBar 这两个皮肤。。。
android 皮肤的使用 在这里。。
http://developer.android.com/guide/topics/ui/themes.html
或者改为:
<resources>
    <!--
        Base application theme, dependent on API level. This theme is replaced
        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
    -->
    <style name="AppBaseTheme" parent="android:Theme.Light.NoTitleBar">
        <!--
            Theme customizations available in newer API levels can go in
            res/values-vXX/styles.xml, while customizations related to
            backward-compatibility can go here.
        -->
    </style>
    <!-- Application theme. -->
    <style name="AppTheme" parent="AppBaseTheme">
        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
    </style>
</resources>
这样也可解决。

2、如果Unity打包过程出现类似下面这样的错误提示:
     Error building Player: CommandInvokationFailure: Failed to re-package resources. See the Console for details.
     X:\Android SDK\adt-bundle-windows-x86-XXXXXX\sdk\build-tools\X.1.0\aapt.exe package --auto-add-overlay -v -f -m -J gen -M AndroidManifest.xml -S "res" -I "E:/Android SDK/adt-bundle-windows-x86-XXXXXX/sdk/platforms/android-X\android.jar" -F bin/resources.ap_
表示在Android外挂项目里的res文件夹里有的地方的xml设置文件里的设置内容找不到,比如一条设置指向一个名为bnt的png图片文件,但这个图片的名字不是这个。

    3、如果Unity打包过程出现类似下面这样的错误提示:
    Error building Player: CommandInvokationFailure: Unable to convert classes into dex format. See the Console for details.
    X:\Program Files\Java\jdk1.X.X_XX\bin\java.exe -Xmx1024M -Dcom.android.sdkmanager.toolsdir="X:/Android SDK/adt-bundle-windows-x86-20130729/sdk\tools" -Dfile.encoding=UTF8 -jar "X:/Program Files/Unity/Editor/Data/BuildTargetTools/AndroidPlayer\sdktools.jar" -
表示你的你复制过来的libs文件夹里的东西和Unity的有冲突,通常是含有和Unity的classes.jar相同内容的jar文件。
二、安装后运行报错。================================================================
1.如果单独在运行没问题,接到Unity打包后在Android机子上一运行就退出,特别是那些拿来demo修改一下就用的人好碰到这种情况。检查代码都没有问题。这种情况原因很多,有一个可能性很大,就是在继承UnityPlayerActivity的那个Activity里用了finish函数,把这句删掉就行了,因为继承了UnityPlayerActivity就是Unity程序了,设置时还让它和Unity一起启动的,finish它,就是把你的游戏程序finish了。





2016年3月15日 星期二

Android import local aar

stackoverflow

在project folder
File > New > New module and choose Import .JAR/.AAR Package.
此時AAR project會產生在跟app同層並且此aap project的build.gradle會寫上
configurations.create("default")
artifacts.add("default", file('this-is-yours-package-in-aar-format.aar'))

此時在app的build.gradle的dependencies裡加上
compile project(':name-of-module-created-via-new-module-option-described-above')

Android VelocivtyTracker

VelocityTracker.cpp

裡面的LeastSquaresVelocityTrackerStrategy很有趣
我感覺是類似利用位移的物公式x = x0 + v * t + c * a * (t^2) (x為總位移, x0為某常數, t為時間,v為速度, c為某常數,a為加速度,以degree為2的情況來看)
就是利用之前記錄下的movement及time去建立一個矩陣去解least square(解x0 , v, c*a)
主要的解釋在這裡:
* That is to say, the function that generated the input data can be approximated
* by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
*
* The coefficient of determination (R^2) is also returned to describe the goodness
* of fit of the model for the given data.  It is a value between 0 and 1, where 1
* indicates perfect correspondence.
*
* This function first expands the X vector to a m by n matrix A such that
* A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then
* multiplies it by w[i]./
*

而其中y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n.
這裡y(x)就是位移(movement), x是時間, degree只有2所以就是解y(x) ~= B[0] + B[1] x + B[2] x^2
再對照x = x0 + v * t + c * a * (t^2)可知 B[1]就是v, 所以在最後return velocity時
(VelocityTracker::getVelocity裡)為 *outVx = estimator.xCoeff[1];

註解裡面的w[i]是權重,這個就還沒仔細研究, 不過權重目的應該是突顯某個B[i]的重要性

當然以上都只是我的感覺啦

2016年3月9日 星期三

Android Activity main thread entry point

一個好解說

Android 5 ActivityThread source code

Android workaround

java.lang.IllegalArgumentException: Service Intent must be explicit

cause source code (in ContextImpl.java)

private void validateServiceIntent(Intent service) {
        if (service.getComponent() == null && service.getPackage() == null) {
            if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                IllegalArgumentException ex = new IllegalArgumentException(
                        "Service Intent must be explicit: " + service);
                throw ex;
            } else {
                Log.w(TAG, "Implicit intents with startService are not safe: " + service
                        + " " + Debug.getCallers(2, 3));
            }
        }
    }
solution