[
  {
    "path": ".gitignore",
    "content": ".gradle\n.idea\nbuild\n*.iml\n/local.properties\n/bin"
  },
  {
    "path": "AgoraDemo/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "AgoraDemo/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 21\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"io.agora.demo.agora\"\n        minSdkVersion 14\n        targetSdkVersion 21\n        versionCode 20150915\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n//    sourceSets {\n//        main {\n//            jniLibs.srcDirs = ['../../libs']\n//        }\n//    }\n\n}\n\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: '../../libs')\n    compile 'com.android.support:appcompat-v7:21.0.3'\n    compile files('libs/agora-rtc-sdk.jar')\n    compile files('libs/android-async-http-1.4.8.jar')\n    compile files('libs/crasheye.jar')\n    compile files('libs/fastjson.jar')\n}\n"
  },
  {
    "path": "AgoraDemo/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/apple/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "AgoraDemo/src/androidTest/java/io/agora/demo/agora/ApplicationTest.java",
    "content": "package io.agora.demo.agora;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "AgoraDemo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"io.agora.demo.agora\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_LOGS\"/>\n\n    <application\n        android:name=\".AgoraApplication\"\n        android:icon=\"@drawable/app_icon_96\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@android:style/Theme.NoTitleBar\">\n\n        <activity\n            android:name=\"io.agora.demo.agora.EntryActivity\"\n            android:label=\"@string/app_name\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\"io.agora.demo.agora.LoginActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\" />\n\n        <activity\n            android:name=\"io.agora.demo.agora.ChannelActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"portrait\" />\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/AgoraApplication.java",
    "content": "package io.agora.demo.agora;\n\nimport android.app.Application;\nimport android.content.Context;\nimport android.util.Log;\n\nimport com.xsj.crasheye.Crasheye;\n\nimport io.agora.rtc.RtcEngine;\n\n/**\n * Created by apple on 15/9/9.\n */\npublic class AgoraApplication extends Application {\n\n    private RtcEngine rtcEngine;\n    private MessageHandler messageHandler;\n\n    @Override\n    public void onCreate(){\n\n        super.onCreate();\n        Crasheye.initWithNativeHandle(this, \"06798b00\");\n\n        messageHandler = new MessageHandler();\n    }\n\n    /**\n     * Test vendor key:  6D7A26A1D3554A54A9F43BE6797FE3E2\n     * @param vendorKey\n     */\n    public void setRtcEngine(String vendorKey){\n\n        if(rtcEngine==null) {\n            rtcEngine = RtcEngine.create(getApplicationContext(), vendorKey, messageHandler);\n        }\n    }\n\n    public RtcEngine getRtcEngine(){\n\n        return rtcEngine;\n    }\n\n    public void setEngineEventHandlerActivity(BaseEngineEventHandlerActivity engineEventHandlerActivity){\n        messageHandler.setActivity(engineEventHandlerActivity);\n    }\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/BaseActivity.java",
    "content": "package io.agora.demo.agora;\n\nimport android.os.Bundle;\nimport android.os.PersistableBundle;\nimport android.support.v4.app.FragmentActivity;\nimport android.view.View;\n\nimport io.agora.demo.agora.util.LoggingUtils;\n\n/**\n * Provide basic func for all activities used in App\n *\n * Created by on 9/12/15.\n */\npublic class BaseActivity extends FragmentActivity{\n\n\n    @Override\n    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {\n        super.onCreate(savedInstanceState, persistentState);\n    }\n\n    protected void onPause() {\n        super.onPause();\n    }\n\n    protected void onResume() {\n        super.onResume();\n    }\n\n\n    // Global view click listener\n    private View.OnClickListener onClickListener = new View.OnClickListener() {\n        @Override\n        public void onClick(View view) {\n            onUserInteraction(view);\n        }\n    };\n\n\n    public View.OnClickListener getViewClickListener(){\n        return onClickListener;\n    }\n\n    /**\n     * Central point of handling all view click events\n     * @param view\n     */\n    public void onUserInteraction(View view){\n\n    }\n\n    public void log(Object obj) {\n\n        // You can use filter *** to filter out message\n        LoggingUtils.error(getClass().getName(),\n                String.format(\"*** %s ***\",\n                        obj == null ? \"--!--\"\n                                : obj.toString()));\n    }\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/BaseEngineEventHandlerActivity.java",
    "content": "package io.agora.demo.agora;\n\nimport io.agora.rtc.IRtcEngineEventHandler;\n\n/**\n *\n * A handler activity act as a bridge to take callbacks from @MessageHandler.\n * Subclasses should override these key methods.\n *\n * Created by on 9/13/15.\n */\npublic class BaseEngineEventHandlerActivity extends BaseActivity {\n\n\n    public void onJoinChannelSuccess(String channel, int uid, int elapsed) {\n    }\n\n    public void onRejoinChannelSuccess(String channel, int uid, int elapsed) {\n    }\n\n    public void onError(int err) {\n    }\n\n    public void onCameraReady() {\n    }\n\n    public void onAudioQuality(int uid, int quality, short delay, short lost) {\n    }\n\n    public void onAudioTransportQuality(int uid, short delay, short lost) {\n    }\n\n    public void onVideoTransportQuality(int uid, short delay, short lost) {\n    }\n\n    public void onLeaveChannel(IRtcEngineEventHandler.RtcStats stats) {\n    }\n\n    public void onUpdateSessionStats(IRtcEngineEventHandler.RtcStats stats) {\n    }\n\n    public void onRecap(byte[] recap) {\n    }\n\n    public void onAudioVolumeIndication(IRtcEngineEventHandler.AudioVolumeInfo[] speakers, int totalVolume) {\n    }\n\n    public void onNetworkQuality(int quality) {\n    }\n\n    public void onUserJoined(int uid, int elapsed) {\n    }\n\n    public void onUserOffline(int uid) {\n    }\n\n    public void onUserMuteAudio(int uid, boolean muted) {\n    }\n\n    public void onUserMuteVideo(int uid, boolean muted) {\n    }\n\n    public void onAudioRecorderException(int nLastTimeStamp) {\n    }\n\n    public void onRemoteVideoStat(int uid, int frameCount, int delay, int receivedBytes) {\n    }\n\n    public void onLocalVideoStat(int sentBytes, int sentFrames) {\n    }\n\n    public void onFirstRemoteVideoFrame(int uid, int width, int height, int elapsed) {\n    }\n\n    public void onFirstLocalVideoFrame(int width, int height, int elapsed) {\n    }\n\n    public void onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed) {\n    }\n\n    public void onConnectionLost() {\n    }\n\n    public void onMediaEngineEvent(int code) {\n    }\n\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/ChannelActivity.java",
    "content": "package io.agora.demo.agora;\n\nimport android.app.AlertDialog;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.res.Configuration;\nimport android.os.Bundle;\nimport android.util.TypedValue;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.SurfaceView;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.widget.CheckBox;\nimport android.widget.CompoundButton;\nimport android.widget.FrameLayout;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport java.util.Random;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport io.agora.demo.agora.util.NetworkConnectivityUtils;\nimport io.agora.rtc.IRtcEngineEventHandler;\nimport io.agora.rtc.RtcEngine;\nimport io.agora.rtc.video.VideoCanvas;\n\n\n/**\n * some refs:\n * <p/>\n * 1. http://stackoverflow.com/questions/6026625/layout-design-surfaceview-doesnt-display\n * 2. http://stackoverflow.com/questions/1096618/android-surfaceview-scrolling/2216788#2216788\n */\n\n/**\n * Created by apple on 15/9/9.\n */\npublic class ChannelActivity extends BaseEngineEventHandlerActivity {\n\n    public final static int CALLING_TYPE_VIDEO = 0x100;\n    public final static int CALLING_TYPE_VOICE = 0x101;\n\n    public final static String EXTRA_CALLING_TYPE = \"EXTRA_CALLING_TYPE\";\n    public final static String EXTRA_VENDOR_KEY = \"EXTRA_VENDOR_KEY\";\n    public final static String EXTRA_CHANNEL_ID = \"EXTRA_CHANNEL_ID\";\n\n\n    private int mCallingType;\n    private SurfaceView mLocalView;\n    private String vendorKey = \"\";\n    private String channelId = \"\";\n    private TextView mDuration;\n    private TextView mByteCounts;\n    private View mCameraEnabler;\n    private View mCameraSwitcher;\n    private LinearLayout mRemoteUserContainer;\n    private AlertDialog alertDialog;\n    private int time = 0;\n\n    private int mLastRxBytes = 0;\n    private int mLastTxBytes = 0;\n    private int mLastDuration = 0;\n\n    private int mRemoteUserViewWidth = 0;\n\n    RtcEngine rtcEngine;\n\n\n    @Override\n    public void onCreate(Bundle savedInstance) {\n\n        super.onCreate(savedInstance);\n        setContentView(R.layout.activity_room);\n\n        // keep screen on - turned on\n        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n\n        mRemoteUserViewWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics());\n        mCallingType = getIntent().getIntExtra(EXTRA_CALLING_TYPE, CALLING_TYPE_VOICE /*default is voice call*/);\n\n        setupRtcEngine();\n        initViews();\n        setupTime();\n\n        if (CALLING_TYPE_VIDEO == mCallingType) {\n            // video call\n\n            View simulateClick = new View(getApplicationContext());\n            simulateClick.setId(R.id.wrapper_action_video_calling);\n            this.onUserInteraction(simulateClick);\n\n\n        } else if (CALLING_TYPE_VOICE == mCallingType) {\n            // voice call\n            View simulateClick = new View(getApplicationContext());\n            simulateClick.setId(R.id.wrapper_action_voice_calling);\n            this.onUserInteraction(simulateClick);\n        }\n\n\n        // check network\n        if (!NetworkConnectivityUtils.isConnectedToNetwork(getApplicationContext())) {\n            onError(104);\n        }\n    }\n\n    void setupChannel() {\n        String channelId = getIntent().getStringExtra(EXTRA_CHANNEL_ID);\n        this.channelId = channelId;\n\n        this.rtcEngine.joinChannel(\n                this.vendorKey,\n                this.channelId,\n                \"\" /*optionalInfo*/,\n                new Random().nextInt(Math.abs((int) System.currentTimeMillis()))/*optionalUid*/);\n\n        ((TextView) findViewById(R.id.channel_id)).setText(String.format(getString(R.string.title_channel), channelId));\n\n    }\n\n    void setupRtcEngine() {\n\n        String vendorKey = getIntent().getStringExtra(EXTRA_VENDOR_KEY);\n        this.vendorKey = vendorKey;\n\n        // setup engine\n        ((AgoraApplication) getApplication()).setRtcEngine(vendorKey);\n        rtcEngine = ((AgoraApplication) getApplication()).getRtcEngine();\n//        LogUtil.log.d(getApplicationContext().getExternalFilesDir(null).toString() + \"/agorasdk.log\");\n        rtcEngine.setLogFile(getApplicationContext().getExternalFilesDir(null).toString() + \"/agorasdk.log\");\n\n\n        // setup engine event activity\n        ((AgoraApplication) getApplication()).setEngineEventHandlerActivity(this);\n\n        rtcEngine.enableVideo();\n\n    }\n\n\n    void ensureLocalViewIsCreated() {\n\n\n        if (this.mLocalView == null) {\n\n            // local view has not been added before\n            FrameLayout localViewContainer = (FrameLayout) findViewById(R.id.user_local_view);\n            SurfaceView localView = rtcEngine.CreateRendererView(getApplicationContext());\n            this.mLocalView = localView;\n            localViewContainer.addView(localView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));\n\n            rtcEngine.enableVideo();\n            rtcEngine.setupLocalVideo(new VideoCanvas(this.mLocalView));\n        }\n\n    }\n\n    /**\n     * Initialize views and its listeners\n     */\n    void initViews() {\n\n        // muter\n        CheckBox muter = (CheckBox) findViewById(R.id.action_muter);\n        muter.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {\n            @Override\n            public void onCheckedChanged(CompoundButton compoundButton, boolean mutes) {\n\n                rtcEngine.muteLocalAudioStream(mutes);\n                compoundButton.setBackgroundResource(mutes ? R.drawable.ic_room_mute_pressed:R.drawable.ic_room_mute);\n\n            }\n        });\n\n        // speaker\n        CheckBox speaker = (CheckBox) findViewById(R.id.action_speaker);\n        speaker.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {\n            @Override\n            public void onCheckedChanged(CompoundButton compoundButton, boolean usesSpeaker) {\n\n                rtcEngine.setEnableSpeakerphone(usesSpeaker);\n                compoundButton.setBackgroundResource(usesSpeaker ? R.drawable.ic_room_loudspeaker : R.drawable.ic_room_loudspeaker_pressed);\n\n            }\n        });\n\n        // camera enabler\n        CheckBox cameraEnabler = (CheckBox) findViewById(R.id.action_camera_enabler);\n        cameraEnabler.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {\n            @Override\n            public void onCheckedChanged(CompoundButton compoundButton, boolean disablesCamera) {\n\n                rtcEngine.muteLocalVideoStream(disablesCamera);\n\n                if (disablesCamera) {\n                    findViewById(R.id.user_local_voice_bg).setVisibility(View.VISIBLE);\n                    rtcEngine.muteLocalVideoStream(true);\n\n                } else {\n                    findViewById(R.id.user_local_voice_bg).setVisibility(View.GONE);\n                    rtcEngine.muteLocalVideoStream(false);\n                }\n\n                compoundButton.setBackgroundResource(disablesCamera ? R.drawable.ic_room_button_close_pressed : R.drawable.ic_room_button_close);\n\n            }\n        });\n\n\n        // camera switcher\n        CheckBox cameraSwitch = (CheckBox) findViewById(R.id.action_camera_switcher);\n        cameraSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {\n            @Override\n            public void onCheckedChanged(CompoundButton compoundButton, boolean switches) {\n\n                rtcEngine.switchCamera();\n\n                compoundButton.setBackgroundResource(switches ? R.drawable.ic_room_button_change_pressed : R.drawable.ic_room_button_change);\n\n            }\n        });\n\n\n        // setup states of action buttons\n        muter.setChecked(false);\n        speaker.setChecked(true);\n        cameraEnabler.setChecked(false);\n        cameraSwitch.setChecked(false);\n\n\n        findViewById(R.id.wrapper_action_video_calling).setOnClickListener(getViewClickListener());\n        findViewById(R.id.wrapper_action_voice_calling).setOnClickListener(getViewClickListener());\n        findViewById(R.id.action_hung_up).setOnClickListener(getViewClickListener());\n        findViewById(R.id.action_back).setOnClickListener(getViewClickListener());\n\n\n        mDuration = (TextView) findViewById(R.id.stat_time);\n        mByteCounts = (TextView) findViewById(R.id.stat_bytes);\n\n\n        mCameraEnabler = findViewById(R.id.wrapper_action_camera_enabler);\n        mCameraSwitcher = findViewById(R.id.wrapper_action_camera_switcher);\n\n        mRemoteUserContainer = (LinearLayout) findViewById(R.id.user_remote_views);\n\n\n        setRemoteUserViewVisibility(false);\n    }\n\n    void setRemoteUserViewVisibility(boolean isVisible) {\n\n        findViewById(R.id.user_remote_views).getLayoutParams().height =\n                isVisible ? (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, getResources().getDisplayMetrics())\n                        : 0;\n\n    }\n\n    void removeBackgroundOfCallingWrapper() {\n        findViewById(R.id.wrapper_action_video_calling).setBackgroundResource(R.drawable.shape_transparent);\n        findViewById(R.id.wrapper_action_voice_calling).setBackgroundResource(R.drawable.shape_transparent);\n    }\n\n    void setupTime() {\n\n        TimerTask task = new TimerTask() {\n            @Override\n            public void run() {\n                runOnUiThread(new Runnable() {\n                    @Override\n                    public void run() {\n\n                        time++;\n\n                        if (time >= 3600) {\n                            mDuration.setText(String.format(\"%d:%02d:%02d\", time / 3600, (time % 3600) / 60, (time % 60)));\n                        } else {\n                            mDuration.setText(String.format(\"%02d:%02d\", (time % 3600) / 60, (time % 60)));\n                        }\n                    }\n                });\n            }\n        };\n\n        Timer timer = new Timer();\n        timer.schedule(task, 1000, 1000);\n    }\n\n\n    /**\n     * 切换视频音频通话时，更新 view 的显示。只是更新重用的 view，并不新添加。\n     *\n     * @param callingType\n     */\n    void updateRemoteUserViews(int callingType) {\n\n        int visibility = View.GONE;\n\n        if (CALLING_TYPE_VIDEO == callingType) {\n            visibility = View.GONE;\n\n        } else if (CALLING_TYPE_VOICE == callingType) {\n            visibility = View.VISIBLE;\n        }\n\n        for (int i = 0, size = mRemoteUserContainer.getChildCount(); i < size; i++) {\n\n            View singleRemoteView = mRemoteUserContainer.getChildAt(i);\n            singleRemoteView.findViewById(R.id.remote_user_voice_container).setVisibility(visibility);\n\n            if (CALLING_TYPE_VIDEO == callingType) {\n                // re-setup remote video\n\n                FrameLayout remoteVideoUser = (FrameLayout) singleRemoteView.findViewById(R.id.viewlet_remote_video_user);\n                // ensure remote video view setup\n                if(remoteVideoUser.getChildCount()>0) {\n                    final SurfaceView remoteView = (SurfaceView) remoteVideoUser.getChildAt(0);\n                    if(remoteView!=null) {\n                        remoteView.setZOrderOnTop(true);\n                        remoteView.setZOrderMediaOverlay(true);\n                        int savedUid = (Integer) remoteVideoUser.getTag();\n                        log(\"saved uid: \" + savedUid);\n                        rtcEngine.setupRemoteVideo(new VideoCanvas(remoteView, VideoCanvas.RENDER_MODE_ADAPTIVE, savedUid));\n                    }\n                }\n\n            }\n        }\n    }\n\n    @Override\n    public void onUserInteraction(View view) {\n        switch (view.getId()) {\n            default:\n                super.onUserInteraction(view);\n                break;\n            case R.id.wrapper_action_video_calling: {\n\n                mCallingType = CALLING_TYPE_VIDEO;\n\n                mCameraEnabler.setVisibility(View.VISIBLE);\n                mCameraSwitcher.setVisibility(View.VISIBLE);\n\n                removeBackgroundOfCallingWrapper();\n                findViewById(R.id.wrapper_action_video_calling).setBackgroundResource(R.drawable.ic_room_button_yellow_bg);\n                findViewById(R.id.user_local_voice_bg).setVisibility(View.GONE);\n\n                // enable video call\n                ensureLocalViewIsCreated();\n\n                rtcEngine.enableVideo();\n                rtcEngine.muteLocalVideoStream(false);\n                rtcEngine.muteLocalAudioStream(false);\n                rtcEngine.muteAllRemoteVideoStreams(false);\n\n                // join video call\n                if (mRemoteUserContainer.getChildCount() == 0) {\n                    this.setupChannel();\n                }\n\n                new android.os.Handler().postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        updateRemoteUserViews(CALLING_TYPE_VIDEO);\n                    }\n                },500);\n\n                // ensure video camera enabler states\n                CheckBox cameraEnabler = (CheckBox) findViewById(R.id.action_camera_enabler);\n                cameraEnabler.setChecked(false);\n\n            }\n            break;\n            case R.id.wrapper_action_voice_calling: {\n\n                mCallingType = CALLING_TYPE_VOICE;\n\n                mCameraEnabler.setVisibility(View.GONE);\n                mCameraSwitcher.setVisibility(View.GONE);\n\n                removeBackgroundOfCallingWrapper();\n                findViewById(R.id.wrapper_action_voice_calling).setBackgroundResource(R.drawable.ic_room_button_yellow_bg);\n\n                // show background for voice call\n                findViewById(R.id.user_local_voice_bg).setVisibility(View.VISIBLE);\n\n\n                ensureLocalViewIsCreated();\n\n                // disable video call when necessary\n                rtcEngine.disableVideo();\n                rtcEngine.muteLocalVideoStream(true);\n                rtcEngine.muteAllRemoteVideoStreams(true);\n\n                // join voice call\n                if (mRemoteUserContainer.getChildCount() == 0) {\n                    this.setupChannel();\n                }\n\n                new android.os.Handler().postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        updateRemoteUserViews(CALLING_TYPE_VOICE);\n                    }\n                },500);\n\n            }\n            break;\n\n            case R.id.action_hung_up:\n            case R.id.action_back: {\n                onBackPressed();\n            }\n            break;\n        }\n\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        return true;\n    }\n\n    @Override\n    public void onConfigurationChanged(Configuration newConfig) {\n\n        super.onConfigurationChanged(newConfig);\n    }\n\n    @Override\n    public void onBackPressed() {\n\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                rtcEngine.leaveChannel();\n            }\n        }).run();\n\n        // keep screen on - turned off\n        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n    }\n\n\n    public void onUpdateSessionStats(final IRtcEngineEventHandler.RtcStats stats) {\n\n        runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n\n                // bytes\n                mByteCounts.setText(((stats.txBytes + stats.rxBytes - mLastTxBytes - mLastRxBytes) / 1024 / (stats.totalDuration - mLastDuration + 1)) + \"KB/s\");\n\n                // remember data from this call back\n                mLastRxBytes = stats.rxBytes;\n                mLastTxBytes = stats.txBytes;\n                mLastDuration = stats.totalDuration;\n\n            }\n        });\n\n\n    }\n\n    public synchronized void onFirstRemoteVideoDecoded(final int uid, int width, int height, final int elapsed) {\n\n        log(\"onFirstRemoteVideoDecoded: uid: \" + uid + \", width: \" + width + \", height: \" + height);\n\n\n        runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n\n                View remoteUserView = mRemoteUserContainer.findViewById(Math.abs(uid));\n\n                // ensure container is added\n                if (remoteUserView == null) {\n\n                    LayoutInflater layoutInflater = getLayoutInflater();\n\n                    View singleRemoteUser = layoutInflater.inflate(R.layout.viewlet_remote_user, null);\n                    singleRemoteUser.setId(Math.abs(uid));\n\n                    TextView username = (TextView) singleRemoteUser.findViewById(R.id.remote_user_name);\n                    username.setText(String.valueOf(uid));\n\n                    mRemoteUserContainer.addView(singleRemoteUser, new LinearLayout.LayoutParams(mRemoteUserViewWidth, mRemoteUserViewWidth));\n\n                    remoteUserView = singleRemoteUser;\n                }\n\n\n                FrameLayout remoteVideoUser = (FrameLayout) remoteUserView.findViewById(R.id.viewlet_remote_video_user);\n                remoteVideoUser.removeAllViews();\n                remoteVideoUser.setTag(uid);\n\n                // ensure remote video view setup\n                final SurfaceView remoteView = RtcEngine.CreateRendererView(getApplicationContext());\n                remoteVideoUser.addView(remoteView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));\n                remoteView.setZOrderOnTop(true);\n                remoteView.setZOrderMediaOverlay(true);\n\n                rtcEngine.enableVideo();\n                int successCode = rtcEngine.setupRemoteVideo(new VideoCanvas(remoteView, VideoCanvas.RENDER_MODE_ADAPTIVE, uid));\n\n                if (successCode < 0) {\n                    new android.os.Handler().postDelayed(new Runnable() {\n                        @Override\n                        public void run() {\n                            rtcEngine.setupRemoteVideo(new VideoCanvas(remoteView, VideoCanvas.RENDER_MODE_ADAPTIVE, uid));\n                            remoteView.invalidate();\n                        }\n                    }, 500);\n                }\n\n\n                if (remoteUserView != null && CALLING_TYPE_VIDEO == mCallingType) {\n                    remoteUserView.findViewById(R.id.remote_user_voice_container).setVisibility(View.GONE);\n                } else {\n                    remoteUserView.findViewById(R.id.remote_user_voice_container).setVisibility(View.VISIBLE);\n                }\n\n                // app hints before you join\n                TextView appNotification = (TextView) findViewById(R.id.app_notification);\n                appNotification.setText(\"\");\n                setRemoteUserViewVisibility(true);\n            }\n        });\n\n    }\n\n    public synchronized void onUserJoined(final int uid, int elapsed) {\n\n        log(\"onUserJoined: uid: \" + uid);\n\n        View existedUser = mRemoteUserContainer.findViewById(Math.abs(uid));\n        if (existedUser != null) {\n            // user view already added\n            return;\n        }\n\n        runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n\n\n                // Handle the case onFirstRemoteVideoDecoded() is called before onUserJoined()\n                View singleRemoteUser = mRemoteUserContainer.findViewById(Math.abs(uid));\n                if (singleRemoteUser != null) {\n                    return;\n                }\n\n                LayoutInflater layoutInflater = getLayoutInflater();\n                singleRemoteUser = layoutInflater.inflate(R.layout.viewlet_remote_user, null);\n                singleRemoteUser.setId(Math.abs(uid));\n\n                TextView username = (TextView) singleRemoteUser.findViewById(R.id.remote_user_name);\n                username.setText(String.valueOf(uid));\n\n                mRemoteUserContainer.addView(singleRemoteUser, new LinearLayout.LayoutParams(mRemoteUserViewWidth, mRemoteUserViewWidth));\n\n\n                // app hints before you join\n                TextView appNotification = (TextView) findViewById(R.id.app_notification);\n                appNotification.setText(\"\");\n                setRemoteUserViewVisibility(true);\n\n            }\n        });\n\n\n    }\n\n    public void onUserOffline(final int uid) {\n\n        log(\"onUserOffline: uid: \" + uid);\n\n        if(isFinishing()){\n            return;\n        }\n\n        if(mRemoteUserContainer==null){\n            return;\n        }\n\n        runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n\n                View userViewToRemove = mRemoteUserContainer.findViewById(Math.abs(uid));\n                mRemoteUserContainer.removeView(userViewToRemove);\n\n                // no joined users any more\n                if (mRemoteUserContainer.getChildCount() == 0) {\n                    setRemoteUserViewVisibility(false);\n                    TextView appNotification = (TextView) findViewById(R.id.app_notification);\n                    appNotification.setText(R.string.room_prepare);\n                }\n            }\n        });\n\n\n    }\n\n\n    @Override\n    public void finish() {\n\n        if(alertDialog!=null){\n            alertDialog.dismiss();\n        }\n\n        super.finish();\n    }\n\n    @Override\n    public void onLeaveChannel(IRtcEngineEventHandler.RtcStats stats) {\n        try {\n            finish();\n        }catch (Exception e){\n            e.printStackTrace();\n        }\n    }\n\n    public void onUserMuteVideo(final int uid, final boolean muted) {\n\n        log(\"onUserMuteVideo uid: \" + uid + \", muted: \" + muted);\n\n        if(isFinishing()){\n            return;\n        }\n\n        if(mRemoteUserContainer==null){\n            return;\n        }\n\n        runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n\n                View remoteView = mRemoteUserContainer.findViewById(Math.abs(uid));\n                remoteView.findViewById(R.id.remote_user_voice_container).setVisibility(\n                        (CALLING_TYPE_VOICE==mCallingType || (CALLING_TYPE_VIDEO==mCallingType && muted))\n                                ? View.VISIBLE\n                                : View.GONE);\n                remoteView.invalidate();\n            }\n        });\n\n    }\n\n    @Override\n    public synchronized void onError(int err) {\n\n\n        if(isFinishing()){\n            return;\n        }\n\n\n        // incorrect vendor key\n        if(101==err){\n\n            runOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n\n                   if(alertDialog!=null){\n                       return;\n                   }\n\n                   alertDialog= new AlertDialog.Builder(ChannelActivity.this).setCancelable(false)\n                            .setMessage(getString(R.string.error_101))\n                            .setPositiveButton(getString(R.string.error_confirm), new DialogInterface.OnClickListener() {\n                                @Override\n                                public void onClick(DialogInterface dialog, int which) {\n\n                                    // Go to login\n                                    Intent toLogin = new Intent(ChannelActivity.this, LoginActivity.class);\n                                    toLogin.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);\n                                    startActivity(toLogin);\n\n                                    rtcEngine.leaveChannel();\n\n                                }\n                            }).setOnCancelListener(new DialogInterface.OnCancelListener() {\n                               @Override\n                               public void onCancel(DialogInterface dialogInterface) {\n                                   dialogInterface.dismiss();\n                               }\n                           })\n                           .create();\n\n                    alertDialog.show();\n                }\n            });\n\n\n\n        }\n\n        // no network connection\n        if (104 == err) {\n            runOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    TextView appNotification = (TextView) findViewById(R.id.app_notification);\n                    appNotification.setText(R.string.network_error);\n                }\n            });\n        }\n\n\n    }\n\n    public static String humanReadableByteCount(long bytes, boolean si) {\n        int unit = si ? 1000 : 1024;\n        if (bytes < unit) return bytes + \" B\";\n        int exp = (int) (Math.log(bytes) / Math.log(unit));\n        String pre = (si ? \"kMGTPE\" : \"KMGTPE\").charAt(exp - 1) + (si ? \"\" : \"i\");\n        return String.format(\"%.1f %sB\", bytes / Math.pow(unit, exp), pre);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        ((AgoraApplication) getApplication()).setEngineEventHandlerActivity(null);\n    }\n}"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/EntryActivity.java",
    "content": "package io.agora.demo.agora;\n\nimport android.content.Intent;\nimport android.content.res.Configuration;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.view.Window;\n\n/**\n * Launcher screen of app\n */\npublic class EntryActivity extends BaseActivity {\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n\n        super.requestWindowFeature(Window.FEATURE_NO_TITLE);\n\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_entry);\n\n        enterAppWithDelay(2000);\n    }\n\n\n    @Override\n    public void onConfigurationChanged(Configuration newConfig){\n\n        super.onConfigurationChanged(newConfig);\n    }\n\n    // move to login screen in delayInMillis\n    private void enterAppWithDelay(long delayInMillis){\n\n        new Handler().postDelayed(new Runnable() {\n            @Override\n            public void run() {\n\n                Intent intent=new Intent(EntryActivity.this,LoginActivity.class);\n                startActivity(intent);\n                finish();\n            }\n        },delayInMillis);\n    }\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/LoginActivity.java",
    "content": "package io.agora.demo.agora;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.Configuration;\nimport android.os.AsyncTask;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.view.View;\nimport android.view.Window;\nimport android.widget.EditText;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport org.apache.http.HttpResponse;\nimport org.apache.http.HttpStatus;\nimport org.apache.http.StatusLine;\nimport org.apache.http.client.ClientProtocolException;\nimport org.apache.http.client.HttpClient;\nimport org.apache.http.client.methods.HttpGet;\nimport org.apache.http.impl.client.DefaultHttpClient;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\n\n/**\n * Created by apple on 15/9/9.\n */\npublic class LoginActivity extends BaseActivity {\n\n    private EditText mVendorKey;\n    private EditText mChannelID;\n\n    @Override\n    public void onCreate(Bundle savedInstance) {\n\n        super.requestWindowFeature(Window.FEATURE_NO_TITLE);\n\n        super.onCreate(savedInstance);\n\n        new RequestTask().execute(\"http://192.168.99.253:8970/agora.inner.test.key.txt\"); // just update inner testing vendor key\n\n        setContentView(R.layout.activity_login);\n\n        initViews();\n    }\n\n    private void initViews() {\n\n        // bind listeners\n        findViewById(R.id.action_video_calling).setOnClickListener(getViewClickListener());\n        findViewById(R.id.action_voice_calling).setOnClickListener(getViewClickListener());\n\n        this.mVendorKey = (EditText) findViewById(R.id.input_vendor_key);\n        this.mChannelID = (EditText) findViewById(R.id.input_room_number);\n\n        // please your own key, the test key is unavailable soon.\n        this.mVendorKey.setText(getSharedPreferences(getClass().getName(), Context.MODE_PRIVATE).getString(ChannelActivity.EXTRA_VENDOR_KEY, \"5a04ec409d984031bd0ebd417efd7f8f\"));\n        this.mChannelID.setText(getSharedPreferences(getClass().getName(), Context.MODE_PRIVATE).getString(ChannelActivity.EXTRA_CHANNEL_ID, \"88888888\"));\n\n    }\n\n    @Override\n    public void onUserInteraction(View view) {\n\n\n        // Ensure inputs are valid;\n        if(!validateInput()){\n            return ;\n        }\n\n\n        switch (view.getId()) {\n            default:\n                super.onUserInteraction(view);\n\n                // Voice calling\n            case R.id.action_voice_calling: {\n\n                Intent intent = new Intent(LoginActivity.this, ChannelActivity.class);\n                intent.putExtra(ChannelActivity.EXTRA_CALLING_TYPE, ChannelActivity.CALLING_TYPE_VOICE);\n                intent.putExtra(ChannelActivity.EXTRA_VENDOR_KEY, mVendorKey.getText().toString());\n                intent.putExtra(ChannelActivity.EXTRA_CHANNEL_ID, mChannelID.getText().toString());\n                startActivity(intent);\n//                finish();\n\n            }\n            break;\n\n            // Video calling\n            case R.id.action_video_calling: {\n\n                Intent intent = new Intent(LoginActivity.this, ChannelActivity.class);\n                intent.putExtra(ChannelActivity.EXTRA_CALLING_TYPE, ChannelActivity.CALLING_TYPE_VIDEO);\n                intent.putExtra(ChannelActivity.EXTRA_VENDOR_KEY, mVendorKey.getText().toString());\n                intent.putExtra(ChannelActivity.EXTRA_CHANNEL_ID, mChannelID.getText().toString());\n                startActivity(intent);\n//                finish();\n\n            }\n            break;\n\n        }\n\n\n\n        // remember the vendor key and channel ID\n        getSharedPreferences(getClass().getName(), Context.MODE_PRIVATE)\n                .edit()\n                .putString(ChannelActivity.EXTRA_VENDOR_KEY, mVendorKey.getText().toString())\n                .putString(ChannelActivity.EXTRA_CHANNEL_ID, mChannelID.getText().toString())\n                .apply();\n    }\n\n    @Override\n    public void onConfigurationChanged(Configuration newConfig) {\n\n        super.onConfigurationChanged(newConfig);\n    }\n\n\n    boolean validateInput() {\n\n        String vendorKey = mVendorKey.getText().toString();\n        String roomNumber = mChannelID.getText().toString();\n\n        // validate vendor key\n        if (TextUtils.isEmpty(vendorKey)) {\n            Toast.makeText(getApplicationContext(), R.string.key_required, Toast.LENGTH_SHORT).show();\n            return false;\n        }\n\n        // validate room number - cannot be empty\n        if (TextUtils.isEmpty(roomNumber)) {\n            Toast.makeText(getApplicationContext(), R.string.room_required, Toast.LENGTH_SHORT).show();\n            return false;\n        }\n\n\n        // validate room number - should be digits only\n        if(!TextUtils.isDigitsOnly(roomNumber)){\n            Toast.makeText(getApplicationContext(), R.string.room_required, Toast.LENGTH_SHORT).show();\n            return false;\n        }\n\n        return true;\n    }\n\n\n    class RequestTask extends AsyncTask<String, String, String> {\n\n\n        String responseString = null;\n\n        @Override\n        protected String doInBackground(String... uri) {\n            HttpClient httpclient = new DefaultHttpClient();\n            HttpResponse response;\n            try {\n                response = httpclient.execute(new HttpGet(uri[0]));\n                StatusLine statusLine = response.getStatusLine();\n                if(statusLine != null && statusLine.getStatusCode() == HttpStatus.SC_OK){\n                    ByteArrayOutputStream out = new ByteArrayOutputStream();\n                    response.getEntity().writeTo(out);\n                    responseString = out.toString();\n                    out.close();\n                } else{\n                    //Closes the connection.\n                    response.getEntity().getContent().close();\n                    throw new IOException(statusLine.getReasonPhrase());\n                }\n            } catch (ClientProtocolException e) {\n                //TODO Handle problems..\n            } catch (IOException e) {\n                //TODO Handle problems..\n            }\n            return responseString;\n        }\n\n        @Override\n        protected void onPostExecute(String result) {\n            super.onPostExecute(result);\n            //Do anything with response..\n            if (responseString != null) {\n                mVendorKey.setText(responseString.replaceAll(\"\\\\s+\",\"\"), TextView.BufferType.EDITABLE);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/MessageHandler.java",
    "content": "package io.agora.demo.agora;\n\nimport io.agora.rtc.IRtcEngineEventHandler;\n\n/**\n * Created by apple on 15/9/10.\n */\npublic class MessageHandler extends IRtcEngineEventHandler {\n\n    private BaseEngineEventHandlerActivity mHandlerActivity;\n\n    //显示房间内其他用户的视频\n    @Override\n    public void onFirstRemoteVideoDecoded(int uid, int width, int height, int elapsed) {\n\n        BaseEngineEventHandlerActivity activity = getActivity();\n\n        if (activity != null) {\n            activity.onFirstRemoteVideoDecoded(uid, width, height, elapsed);\n        }\n    }\n\n    //用户进入\n    @Override\n    public void onUserJoined(int uid, int elapsed){\n\n        BaseEngineEventHandlerActivity activity = getActivity();\n\n        if (activity != null) {\n            activity.onUserJoined(uid, elapsed);\n        }\n    }\n\n    //用户退出\n    @Override\n    public void onUserOffline(int uid, int reason) {\n\n        BaseEngineEventHandlerActivity activity = getActivity();\n\n        if (activity != null) {\n            activity.onUserOffline(uid);\n        }\n    }\n\n    //监听其他用户是否关闭视频\n    @Override\n    public void onUserMuteVideo(int uid,boolean muted){\n\n        BaseEngineEventHandlerActivity activity = getActivity();\n\n        if (activity != null) {\n            activity.onUserMuteVideo(uid, muted);\n        }\n    }\n\n    //更新聊天数据\n    @Override\n    public void onRtcStats(RtcStats stats){\n\n        BaseEngineEventHandlerActivity activity = getActivity();\n\n        if (activity != null) {\n            activity.onUpdateSessionStats(stats);\n        }\n    }\n\n\n    @Override\n    public void onLeaveChannel(RtcStats stats) {\n        BaseEngineEventHandlerActivity activity = getActivity();\n\n        if (activity != null) {\n            activity.onLeaveChannel(stats);\n        }\n    }\n\n\n    @Override\n    public void onError(int err) {\n        BaseEngineEventHandlerActivity activity = getActivity();\n\n        if (activity != null) {\n            activity.onError(err);\n        }\n    }\n\n    public void setActivity(BaseEngineEventHandlerActivity activity) {\n\n        this.mHandlerActivity = activity;\n    }\n\n    public BaseEngineEventHandlerActivity getActivity(){\n\n        return mHandlerActivity;\n    }\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/util/LoggingUtils.java",
    "content": "/*-\n * Authors      : harry \n *\n * Created Date : Jun 13, 2013\n *  \n * Beauty Bond Inc.  All rights reserved.\n *\n */\n\npackage io.agora.demo.agora.util;\n\nimport android.util.Log;\n\nimport io.agora.demo.agora.BuildConfig;\n\n\n/**\n * This util offers flexible logging for development of Android application. We\n * will need log information when the application is in working process. But it\n * is not recommended to provide log information when the application is\n * released. Thus the wrapper of Log will help. The principal is to offer a\n * switch to turn on and off and any time to control if the log information is\n * visible.\n */\npublic class LoggingUtils {\n\n    public static void debug (String tag, String msg) {\n\n        if (BuildConfig.DEBUG) {\n            msg = msg == null ? \"\" : msg;\n            Log.d(tag, msg);\n        }\n    }\n\n    public static void error (String tag, String msg) {\n\n        if (BuildConfig.DEBUG) {\n            msg = msg == null ? \"\" : msg;\n            Log.e(tag, msg);\n        }\n    }\n\n    public static void warning (String tag, String msg) {\n\n        if (BuildConfig.DEBUG) {\n            msg = msg == null ? \"\" : msg;\n            Log.w(tag, msg);\n        }\n    }\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/java/io/agora/demo/agora/util/NetworkConnectivityUtils.java",
    "content": "/**\n * Created by harry on Nov 24, 2011 Copyright : FOCUSONE Inc. All Rights\n * Reserved.\n */\n\npackage io.agora.demo.agora.util;\n\nimport android.content.Context;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.net.NetworkInfo.State;\n\n/**\n * This utils helps to do the following: 1) If device is connected to mobile\n * network 2) If device is connected to wifi 3) If device is connected, either\n * to mobile network or wifi.\n * \n * @author harry\n */\npublic class NetworkConnectivityUtils {\n\n    /**\n     * Log\n     */\n    private static final String TAG = NetworkConnectivityUtils.class.getSimpleName ();\n\n    public static boolean isConnectedToMobile (Context context) {\n\n        ConnectivityManager conMan = (ConnectivityManager) context.getSystemService (Context.CONNECTIVITY_SERVICE);\n        // mobile\n        State mobile = conMan.getNetworkInfo (0).getState ();\n        LoggingUtils.debug (TAG,\n                            \"checking if device is connected to  mobile network\");\n        return mobile == State.CONNECTED;\n\n    }\n\n    public static boolean isConnectedToWifi (Context context) {\n\n        ConnectivityManager conMan = (ConnectivityManager) context.getSystemService (Context.CONNECTIVITY_SERVICE);\n        // wifi\n        State wifi = conMan.getNetworkInfo (1).getState ();\n        LoggingUtils.debug (TAG, \"checking if device is connected to wifi\");\n\n        return wifi == State.CONNECTED;\n    }\n\n    /**\n     * This is a simple way to check if you are CONNECTED or is CONNECTING to\n     * network. NOTE: you need to set <uses-permission\n     * android:name=\"android.permission.ACCESS_NETWORK_STATE\"></uses-permission>\n     * in your AndroidManifest.xml\n     * \n     * @param context a context used to getSystemInfo\n     * @return\n     */\n    public static boolean isConnectedToNetwork (Context context) {\n\n        ConnectivityManager cm = (ConnectivityManager) context.getSystemService (Context.CONNECTIVITY_SERVICE);\n        NetworkInfo netInfo = cm.getActiveNetworkInfo ();\n        boolean isConnected = netInfo != null && netInfo.isConnected ();\n        LoggingUtils.debug (TAG, \"device is connected to network :  \"\n                + isConnected);\n        return isConnected;\n    }\n\n    private NetworkConnectivityUtils() {\n\n    }\n}\n"
  },
  {
    "path": "AgoraDemo/src/main/res/drawable-xhdpi/button_selector_hung_up.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--扬声器-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/ic_room_button_red\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_room_button_red_pressed\"/>\n</selector>"
  },
  {
    "path": "AgoraDemo/src/main/res/drawable-xhdpi/go_micro_button_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/ic_go_button_yellow\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_go_button_yellow_pressed\"/>\n</selector>"
  },
  {
    "path": "AgoraDemo/src/main/res/drawable-xhdpi/go_video_button_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/ic_go_button_blue\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_go_button_blue_pressed\"/>\n</selector>"
  },
  {
    "path": "AgoraDemo/src/main/res/drawable-xhdpi/room_leave_button_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/ic_room_button_red\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_room_button_red_pressed\"/>\n</selector>"
  },
  {
    "path": "AgoraDemo/src/main/res/drawable-xhdpi/shape_theme.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\"rectangle\">\n    <solid android:color=\"@color/white\"/>\n</shape>"
  },
  {
    "path": "AgoraDemo/src/main/res/drawable-xhdpi/shape_transparent.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\" >\n    <solid android:color=\"#00000000\" />\n</shape>"
  },
  {
    "path": "AgoraDemo/src/main/res/layout/actionbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:id=\"@+id/acitonbar_title\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:paddingLeft=\"20dp\"\n        android:paddingRight=\"20dp\"\n        android:gravity=\"center\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "AgoraDemo/src/main/res/layout/activity_entry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/ic_splash_pic\">\n\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:background=\"@drawable/ic_splash_pic\"\n        android:layout_centerInParent=\"true\"\n        android:layout_height=\"wrap_content\" />\n\n</RelativeLayout>"
  },
  {
    "path": "AgoraDemo/src/main/res/layout/activity_login.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/ic_go_bg\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\"\n            android:padding=\"20dp\">\n\n            <!--logo-->\n\n            <TextView\n                android:id=\"@+id/go_logo\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"20dp\"\n                android:drawableLeft=\"@drawable/ic_go_logo\"\n                android:drawablePadding=\"10dp\"\n                android:gravity=\"center\"\n                android:textSize=\"16sp\"\n                android:textColor=\"@color/white\"\n                android:text=\"@string/app_conf\" />\n\n            <!--vendor key-->\n\n            <EditText\n                android:id=\"@+id/input_vendor_key\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"60dp\"\n                android:layout_toRightOf=\"@+id/go_key_image\"\n                android:background=\"@drawable/ic_go_input\"\n                android:drawableLeft=\"@drawable/ic_go_input_cell_key\"\n                android:drawablePadding=\"10dp\"\n                android:gravity=\"center_vertical\"\n                android:hint=\"@string/key_input\"\n                android:paddingLeft=\"10dp\"\n                android:paddingRight=\"10dp\"\n                android:ellipsize=\"end\"\n                android:singleLine=\"true\" />\n\n            <!--room #-->\n\n            <EditText\n                android:id=\"@+id/input_room_number\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"15dp\"\n                android:inputType=\"numberDecimal\"\n                android:layout_toRightOf=\"@+id/go_room_image\"\n                android:background=\"@drawable/ic_go_input\"\n                android:drawableLeft=\"@drawable/ic_go_input_cell_computer\"\n                android:drawablePadding=\"10dp\"\n                android:gravity=\"center_vertical\"\n                android:hint=\"@string/room_input\"\n                android:paddingLeft=\"10dp\"\n                android:paddingRight=\"10dp\"\n                android:ellipsize=\"end\"\n                android:singleLine=\"true\" />\n\n\n            <LinearLayout\n                android:layout_marginTop=\"15dp\"\n                android:layout_width=\"match_parent\"\n                android:minHeight=\"80dp\"\n                android:gravity=\"center_vertical\"\n                android:layout_height=\"wrap_content\"\n                android:orientation=\"horizontal\">\n\n                <!--Button for video calling-->\n\n                <RelativeLayout\n                    android:id=\"@+id/action_video_calling\"\n                    android:layout_weight=\"1.0\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:background=\"@drawable/ic_go_button_blue\">\n\n                    <ImageView\n                        android:id=\"@+id/go_button_video_image\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginTop=\"10dp\"\n                        android:layout_centerHorizontal=\"true\"\n                        android:background=\"@drawable/ic_go_button_cell_video\"/>\n\n                    <TextView\n                        android:id=\"@+id/go_button_video_text\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginTop=\"12dp\"\n                        android:textSize=\"16sp\"\n                        android:layout_below=\"@+id/go_button_video_image\"\n                        android:layout_centerHorizontal=\"true\"\n                        android:text=\"@string/video_enter\"\n                        android:textColor=\"@color/white\"/>\n\n                    <ImageView\n                        android:layout_width=\"15dp\"\n                        android:layout_height=\"15dp\"\n                        android:layout_marginTop=\"15dp\"\n\n                        android:layout_marginLeft=\"5dp\"\n                        android:layout_below=\"@+id/go_button_video_image\"\n                        android:layout_toRightOf=\"@+id/go_button_video_text\"\n                        android:background=\"@drawable/ic_go_button_cell_arrow\"/>\n\n                </RelativeLayout>\n\n\n                <!--Button for voice calling-->\n\n                <RelativeLayout\n                    android:id=\"@+id/action_voice_calling\"\n                    android:layout_weight=\"1.0\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"match_parent\"\n                    android:background=\"@drawable/ic_go_button_yellow\">\n\n                    <ImageView\n                        android:id=\"@+id/go_button_micro_image\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginTop=\"10dp\"\n                        android:layout_centerHorizontal=\"true\"\n                        android:background=\"@drawable/ic_go_button_cell_micro\"/>\n\n                    <TextView\n                        android:id=\"@+id/go_button_micro_text\"\n                        android:layout_width=\"wrap_content\"\n                        android:layout_height=\"wrap_content\"\n                        android:layout_marginTop=\"9dp\"\n                        android:textSize=\"16sp\"\n                        android:layout_below=\"@+id/go_button_micro_image\"\n                        android:layout_centerHorizontal=\"true\"\n                        android:text=\"@string/micro_enter\"\n                        android:textColor=\"@color/white\"/>\n\n                    <ImageView\n                        android:layout_width=\"15dp\"\n                        android:layout_height=\"15dp\"\n                        android:layout_marginTop=\"12dp\"\n                        android:layout_marginLeft=\"5dp\"\n                        android:layout_below=\"@+id/go_button_micro_image\"\n                        android:layout_toRightOf=\"@+id/go_button_micro_text\"\n                        android:background=\"@drawable/ic_go_button_cell_arrow\"/>\n\n                </RelativeLayout>\n\n            </LinearLayout>\n\n\n        </LinearLayout>\n\n    </ScrollView>\n\n</RelativeLayout>\n"
  },
  {
    "path": "AgoraDemo/src/main/res/layout/activity_room.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n\n    <RelativeLayout\n        android:id=\"@+id/custom_title_bar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?android:attr/actionBarSize\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"@drawable/ic_room_button_rectangle_blue\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:paddingLeft=\"15dp\"\n        android:paddingRight=\"15dp\">\n\n        <TextView\n            android:id=\"@+id/action_back\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:drawableLeft=\"@drawable/ic_navi_button_back\"\n            android:drawablePadding=\"5dp\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"14sp\" />\n\n\n        <TextView\n            android:id=\"@+id/channel_id\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_centerInParent=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:gravity=\"center\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"18sp\" />\n\n\n    </RelativeLayout>\n\n    <!--4 actions buttons-->\n\n\n    <LinearLayout\n        android:id=\"@+id/top_actions_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/custom_title_bar\"\n        android:background=\"@drawable/ic_room_rectangle\"\n        android:minHeight=\"50dp\"\n        android:orientation=\"horizontal\"\n        android:paddingBottom=\"5dp\"\n        android:paddingTop=\"5dp\"\n        android:visibility=\"visible\">\n\n        <!--静音-->\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\">\n\n            <CheckBox\n                android:id=\"@+id/action_muter\"\n                android:layout_width=\"35dp\"\n                android:layout_height=\"35dp\"\n                android:checked=\"true\"\n                android:button=\"@drawable/shape_transparent\"\n                />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"3dp\"\n                android:gravity=\"center\"\n                android:text=\"@string/room_button_one\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"10sp\" />\n\n        </LinearLayout>\n\n\n        <!--扬声器-->\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\">\n\n\n            <CheckBox\n                android:id=\"@+id/action_speaker\"\n                android:layout_width=\"35dp\"\n                android:layout_height=\"35dp\"\n                android:checked=\"false\"\n                android:button=\"@drawable/shape_transparent\" />\n\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"3dp\"\n                android:gravity=\"center\"\n                android:text=\"@string/room_button_two\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"10sp\" />\n\n        </LinearLayout>\n\n\n        <!--关闭摄像头-->\n        <LinearLayout\n            android:id=\"@+id/wrapper_action_camera_enabler\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\">\n\n            <CheckBox\n                android:id=\"@+id/action_camera_enabler\"\n                android:layout_width=\"35dp\"\n                android:checked=\"true\"\n                android:layout_height=\"35dp\"\n                android:button=\"@drawable/shape_transparent\"\n\n                />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"3dp\"\n                android:gravity=\"center\"\n                android:text=\"@string/room_button_three\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"10sp\" />\n\n        </LinearLayout>\n\n\n        <!--切换摄像头-->\n        <LinearLayout\n            android:id=\"@+id/wrapper_action_camera_switcher\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\">\n\n\n            <CheckBox\n                android:id=\"@+id/action_camera_switcher\"\n                android:layout_width=\"35dp\"\n                android:layout_height=\"35dp\"\n                android:checked=\"true\"\n                android:button=\"@drawable/shape_transparent\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"3dp\"\n                android:gravity=\"center\"\n                android:text=\"@string/room_button_four\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"10sp\" />\n\n\n        </LinearLayout>\n\n\n    </LinearLayout>\n\n\n    <!--视频通话 语音通话切换部分-->\n\n    <FrameLayout\n        android:id=\"@+id/bottom_actions_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"50dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"@drawable/ic_room_button_rectangle_blue\"\n        android:padding=\"6dp\">\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@drawable/ic_room_button_black_bg\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"horizontal\">\n\n\n            <!--button wrapper-->\n\n            <LinearLayout\n                android:id=\"@+id/wrapper_action_video_calling\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center\">\n\n                <View\n                    android:layout_width=\"1dp\"\n                    android:layout_height=\"0dp\"\n                    android:layout_weight=\"1\" />\n\n\n                <ImageView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:src=\"@drawable/ic_room_button_video\" />\n\n                <!--视频通话按钮-->\n                <TextView\n                    android:id=\"@+id/action_enable_video_call\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_marginLeft=\"10dp\"\n                    android:background=\"@drawable/shape_transparent\"\n                    android:gravity=\"center_vertical\"\n                    android:text=\"@string/video_button\"\n                    android:textColor=\"@color/white\"\n                    android:textSize=\"16sp\" />\n\n                <View\n                    android:layout_width=\"1dp\"\n                    android:layout_height=\"0dp\"\n                    android:layout_weight=\"1\" />\n\n\n            </LinearLayout>\n\n\n            <LinearLayout\n                android:id=\"@+id/wrapper_action_voice_calling\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:layout_weight=\"1\"\n                android:gravity=\"center\"\n                android:orientation=\"horizontal\">\n\n\n                <View\n                    android:layout_width=\"1dp\"\n                    android:layout_height=\"0dp\"\n                    android:layout_weight=\"1\" />\n\n\n                <ImageView\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"wrap_content\"\n                    android:src=\"@drawable/ic_room_button_audio\" />\n\n                <!--语音通话按钮-->\n                <TextView\n                    android:id=\"@+id/action_enable_voice_call\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_marginLeft=\"10dp\"\n                    android:background=\"@drawable/shape_transparent\"\n                    android:gravity=\"center_vertical\"\n                    android:text=\"@string/micro_button\"\n                    android:textColor=\"@color/white\"\n                    android:textSize=\"16sp\" />\n\n                <View\n                    android:layout_width=\"1dp\"\n                    android:layout_height=\"0dp\"\n                    android:layout_weight=\"1\" />\n\n\n            </LinearLayout>\n        </LinearLayout>\n    </FrameLayout>\n\n\n    <!--本地通话 和 远程通话用户头像列表-->\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@id/bottom_actions_container\"\n        android:layout_below=\"@id/top_actions_container\">\n\n\n        <FrameLayout\n            android:id=\"@+id/user_local_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@color/yellow\" />\n\n\n        <ImageView\n            android:id=\"@+id/user_local_voice_bg\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@color/black\"\n            android:scaleType=\"centerCrop\"\n            android:src=\"@drawable/ic_room_bg\" />\n\n\n        <LinearLayout\n            android:id=\"@+id/user_remote_views\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"80dp\"\n            android:layout_alignParentBottom=\"true\"\n            android:orientation=\"horizontal\" />\n\n        <LinearLayout\n            android:id=\"@+id/wrapper_session_stats\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"30dp\"\n            android:layout_marginTop=\"20dp\"\n            android:background=\"@drawable/ic_room_bg_talk_time\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"vertical\"\n            android:padding=\"5dp\">\n\n            <TextView\n                android:id=\"@+id/stat_time\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:singleLine=\"true\"\n                android:textColor=\"@color/yellow\"\n                android:textSize=\"10sp\"\n                android:textStyle=\"bold\" />\n\n\n            <TextView\n                android:id=\"@+id/stat_bytes\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"3dp\"\n                android:singleLine=\"true\"\n                android:textColor=\"@color/white\"\n                android:textSize=\"10sp\"\n                android:textStyle=\"bold\" />\n\n        </LinearLayout>\n\n\n        <TextView\n            android:id=\"@+id/app_notification\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"@string/room_prepare\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"18sp\" />\n\n\n        <Button\n            android:id=\"@+id/action_hung_up\"\n            android:layout_width=\"36dp\"\n            android:layout_height=\"36dp\"\n            android:layout_above=\"@id/user_remote_views\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginBottom=\"15dp\"\n            android:background=\"@drawable/button_selector_hung_up\" />\n\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "AgoraDemo/src/main/res/layout/viewlet_remote_user.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n\n    <FrameLayout\n        android:id=\"@+id/viewlet_remote_video_user\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n    </FrameLayout>\n\n\n    <LinearLayout\n        android:id=\"@+id/remote_user_voice_container\"\n        android:gravity=\"center_vertical\"\n        android:background=\"@color/remote_user_bg\"\n        android:layout_width=\"match_parent\"\n        android:orientation=\"vertical\"\n        android:layout_height=\"match_parent\">\n\n\n        <ImageView\n\n            android:layout_gravity=\"center_horizontal\"\n            android:id=\"@+id/remote_user_voice_icon\"\n            android:scaleType=\"centerInside\"\n            android:src=\"@drawable/ic_room_cell_microphone\"\n            android:layout_width=\"30dp\"\n            android:layout_height=\"30dp\" />\n\n        <TextView\n            android:id=\"@+id/remote_user_name\"\n            android:padding=\"2dp\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"10sp\"\n            android:gravity=\"center\"\n            android:singleLine=\"true\"\n            android:ellipsize=\"end\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n    </LinearLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "AgoraDemo/src/main/res/menu/menu_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\" tools:context=\".MainActivity\">\n\n    <item android:id=\"@+id/action_settings\"\n        android:title=\"@string/action_settings\"\n        android:orderInCategory=\"100\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "AgoraDemo/src/main/res/values/color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <color name=\"transparent\">#00000000</color>\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"yellow\">#DCBA75</color>\n    <color name=\"black\">#000</color>\n    <color name=\"remote_user_bg\">#3B3E4D</color>\n</resources>"
  },
  {
    "path": "AgoraDemo/src/main/res/values/strings.xml",
    "content": "<resources>\n\n    <string name=\"app_name\">AgoraDemo</string>\n    <string name=\"app_conf\">AgoraDemo</string>\n\n    <string name=\"title_channel\">Room %s</string>\n    <string name=\"action_settings\">Settings</string>\n\n    <string name=\"key_input\">Enter Vendor Key</string>\n\n    <string name=\"room_input\">Enter Room No.</string>\n\n    <string name=\"video_enter\">Join Video Call</string>\n\n    <string name=\"micro_enter\">Join Voice Call</string>\n\n    <string name=\"video_button\">Video Call</string>\n\n    <string name=\"micro_button\">Voice Call</string>\n\n    <string name=\"key_required\">Key can not be empty</string>\n\n    <string name=\"room_required\">Room No. can not be empty and must be digits</string>\n\n    <string name=\"room_button_one\">Mute</string>\n\n    <string name=\"room_button_two\">Speaker</string>\n\n    <string name=\"room_button_three\">Camera Off</string>\n\n    <string name=\"room_button_four\">Switch Camera</string>\n\n    <string name=\"room_prepare\">Waiting for attendees</string>\n\n    <string name=\"network_error\">No network connection, please check your network</string>\n\n    <string name=\"error_101\">Invalid vendor key</string>\n\n    <string name=\"error_confirm\">Done</string>\n\n\n</resources>\n"
  },
  {
    "path": "AgoraDemo/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"Theme.AppCompat.Light\">\n        <!--\n            Theme customizations available in newer API levels can go in\n            res/values-vXX/styles.xml, while customizations related to\n            backward-compatibility can go here.\n        -->\n    </style>\n\n    <!-- Application theme. -->\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n        <!-- All customizations that are NOT specific to a particular API-level can go here. -->\n        <item name=\"android:actionBarStyle\">@style/MyActionBar</item>\n    </style>\n\n    <!-- ActionBar styles -->\n    <style name=\"MyActionBar\" parent=\"@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse\">\n        <item name=\"android:background\">@drawable/shape_theme</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "AgoraDemo/src/main/res/values-zh/strings.xml",
    "content": "<resources>\n\n    <string name=\"app_name\">AgoraDemo</string>\n    <string name=\"app_conf\">Agora会议室</string>\n\n    <string name=\"title_channel\">房间%s</string>\n    <string name=\"action_settings\">设置</string>\n\n    <string name=\"key_input\">输入厂商Key</string>\n\n    <string name=\"room_input\">输入房间号码</string>\n\n    <string name=\"video_enter\">进入视频通话</string>\n\n    <string name=\"micro_enter\">进入语音通话</string>\n\n    <string name=\"video_button\">视频通话</string>\n\n    <string name=\"micro_button\">语音通话</string>\n\n    <string name=\"key_required\">Key不能为空</string>\n\n    <string name=\"room_required\">房间号不能为空, 且需要是数字</string>\n\n    <string name=\"room_button_one\">静音</string>\n\n    <string name=\"room_button_two\">扬声器</string>\n\n    <string name=\"room_button_three\">关闭摄像头</string>\n\n    <string name=\"room_button_four\">切换摄像头</string>\n\n    <string name=\"room_prepare\">房间等待好友加入</string>\n\n    <string name=\"network_error\">网络无法连接，请检查网络</string>\n\n    <string name=\"error_101\">无效的厂商Key</string>\n\n    <string name=\"error_confirm\">确定</string>\n\n</resources>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/.gitignore",
    "content": "/build\n/bin\n\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"com.cjj.android_materialrefreshlayout\"\n        minSdkVersion 11\n        targetSdkVersion 21\n        versionCode 2\n        versionName \"2.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    compile project(':MaterialRefresh_library')\n//    compile 'com.cjj.materialrefeshlayout:library:1.3.0'\n    compile 'com.android.support:recyclerview-v7:23.1.1'\n    compile 'com.android.support:cardview-v7:23.1.1'\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.cjj.android_materialrefreshlayout\" >\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/a\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/BaseTheme\" >\n        <activity\n            android:name=\".ScrollViewActivity\"\n            android:label=\"@string/app_name\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity android:name=\".AutoRefreshActivity\"/>\n        <activity android:name=\".LoadMoreActivity\"/>\n        <activity android:name=\".SimpleActivity\"/>\n        <activity android:name=\".WaveActivity\"/>\n        <activity android:name=\".SunActivity\"/>\n        <activity android:name=\".OverLayActivity\"/>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/AutoRefreshActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\nimport android.os.Bundle;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\n/**\n * Created by Administrator on 2015/9/10.\n */\npublic class AutoRefreshActivity extends BaseActivity {\n    private MaterialRefreshLayout materialRefreshLayout;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_listview);\n        String[] array = new String[50];\n        for (int i = 0; i < array.length; i++) {\n            array[i] = \"啊哈哈哈哈哈，啊哈哈 \" + i;\n        }\n\n        final ListView listView = (ListView) findViewById(R.id.lv);\n        listView.setAdapter(new android.widget.ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array));\n        materialRefreshLayout = (MaterialRefreshLayout) findViewById(R.id.refresh);\n        materialRefreshLayout.setLoadMore(true);\n        materialRefreshLayout.finishRefreshLoadMore();\n        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {\n            @Override\n            public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefresh();\n\n                    }\n                }, 3000);\n\n            }\n\n            @Override\n            public void onfinish() {\n                Toast.makeText(AutoRefreshActivity.this, \"finish\", Toast.LENGTH_LONG).show();\n            }\n\n            @Override\n            public void onRefreshLoadMore(MaterialRefreshLayout materialRefreshLayout) {\n                Toast.makeText(AutoRefreshActivity.this, \"load more\", Toast.LENGTH_LONG).show();\n            }\n        });\n\n\n        materialRefreshLayout.autoRefresh();\n\n    }\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/BaseActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\n\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\nimport android.widget.LinearLayout;\n\n/**\n * Created by Administrator on 2015/9/6.\n */\npublic class BaseActivity extends AppCompatActivity {\n\n    private LinearLayout rootLayout;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        super.setContentView(R.layout.activity_base);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();\n            localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);\n        }\n        initToolbar();\n    }\n\n    private void initToolbar() {\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\n        if (toolbar != null) {\n            setSupportActionBar(toolbar);\n        }\n    }\n\n    @Override\n    public void setContentView(int layoutId) {\n        setContentView(View.inflate(this, layoutId, null));\n    }\n\n    @Override\n    public void setContentView(View view) {\n        rootLayout = (LinearLayout) findViewById(R.id.root_layout);\n        if (rootLayout == null) return;\n        rootLayout.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        initToolbar();\n    }\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/LoadMoreActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\nimport android.content.Context;\nimport android.support.annotation.Nullable;\nimport android.os.Bundle;\nimport android.support.v7.widget.DefaultItemAnimator;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.Toolbar;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.Toast;\n\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\npublic class LoadMoreActivity extends BaseActivity {\n    private MaterialRefreshLayout materialRefreshLayout;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.fragment_list);\n        initsToolbar();\n\n        materialRefreshLayout = (MaterialRefreshLayout) findViewById(R.id.refresh);\n        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {\n            @Override\n            public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefresh();\n\n                    }\n                }, 3000);\n\n\n            }\n\n            @Override\n            public void onfinish() {\n                Toast.makeText(LoadMoreActivity.this, \"finish\", Toast.LENGTH_LONG).show();\n            }\n\n            @Override\n            public void onRefreshLoadMore(final MaterialRefreshLayout materialRefreshLayout) {\n                Toast.makeText(LoadMoreActivity.this, \"load more\", Toast.LENGTH_LONG).show();\n\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefreshLoadMore();\n\n                    }\n                }, 3000);\n            }\n        });\n\n\n        RecyclerView rv = (RecyclerView) findViewById(R.id.recyclerview);\n        setupRecyclerView(rv);\n    }\n\n    private void initsToolbar() {\n        Toolbar toolbar = (Toolbar) this.findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n        toolbar.setOnMenuItemClickListener(onMenuItemClick);\n    }\n\n    private Toolbar.OnMenuItemClickListener onMenuItemClick = new Toolbar.OnMenuItemClickListener() {\n        @Override\n        public boolean onMenuItemClick(MenuItem menuItem) {\n            switch (menuItem.getItemId()) {\n\n                case R.id.style0:\n                    materialRefreshLayout.setWaveColor(0xff8BC34A);\n                    materialRefreshLayout.setIsOverLay(false);\n                    materialRefreshLayout.setWaveShow(true);\n                    materialRefreshLayout.setShowProgressBg(true);\n                    materialRefreshLayout.setProgressColors(getResources().getIntArray(R.array.material_colors));\n                    materialRefreshLayout.setShowArrow(true);\n                    break;\n                case R.id.style1:\n\n                    break;\n//                case R.id.style2:\n//                    materialRefreshLayout.setWaveColor(0x90ffffff);\n//                    materialRefreshLayout.setIsOverLay(true);\n//                    materialRefreshLayout.setWaveShow(true);\n//                    materialRefreshLayout.setShowProgressBg(true);\n//                    materialRefreshLayout.setProgressColors(getResources().getIntArray(R.array.material_colors));\n//                    materialRefreshLayout.setShowArrow(true);\n//                    break;\n//                case R.id.style3:\n//                    materialRefreshLayout.setWaveColor(0xff8BC34A);\n//                    materialRefreshLayout.setIsOverLay(false);\n//                    materialRefreshLayout.setWaveShow(true);\n//                    materialRefreshLayout.setShowProgressBg(true);\n//                    materialRefreshLayout.setProgressColors(getResources().getIntArray(R.array.material_colors));\n//                    materialRefreshLayout.setShowArrow(true);\n//                    break;\n            }\n\n\n            return true;\n        }\n    };\n\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    private void setupRecyclerView(RecyclerView recyclerView) {\n        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));\n        recyclerView.setAdapter(new SimpleStringRecyclerViewAdapter(LoadMoreActivity.this));\n        recyclerView.setItemAnimator(new DefaultItemAnimator());\n    }\n\n    private List<String> getRandomSublist(String[] array, int amount) {\n        ArrayList<String> list = new ArrayList<>(amount);\n        Random random = new Random();\n        while (list.size() < amount) {\n            list.add(array[random.nextInt(array.length)]);\n        }\n        return list;\n    }\n\n    public static class SimpleStringRecyclerViewAdapter\n            extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {\n\n\n        public static class ViewHolder extends RecyclerView.ViewHolder {\n\n            public final ImageView mImageView;\n\n            public ViewHolder(View view) {\n                super(view);\n                mImageView = (ImageView) view.findViewById(R.id.avatar);\n            }\n\n\n        }\n\n        public SimpleStringRecyclerViewAdapter(Context context) {\n            super();\n        }\n\n        @Override\n        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            View view = LayoutInflater.from(parent.getContext())\n                    .inflate(R.layout.list_item, parent, false);\n            return new ViewHolder(view);\n        }\n\n        @Override\n        public void onBindViewHolder(final ViewHolder holder, int position) {\n            if (position == 0) {\n                holder.mImageView.setImageResource(R.drawable.a6);\n            } else if (position == 1) {\n                holder.mImageView.setImageResource(R.drawable.a5);\n            }\n\n        }\n\n        @Override\n        public int getItemCount() {\n            return 3;\n        }\n    }\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/OverLayActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\nimport android.os.Bundle;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\n/**\n * Created by cjj on 2016/2/24.\n */\npublic class OverLayActivity extends BaseActivity {\n\n    private MaterialRefreshLayout materialRefreshLayout;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_listview);\n        String[] array = new String[50];\n        for (int i = 0; i < array.length; i++) {\n            array[i] = \"啊哈哈哈哈哈 \" + i;\n        }\n\n        final ListView listView = (ListView) findViewById(R.id.lv);\n        listView.setAdapter(new android.widget.ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array));\n        materialRefreshLayout = (MaterialRefreshLayout) findViewById(R.id.refresh);\n        materialRefreshLayout.setSunStyle(true);\n        materialRefreshLayout.setIsOverLay(true);\n        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {\n            @Override\n            public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefresh();\n\n                    }\n                }, 3000);\n\n            }\n\n            @Override\n            public void onfinish() {\n                Toast.makeText(OverLayActivity.this, \"finish\", Toast.LENGTH_LONG).show();\n            }\n\n\n        });\n\n    }\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/ScrollViewActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\n/**\n * Created by Administrator on 2015/9/10.\n */\npublic class ScrollViewActivity extends BaseActivity implements View.OnClickListener {\n    private MaterialRefreshLayout materialRefreshLayout;\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_scrollview);\n\n        materialRefreshLayout = (MaterialRefreshLayout) findViewById(R.id.refresh);\n        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {\n            @Override\n            public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefresh();\n\n                    }\n                }, 3000);\n                materialRefreshLayout.finishRefreshLoadMore();\n\n            }\n\n            @Override\n            public void onfinish() {\n                Toast.makeText(ScrollViewActivity.this, \"finish\", Toast.LENGTH_LONG).show();\n            }\n        });\n    }\n\n    @Override\n    public void onClick(View v) {\n\n        switch (v.getId())\n        {\n            case R.id.simple:\n                startActivity(new Intent(this,SimpleActivity.class));\n                break;\n\n            case R.id.wave:\n                startActivity(new Intent(this,WaveActivity.class));\n                break;\n\n            case R.id.rv:\n                Intent intent = new Intent(this,LoadMoreActivity.class);\n                startActivity(intent);\n                break;\n            case R.id.lv:\n                Intent intent2 = new Intent(this,AutoRefreshActivity.class);\n                startActivity(intent2);\n                break;\n\n\n            case R.id.sun:\n                startActivity(new Intent(this,SunActivity.class));\n                break;\n\n            case R.id.overLay:\n                startActivity(new Intent(this,OverLayActivity.class));\n                break;\n\n        }\n    }\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/SimpleActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.DefaultItemAnimator;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.Toolbar;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.Toast;\n\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * Created by cjj on 2016/2/24.\n */\npublic class SimpleActivity extends BaseActivity {\n\n    private MaterialRefreshLayout materialRefreshLayout;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_simple);\n        initsToolbar();\n\n        materialRefreshLayout = (MaterialRefreshLayout) findViewById(R.id.refresh);\n        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {\n            @Override\n            public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefresh();\n\n                    }\n                }, 3000);\n                materialRefreshLayout.finishRefreshLoadMore();\n\n            }\n\n            @Override\n            public void onfinish() {\n                Toast.makeText(SimpleActivity.this, \"finish\", Toast.LENGTH_LONG).show();\n            }\n\n        });\n\n        RecyclerView rv = (RecyclerView) findViewById(R.id.recyclerview);\n        setupRecyclerView(rv);\n    }\n\n    private void initsToolbar() {\n        Toolbar toolbar = (Toolbar) this.findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n    }\n\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    private void setupRecyclerView(RecyclerView recyclerView) {\n        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));\n        recyclerView.setAdapter(new SimpleStringRecyclerViewAdapter(SimpleActivity.this));\n        recyclerView.setItemAnimator(new DefaultItemAnimator());\n    }\n\n    private List<String> getRandomSublist(String[] array, int amount) {\n        ArrayList<String> list = new ArrayList<>(amount);\n        Random random = new Random();\n        while (list.size() < amount) {\n            list.add(array[random.nextInt(array.length)]);\n        }\n        return list;\n    }\n\n    public static class SimpleStringRecyclerViewAdapter\n            extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {\n\n\n        public static class ViewHolder extends RecyclerView.ViewHolder {\n\n            public final ImageView mImageView;\n\n            public ViewHolder(View view) {\n                super(view);\n                mImageView = (ImageView) view.findViewById(R.id.avatar);\n            }\n\n\n        }\n\n        public SimpleStringRecyclerViewAdapter(Context context) {\n            super();\n        }\n\n        @Override\n        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            View view = LayoutInflater.from(parent.getContext())\n                    .inflate(R.layout.list_item, parent, false);\n            return new ViewHolder(view);\n        }\n\n        @Override\n        public void onBindViewHolder(final ViewHolder holder, int position) {\n            if (position == 0) {\n                holder.mImageView.setImageResource(R.drawable.a6);\n            } else if (position == 1) {\n                holder.mImageView.setImageResource(R.drawable.a5);\n            }\n\n        }\n\n        @Override\n        public int getItemCount() {\n            return 3;\n        }\n    }\n\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/SunActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\nimport android.os.Bundle;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\n/**\n * Created by cjj on 2016/2/24.\n */\npublic class SunActivity extends BaseActivity{\n\n    private MaterialRefreshLayout materialRefreshLayout;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_listview);\n        String[] array = new String[50];\n        for (int i = 0; i < array.length; i++) {\n            array[i] = \"啊哈哈哈哈哈 \" + i;\n        }\n\n        final ListView listView = (ListView) findViewById(R.id.lv);\n        listView.setAdapter(new android.widget.ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array));\n        materialRefreshLayout = (MaterialRefreshLayout) findViewById(R.id.refresh);\n        materialRefreshLayout.setSunStyle(true);\n        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {\n            @Override\n            public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefresh();\n\n                    }\n                }, 3000);\n\n            }\n\n            @Override\n            public void onfinish() {\n                Toast.makeText(SunActivity.this, \"finish\", Toast.LENGTH_LONG).show();\n            }\n\n\n        });\n\n    }\n\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/SwipeRefreshLayoutActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\n/**\n * Created by Administrator on 2015/9/10.\n */\npublic class SwipeRefreshLayoutActivity {\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/java/com/cjj/android_materialrefreshlayout/WaveActivity.java",
    "content": "package com.cjj.android_materialrefreshlayout;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.widget.DefaultItemAnimator;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.Toolbar;\nimport android.view.LayoutInflater;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.Toast;\n\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\n/**\n * Created by cjj on 2016/2/24.\n */\npublic class WaveActivity extends BaseActivity {\n    private MaterialRefreshLayout materialRefreshLayout;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_simple);\n        initsToolbar();\n\n        materialRefreshLayout = (MaterialRefreshLayout) findViewById(R.id.refresh);\n\n        materialRefreshLayout.setWaveShow(true);\n        materialRefreshLayout.setWaveColor(Color.parseColor(\"#60ff2020\"));\n\n        materialRefreshLayout.setMaterialRefreshListener(new MaterialRefreshListener() {\n            @Override\n            public void onRefresh(final MaterialRefreshLayout materialRefreshLayout) {\n                materialRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        materialRefreshLayout.finishRefresh();\n\n                    }\n                }, 3000);\n                materialRefreshLayout.finishRefreshLoadMore();\n\n            }\n\n            @Override\n            public void onfinish() {\n                Toast.makeText(WaveActivity.this, \"finish\", Toast.LENGTH_LONG).show();\n            }\n\n        });\n\n        RecyclerView rv = (RecyclerView) findViewById(R.id.recyclerview);\n        setupRecyclerView(rv);\n    }\n\n    private void initsToolbar() {\n        Toolbar toolbar = (Toolbar) this.findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n    }\n\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    private void setupRecyclerView(RecyclerView recyclerView) {\n        recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext()));\n        recyclerView.setAdapter(new SimpleStringRecyclerViewAdapter(WaveActivity.this));\n        recyclerView.setItemAnimator(new DefaultItemAnimator());\n    }\n\n    private List<String> getRandomSublist(String[] array, int amount) {\n        ArrayList<String> list = new ArrayList<>(amount);\n        Random random = new Random();\n        while (list.size() < amount) {\n            list.add(array[random.nextInt(array.length)]);\n        }\n        return list;\n    }\n\n    public static class SimpleStringRecyclerViewAdapter\n            extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {\n\n\n        public static class ViewHolder extends RecyclerView.ViewHolder {\n\n            public final ImageView mImageView;\n\n            public ViewHolder(View view) {\n                super(view);\n                mImageView = (ImageView) view.findViewById(R.id.avatar);\n            }\n\n\n        }\n\n        public SimpleStringRecyclerViewAdapter(Context context) {\n            super();\n        }\n\n        @Override\n        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            View view = LayoutInflater.from(parent.getContext())\n                    .inflate(R.layout.list_item, parent, false);\n            return new ViewHolder(view);\n        }\n\n        @Override\n        public void onBindViewHolder(final ViewHolder holder, int position) {\n            if (position == 0) {\n                holder.mImageView.setImageResource(R.drawable.a6);\n            } else if (position == 1) {\n                holder.mImageView.setImageResource(R.drawable.a5);\n            }\n\n        }\n\n        @Override\n        public int getItemCount() {\n            return 3;\n        }\n    }\n\n}\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/activity_base.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/root_layout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <include layout=\"@layout/toolbar_layout\" />\n\n</LinearLayout>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/activity_listview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.cjj.MaterialRefreshLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:id=\"@+id/refresh\"\n    app:wave_color=\"#20ff2020\"\n    app:progress_show_circle_backgroud=\"true\"\n    app:overlay=\"false\"\n    app:wave_show=\"true\"\n    app:progress_backgroud_color=\"#FFFAFAFA\"\n    app:progress_colors=\"@array/material_colors\"\n    app:wave_height_type=\"normal\"\n    app:progress_show_arrow=\"true\"\n    >\n\n<ListView\n    android:id=\"@+id/lv\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"></ListView>\n\n\n</com.cjj.MaterialRefreshLayout>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/activity_main.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\" android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\" tools:context=\".MainActivity\">\n\n    <TextView android:text=\"@string/hello_world\" android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/activity_scrollview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.cjj.MaterialRefreshLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/refresh\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:overlay=\"false\"\n    app:progress_backgroud_color=\"#FFFAFAFA\"\n    app:progress_colors=\"@array/material_colors\"\n    app:progress_show_arrow=\"true\"\n    app:progress_show_circle_backgroud=\"true\"\n    app:wave_color=\"#60ff2020\"\n    app:wave_height_type=\"normal\"\n    app:wave_show=\"true\"\n    >\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <Button\n                android:id=\"@+id/simple\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"60dp\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:layout_marginTop=\"10dp\"\n                android:background=\"#ff2020\"\n                android:onClick=\"onClick\"\n                android:text=\"simple\"\n                android:textColor=\"#fff\"/>\n\n            <Button\n                android:id=\"@+id/wave\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"60dp\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:layout_marginTop=\"10dp\"\n                android:background=\"#ff2020\"\n                android:onClick=\"onClick\"\n                android:text=\"wave\"\n                android:textColor=\"#fff\"/>\n\n            <Button\n                android:id=\"@+id/rv\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"60dp\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:layout_marginTop=\"10dp\"\n                android:background=\"#ff2020\"\n                android:onClick=\"onClick\"\n                android:text=\"loadMore\"\n                android:textColor=\"#fff\"/>\n\n            <Button\n                android:id=\"@+id/lv\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"60dp\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:layout_marginTop=\"10dp\"\n                android:background=\"#ff2020\"\n                android:onClick=\"onClick\"\n                android:text=\"autoFresh\"\n                android:textColor=\"#fff\"/>\n\n            <Button\n                android:id=\"@+id/sun\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"60dp\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:layout_marginTop=\"10dp\"\n                android:background=\"#ff2020\"\n                android:onClick=\"onClick\"\n                android:text=\"sun\"\n                android:textColor=\"#fff\"/>\n\n            <Button\n                android:id=\"@+id/overLay\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"60dp\"\n                android:layout_marginLeft=\"10dp\"\n                android:layout_marginRight=\"10dp\"\n                android:layout_marginTop=\"10dp\"\n                android:background=\"#ff2020\"\n                android:onClick=\"onClick\"\n                android:text=\"overLay\"\n                android:textColor=\"#fff\"/>\n\n\n        </LinearLayout>\n    </ScrollView>\n</com.cjj.MaterialRefreshLayout>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/activity_simple.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ Copyright (C) 2015 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<com.cjj.MaterialRefreshLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/refresh\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    app:overlay=\"true\"\n    app:progress_colors=\"@array/material_colors\"\n    app:progress_size_type=\"normal\"\n    app:wave_height_type=\"normal\"\n    app:wave_show=\"false\"\n    >\n\n\n    <android.support.v7.widget.RecyclerView\n        xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        />\n\n\n</com.cjj.MaterialRefreshLayout>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/fragment_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--\n  ~ Copyright (C) 2015 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<com.cjj.MaterialRefreshLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:id=\"@+id/refresh\"\n    app:overlay=\"true\"\n    app:isLoadMore=\"true\"\n    app:progress_size_type=\"normal\"\n    app:wave_color=\"#90ffffff\"\n    app:wave_show=\"true\"\n    app:progress_colors=\"@array/material_colors\"\n    app:wave_height_type=\"normal\"\n    >\n\n\n    <android.support.v7.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:id=\"@+id/recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n     />\n\n\n</com.cjj.MaterialRefreshLayout>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n  ~ Copyright (C) 2015 The Android Open Source Project\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~      http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n\n<android.support.v7.widget.CardView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"10dp\"\n    android:gravity=\"center_vertical\">\n\n    <ImageView\n        android:src=\"@drawable/dd\"\n        android:id=\"@+id/avatar\"\n        android:scaleType=\"centerCrop\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"250dp\"\n      />\n</android.support.v7.widget.CardView>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/layout/toolbar_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.v7.widget.Toolbar xmlns:android=\"http://schemas.android.com/apk/res/android\"\n                                   xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n                                   android:id=\"@+id/toolbar\"\n                                   android:layout_width=\"match_parent\"\n                                   android:layout_height=\"wrap_content\"\n                                   android:paddingTop=\"@dimen/toolbar_padding_top\"\n                                   android:background=\"#ff2020\"\n                                   android:minHeight=\"?attr/actionBarSize\"\n                                   app:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\"\n                                   app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\" />"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/menu/menu_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\" tools:context=\".MainActivity\">\n\n    <item android:id=\"@+id/style0\"\n        android:title=\"wave\"\n        android:orderInCategory=\"70\"\n        app:showAsAction=\"never\" />\n\n    <item android:id=\"@+id/style1\"\n        android:title=\"overlay\"\n        android:orderInCategory=\"80\"\n        app:showAsAction=\"never\" />\n\n    <item android:id=\"@+id/style2\"\n        android:title=\"invasive\"\n        android:orderInCategory=\"90\"\n        app:showAsAction=\"never\" />\n\n    <item android:id=\"@+id/style3\" android:title=\"noninvasive\"\n        android:orderInCategory=\"100\" app:showAsAction=\"never\" />\n\n\n\n\n</menu>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"primary_color\">#ff2020</color>\n</resources>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n    <dimen name=\"toolbar_padding_top\">0dp</dimen>\n</resources>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">MaterialRefreshLayout</string>\n\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"action_settings\">Settings</string>\n\n    <integer-array name=\"material_colors\">\n        <item>@color/material_red</item>\n        <item>@color/material_blue</item>\n        <item>@color/material_yellow</item>\n    </integer-array>\n</resources>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"BaseTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"colorPrimary\">#E91E63</item>\n        <item name=\"colorPrimaryDark\">#E91E63</item>\n        <item name=\"colorAccent\">#9C27B0</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values-v19/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"toolbar_padding_top\">25dp</dimen>\n</resources>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values-v19/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Base application theme. -->\n    <style name=\"CJJTheme\" parent=\"BaseTheme\">\n        <!-- 建议在BaseActivity中代码声明 -->\n        <!--<item name=\"android:windowTranslucentStatus\">true</item>-->\n        <item name=\"android:windowContentOverlay\">@null</item>\n    </style>\n</resources>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values-v21/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"CJJTheme\" parent=\"BaseTheme\">\n        <item name=\"android:navigationBarColor\">#E91E63</item>\n\n    </style>\n</resources>"
  },
  {
    "path": "Android-MaterialRefreshLayout-master/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "Android应用源码音乐实时跳动频谱显示Demo/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 22\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"com.terry.AudioFx\"\n        minSdkVersion 15\n        targetSdkVersion 22\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n        }\n    }\n}\n"
  },
  {
    "path": "Android应用源码音乐实时跳动频谱显示Demo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.terry.AudioFx\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" >\n\n    <application\n        android:icon=\"@drawable/icon\"\n        android:label=\"@string/app_name\" >\n        <activity\n            android:label=\"@string/app_name\"\n            android:name=\".AudioFxActivity\" >\n            <intent-filter >\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n    <uses-sdk android:minSdkVersion=\"10\" />\n\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" ></uses-permission>\n\n</manifest>"
  },
  {
    "path": "Android应用源码音乐实时跳动频谱显示Demo/src/main/java/com/terry/AudioFx/AudioFxActivity.java",
    "content": "package com.terry.AudioFx;\n\nimport android.app.Activity;\nimport android.media.AudioManager;\nimport android.media.MediaPlayer;\nimport android.media.audiofx.Visualizer;\nimport android.os.Bundle;\n\npublic class AudioFxActivity extends Activity\n{\n\n\tprivate static final String TAG = \"AudioFxActivity\";\n\n\n\tprivate MediaPlayer mMediaPlayer;\n\tprivate Visualizer mVisualizer;\n\n\tprivate  BaseVisualizerView mBaseVisualizerView;\n\n\tpublic void onCreate(Bundle savedInstanceState)\n\t{\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetVolumeControlStream(AudioManager.STREAM_MUSIC);\n\n\n\t\tsetContentView(R.layout.main);\n\t\tmBaseVisualizerView = (BaseVisualizerView) findViewById(R.id.visualizerview);\n\n\t\tmMediaPlayer = MediaPlayer.create(this, R.raw.z8806c);\n\n\n\n\n\t\tmMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {\n\t\t\t@Override\n\t\t\tpublic void onPrepared(MediaPlayer mp) {\n\t\t\t\tmMediaPlayer.start();\n\t\t\t\tsetupVisualizerFxAndUi();\n\t\t\t}\n\t\t});\n\n\n\t\tmMediaPlayer.setLooping(true);\n\n\t}\n\n\n\t/**\n\t * 生成一个VisualizerView对象，使音频频谱的波段能够反映到 VisualizerView上\n\t */\n\tprivate void setupVisualizerFxAndUi()\n\t{\n\n\t\tint audioSessionid = mMediaPlayer.getAudioSessionId();\n\t\tSystem.out.println(\"audioSessionid==\"+audioSessionid);\n\t\tmVisualizer = new Visualizer(audioSessionid);\n\t\t// 参数内必须是2的位数\n\t\tmVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);\n\t\t// 设置允许波形表示，并且捕获它\n\t\tmBaseVisualizerView.setVisualizer(mVisualizer);\n\t\tmVisualizer.setEnabled(true);\n\t}\n\n\t@Override\n\tprotected void onPause()\n\t{\n\t\tsuper.onPause();\n\t\tif (isFinishing() && mMediaPlayer != null)\n\t\t{\n\t\t\tmVisualizer.release();\n\t\t\tmMediaPlayer.release();\n\t\t\tmMediaPlayer = null;\n\t\t}\n\t}\n\n}"
  },
  {
    "path": "Android应用源码音乐实时跳动频谱显示Demo/src/main/java/com/terry/AudioFx/BaseVisualizerView.java",
    "content": "package com.terry.AudioFx;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Cap;\nimport android.graphics.Paint.Join;\nimport android.media.audiofx.Visualizer;\nimport android.util.AttributeSet;\nimport android.view.View;\n\npublic class BaseVisualizerView extends View implements Visualizer.OnDataCaptureListener{\n\n    private static final int DN_W = 480;\n    private static final int DN_H = 160;\n    private static final int DN_SL =14;\n    private static final int DN_SW = 6;\n\n    private int hgap = 0;\n    private int vgap = 0;\n    private int levelStep = 0;\n    private float strokeWidth = 0;\n    private float strokeLength = 0;\n\n    /**\n     * It is the max level.\n     */\n    protected final static int MAX_LEVEL = 13;\n\n    /**\n     * It is the cylinder number.\n     */\n    protected final static int CYLINDER_NUM = 20;\n\n    /**\n     * It is the visualizer.\n     */\n    protected Visualizer mVisualizer = null;\n\n    /**\n     * It is the paint which is used to draw to visual effect. \n     */\n    protected Paint mPaint = null;\n\n    /**\n     * It is the buffer of fft.\n     */\n    protected byte[] mData = new byte[CYLINDER_NUM];\n\n    boolean mDataEn = true;\n    /**\n     * It constructs the base visualizer view.\n     * @param context It is the context of the view owner.\n     */\n    public BaseVisualizerView(Context context) {\n        super(context);\n\n        initView();\n    }\n\n    private void initView() {\n        mPaint = new Paint();\n        mPaint.setAntiAlias(true);\n        mPaint.setColor(Color.GREEN);\n//        mPaint.setColor(0xFFd60d25);\n        mPaint.setStrokeJoin(Join.ROUND);\n        mPaint.setStrokeCap(Cap.ROUND);\n    }\n\n    public BaseVisualizerView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        initView();\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n\n        float w, h, xr, yr;\n        w = right - left;\n        h = bottom - top;\n        xr = w / (float)DN_W;\n        yr = h / (float)DN_H;\n\n        strokeWidth = DN_SW * yr;\n        strokeLength = DN_SL * xr;\n        hgap = (int)((w - strokeLength * CYLINDER_NUM) / (CYLINDER_NUM + 1) );\n        vgap = (int)(h / (MAX_LEVEL + 2));\n\n        mPaint.setStrokeWidth(strokeWidth);\n    }\n\n    protected void drawCylinder(Canvas canvas, float x, byte value) {\n        if (value < 0) value = 0;\n        for (int i = 0; i < value; i++) {\n            float y = getHeight() - i * vgap - vgap;\n            canvas.drawLine(x, y, x + strokeLength, y, mPaint);\n        }\n    }\n\n    @Override\n    public void onDraw(Canvas canvas) {\n        for (int i = 0; i < CYLINDER_NUM; i ++) {\n            drawCylinder(canvas, strokeWidth / 2 + hgap + i * (hgap + strokeLength), mData[i]);\n        }\n    }\n\n    /**\n     * It sets the visualizer of the view. DO set the viaulizer to null when exit the program.\n     * @parma visualizer It is the visualizer to set.\n     */\n    public void setVisualizer(Visualizer visualizer) {\n        if (visualizer != null) {\n            if (!visualizer.getEnabled()) {\n                visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[0]);\n            }\n            levelStep = 128 / MAX_LEVEL;\n            visualizer.setDataCaptureListener(this, Visualizer.getMaxCaptureRate() / 2, false, true);\n                \n        } else {\n            \n            if (mVisualizer != null) {\n                mVisualizer.setEnabled(false);\n                mVisualizer.release();\n            }\n        }\n        mVisualizer = visualizer;\n    }\n\n    @Override\n    public void onFftDataCapture(Visualizer visualizer, byte[] fft,\n            int samplingRate) {\n        byte[] model = new byte[fft.length / 2 + 1];\n        if (mDataEn) {\n\n            model[0] = (byte) Math.abs(fft[1]);  \n            int j = 1;  \n            for (int i = 2; i < fft.length;) {\n                model[j] = (byte) Math.hypot(fft[i], fft[i + 1]);  \n                i += 2;  \n                j++;  \n            }\n        } else {\n            for (int i = 0; i < CYLINDER_NUM; i ++) {\n                model[i] = 0;\n            }\n        }\n        for (int i = 0; i < CYLINDER_NUM; i ++) {\n            final byte a = (byte)(Math.abs(model[CYLINDER_NUM  - i]) / levelStep);\n\n            final byte b = mData[i];\n            if (a > b) {\n                mData[i] = a;\n            } else {\n                if (b > 0) {\n                    mData[i]--;\n                }\n            }\n        }\n        postInvalidate();\n    }\n\n    @Override\n    public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform,\n            int samplingRate) {\n        // Do nothing.\n    }\n\n    /**\n     * It enables or disables the data processs.\n     * @param en If this value is true it enables the data process..\n     */\n    public void enableDataProcess(boolean en) {\n        mDataEn = en;\n    }\n}\n"
  },
  {
    "path": "Android应用源码音乐实时跳动频谱显示Demo/src/main/res/layout/main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:orientation=\"vertical\">\n\n    <com.terry.AudioFx.BaseVisualizerView\n        android:id=\"@+id/visualizerview\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"100dp\"\n        />\n</LinearLayout>\n"
  },
  {
    "path": "Android应用源码音乐实时跳动频谱显示Demo/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"hello\">Hello World, AudioFxActivity!</string>\n    <string name=\"app_name\">android跳动频谱</string>\n</resources>\n"
  },
  {
    "path": "EventBus/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "EventBus/build.gradle",
    "content": "apply plugin: 'java'\napply plugin: 'maven'\napply plugin: 'signing'\napply plugin: 'idea'\n\narchivesBaseName = 'eventbus'\ngroup = 'org.greenrobot'\nversion = '3.0.0'\nsourceCompatibility = 1.7\n\ndef isSnapshot = version.endsWith('-SNAPSHOT')\ndef sonatypeRepositoryUrl\nif(isSnapshot) {\n\tsonatypeRepositoryUrl = \"https://oss.sonatype.org/content/repositories/snapshots/\"\n} else {\n\tsonatypeRepositoryUrl = \"https://oss.sonatype.org/service/local/staging/deploy/maven2/\"\n}\n\nrepositories {\n    mavenCentral()\n}\n\n// Still unsupported, see http://issues.gradle.org/browse/GRADLE-784\n// Like this, it won't appear at all in the POM\nconfigurations {\n    provided\n    deployerJars\n}\n\ndependencies {\n    provided 'com.google.android:android:4.1.1.4'\n    provided 'com.google.android:android-test:4.1.1.4'\n    provided 'com.google.android:annotations:4.1.1.4'\n    provided 'com.google.android:support-v4:r7'\n    // deployerJars 'org.apache.maven.wagon:wagon-webdav-jackrabbit:2.4'\n    deployerJars 'org.apache.maven.wagon:wagon-webdav:1.0-beta-2'\n}\n\nsourceSets {\n    main {\n        compileClasspath += configurations.provided\n        java {\n            srcDir 'src'\n            // exclude 'de/greenrobot/event/util/**'\n        }\n    }\n}\n\nidea {\n    module {\n        scopes.PROVIDED.plus += [configurations.provided]\n    }\n}\n\njavadoc {\n    failOnError = false\n    classpath += configurations.provided\n    title = \"EventBus ${version} API\"\n\toptions.bottom = 'Available under the Apache License, Version 2.0 - <i>Copyright &#169; 2012-2016 <a href=\"http://greenrobot.org\">greenrobot.org</a>. All Rights Reserved.</i>'\n}\n\ntask javadocJar(type: Jar, dependsOn: javadoc) {\n    classifier = 'javadoc'\n    from 'build/docs/javadoc'\n}\n\ntask sourcesJar(type: Jar) {\n    from sourceSets.main.allSource\n    classifier = 'sources'\n}\n\nartifacts {\n    archives jar\n    archives javadocJar\n    archives sourcesJar\n}\n\nsigning {\n    if(project.hasProperty('signing.keyId') && project.hasProperty('signing.password') &&\n    project.hasProperty('signing.secretKeyRingFile')) {\n        sign configurations.archives\n    } else {\n        println \"Signing information missing/incomplete for ${project.name}\"\n    }\n}\n\nuploadArchives {\n    repositories {\n        mavenDeployer {\n            if(project.hasProperty('preferedRepo') && project.hasProperty('preferedUsername') \n                && project.hasProperty('preferedPassword')) {\n                configuration = configurations.deployerJars\n                repository(url: preferedRepo) {\n                    authentication(userName: preferedUsername, password: preferedPassword)\n                }\n            } else if(project.hasProperty('sonatypeUsername') && project.hasProperty('sonatypePassword')) {\n                beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }\n                repository(url: sonatypeRepositoryUrl) {\n                    authentication(userName: sonatypeUsername, password: sonatypePassword)\n                }\n            } else {\n                println \"Settings sonatypeUsername/sonatypePassword missing/incomplete for ${project.name}\"\n            }\n            pom.project {\n               name 'EventBus'\n               packaging 'jar'\n               description 'EventBus is a publish/subscribe event bus optimized for Android .'\n               url 'http://greenrobot.org/eventbus/'\n\n               scm {\n                   url 'https://github.com/greenrobot/EventBus'\n                   connection 'scm:git@github.com:greenrobot/EventBus.git'\n                   developerConnection 'scm:git@github.com:greenrobot/EventBus.git'\n               }\n\n               licenses {\n                   license {\n                       name 'The Apache Software License, Version 2.0'\n                       url 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n                       distribution 'repo'\n                   }\n               }\n\n               developers {\n                   developer {\n                       id 'greenrobot'\n                       name 'greenrobot'\n                   }\n               }\n               \n               issueManagement {\n                   system 'GitHub Issues'\n                   url 'https://github.com/greenrobot/EventBus/issues'\n               }\n\n               organization {\n                   name 'greenrobot'\n                   url 'http://greenrobot.org'\n               }\n           }\n        }\n    }\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/AsyncPoster.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\n\n/**\n * Posts events in background.\n * \n * @author Markus\n */\nclass AsyncPoster implements Runnable {\n\n    private final PendingPostQueue queue;\n    private final EventBus eventBus;\n\n    AsyncPoster(EventBus eventBus) {\n        this.eventBus = eventBus;\n        queue = new PendingPostQueue();\n    }\n\n    public void enqueue(Subscription subscription, Object event) {\n        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);\n        queue.enqueue(pendingPost);\n        eventBus.getExecutorService().execute(this);\n    }\n\n    @Override\n    public void run() {\n        PendingPost pendingPost = queue.poll();\n        if(pendingPost == null) {\n            throw new IllegalStateException(\"No pending post available\");\n        }\n        eventBus.invokeSubscriber(pendingPost);\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/BackgroundPoster.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nimport android.util.Log;\n\n/**\n * Posts events in background.\n * \n * @author Markus\n */\nfinal class BackgroundPoster implements Runnable {\n\n    private final PendingPostQueue queue;\n    private final EventBus eventBus;\n\n    private volatile boolean executorRunning;\n\n    BackgroundPoster(EventBus eventBus) {\n        this.eventBus = eventBus;\n        queue = new PendingPostQueue();\n    }\n\n    public void enqueue(Subscription subscription, Object event) {\n        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);\n        synchronized (this) {\n            queue.enqueue(pendingPost);\n            if (!executorRunning) {\n                executorRunning = true;\n                eventBus.getExecutorService().execute(this);\n            }\n        }\n    }\n\n    @Override\n    public void run() {\n        try {\n            try {\n                while (true) {\n                    PendingPost pendingPost = queue.poll(1000);\n                    if (pendingPost == null) {\n                        synchronized (this) {\n                            // Check again, this time in synchronized\n                            pendingPost = queue.poll();\n                            if (pendingPost == null) {\n                                executorRunning = false;\n                                return;\n                            }\n                        }\n                    }\n                    eventBus.invokeSubscriber(pendingPost);\n                }\n            } catch (InterruptedException e) {\n                Log.w(\"Event\", Thread.currentThread().getName() + \" was interruppted\", e);\n            }\n        } finally {\n            executorRunning = false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/EventBus.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nimport android.os.Looper;\nimport android.util.Log;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.ExecutorService;\n\n/**\n * EventBus is a central publish/subscribe event system for Android. Events are posted ({@link #post(Object)}) to the\n * bus, which delivers it to subscribers that have a matching handler method for the event type. To receive events,\n * subscribers must register themselves to the bus using {@link #register(Object)}. Once registered, subscribers\n * receive events until {@link #unregister(Object)} is called. Event handling methods must be annotated by\n * {@link Subscribe}, must be public, return nothing (void), and have exactly one parameter\n * (the event).\n *\n * @author Markus Junginger, greenrobot\n */\npublic class EventBus {\n\n    /** Log tag, apps may override it. */\n    public static String TAG = \"EventBus\";\n\n    static volatile EventBus defaultInstance;\n\n    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();\n    private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();\n\n    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;\n    private final Map<Object, List<Class<?>>> typesBySubscriber;\n    private final Map<Class<?>, Object> stickyEvents;\n\n    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {\n        @Override\n        protected PostingThreadState initialValue() {\n            return new PostingThreadState();\n        }\n    };\n\n    private final HandlerPoster mainThreadPoster;\n    private final BackgroundPoster backgroundPoster;\n    private final AsyncPoster asyncPoster;\n    private final SubscriberMethodFinder subscriberMethodFinder;\n    private final ExecutorService executorService;\n\n    private final boolean throwSubscriberException;\n    private final boolean logSubscriberExceptions;\n    private final boolean logNoSubscriberMessages;\n    private final boolean sendSubscriberExceptionEvent;\n    private final boolean sendNoSubscriberEvent;\n    private final boolean eventInheritance;\n\n    private final int indexCount;\n\n    /** Convenience singleton for apps using a process-wide EventBus instance. */\n    public static EventBus getDefault() {\n        if (defaultInstance == null) {\n            synchronized (EventBus.class) {\n                if (defaultInstance == null) {\n                    defaultInstance = new EventBus();\n                }\n            }\n        }\n        return defaultInstance;\n    }\n\n    public static EventBusBuilder builder() {\n        return new EventBusBuilder();\n    }\n\n    /** For unit test primarily. */\n    public static void clearCaches() {\n        SubscriberMethodFinder.clearCaches();\n        eventTypesCache.clear();\n    }\n\n    /**\n     * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a\n     * central bus, consider {@link #getDefault()}.\n     */\n    public EventBus() {\n        this(DEFAULT_BUILDER);\n    }\n\n    EventBus(EventBusBuilder builder) {\n        subscriptionsByEventType = new HashMap<>();\n        typesBySubscriber = new HashMap<>();\n        stickyEvents = new ConcurrentHashMap<>();\n        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);\n        backgroundPoster = new BackgroundPoster(this);\n        asyncPoster = new AsyncPoster(this);\n        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;\n        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,\n                builder.strictMethodVerification, builder.ignoreGeneratedIndex);\n        logSubscriberExceptions = builder.logSubscriberExceptions;\n        logNoSubscriberMessages = builder.logNoSubscriberMessages;\n        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;\n        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;\n        throwSubscriberException = builder.throwSubscriberException;\n        eventInheritance = builder.eventInheritance;\n        executorService = builder.executorService;\n    }\n\n    /**\n     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they\n     * are no longer interested in receiving events.\n     * <p/>\n     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.\n     * The {@link Subscribe} annotation also allows configuration like {@link\n     * ThreadMode} and priority.\n     */\n    public void register(Object subscriber) {\n        Class<?> subscriberClass = subscriber.getClass();\n        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);\n        synchronized (this) {\n            for (SubscriberMethod subscriberMethod : subscriberMethods) {\n                subscribe(subscriber, subscriberMethod);\n            }\n        }\n    }\n\n    // Must be called in synchronized block\n    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {\n        Class<?> eventType = subscriberMethod.eventType;\n        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);\n        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);\n        if (subscriptions == null) {\n            subscriptions = new CopyOnWriteArrayList<>();\n            subscriptionsByEventType.put(eventType, subscriptions);\n        } else {\n            if (subscriptions.contains(newSubscription)) {\n                throw new EventBusException(\"Subscriber \" + subscriber.getClass() + \" already registered to event \"\n                        + eventType);\n            }\n        }\n\n        int size = subscriptions.size();\n        for (int i = 0; i <= size; i++) {\n            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {\n                subscriptions.add(i, newSubscription);\n                break;\n            }\n        }\n\n        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);\n        if (subscribedEvents == null) {\n            subscribedEvents = new ArrayList<>();\n            typesBySubscriber.put(subscriber, subscribedEvents);\n        }\n        subscribedEvents.add(eventType);\n\n        if (subscriberMethod.sticky) {\n            if (eventInheritance) {\n                // Existing sticky events of all subclasses of eventType have to be considered.\n                // Note: Iterating over all events may be inefficient with lots of sticky events,\n                // thus data structure should be changed to allow a more efficient lookup\n                // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).\n                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();\n                for (Map.Entry<Class<?>, Object> entry : entries) {\n                    Class<?> candidateEventType = entry.getKey();\n                    if (eventType.isAssignableFrom(candidateEventType)) {\n                        Object stickyEvent = entry.getValue();\n                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);\n                    }\n                }\n            } else {\n                Object stickyEvent = stickyEvents.get(eventType);\n                checkPostStickyEventToSubscription(newSubscription, stickyEvent);\n            }\n        }\n    }\n\n    private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {\n        if (stickyEvent != null) {\n            // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)\n            // --> Strange corner case, which we don't take care of here.\n            postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());\n        }\n    }\n\n    public synchronized boolean isRegistered(Object subscriber) {\n        return typesBySubscriber.containsKey(subscriber);\n    }\n\n    /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */\n    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {\n        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);\n        if (subscriptions != null) {\n            int size = subscriptions.size();\n            for (int i = 0; i < size; i++) {\n                Subscription subscription = subscriptions.get(i);\n                if (subscription.subscriber == subscriber) {\n                    subscription.active = false;\n                    subscriptions.remove(i);\n                    i--;\n                    size--;\n                }\n            }\n        }\n    }\n\n    /** Unregisters the given subscriber from all event classes. */\n    public synchronized void unregister(Object subscriber) {\n        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);\n        if (subscribedTypes != null) {\n            for (Class<?> eventType : subscribedTypes) {\n                unsubscribeByEventType(subscriber, eventType);\n            }\n            typesBySubscriber.remove(subscriber);\n        } else {\n            Log.w(TAG, \"Subscriber to unregister was not registered before: \" + subscriber.getClass());\n        }\n    }\n\n    /** Posts the given event to the event bus. */\n    public void post(Object event) {\n        PostingThreadState postingState = currentPostingThreadState.get();\n        List<Object> eventQueue = postingState.eventQueue;\n        eventQueue.add(event);\n\n        if (!postingState.isPosting) {\n            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();\n            postingState.isPosting = true;\n            if (postingState.canceled) {\n                throw new EventBusException(\"Internal error. Abort state was not reset\");\n            }\n            try {\n                while (!eventQueue.isEmpty()) {\n                    postSingleEvent(eventQueue.remove(0), postingState);\n                }\n            } finally {\n                postingState.isPosting = false;\n                postingState.isMainThread = false;\n            }\n        }\n    }\n\n    /**\n     * Called from a subscriber's event handling method, further event delivery will be canceled. Subsequent\n     * subscribers\n     * won't receive the event. Events are usually canceled by higher priority subscribers (see\n     * {@link Subscribe#priority()}). Canceling is restricted to event handling methods running in posting thread\n     * {@link ThreadMode#POSTING}.\n     */\n    public void cancelEventDelivery(Object event) {\n        PostingThreadState postingState = currentPostingThreadState.get();\n        if (!postingState.isPosting) {\n            throw new EventBusException(\n                    \"This method may only be called from inside event handling methods on the posting thread\");\n        } else if (event == null) {\n            throw new EventBusException(\"Event may not be null\");\n        } else if (postingState.event != event) {\n            throw new EventBusException(\"Only the currently handled event may be aborted\");\n        } else if (postingState.subscription.subscriberMethod.threadMode != ThreadMode.POSTING) {\n            throw new EventBusException(\" event handlers may only abort the incoming event\");\n        }\n\n        postingState.canceled = true;\n    }\n\n    /**\n     * Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky\n     * event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.\n     */\n    public void postSticky(Object event) {\n        synchronized (stickyEvents) {\n            stickyEvents.put(event.getClass(), event);\n        }\n        // Should be posted after it is putted, in case the subscriber wants to remove immediately\n        post(event);\n    }\n\n    /**\n     * Gets the most recent sticky event for the given type.\n     *\n     * @see #postSticky(Object)\n     */\n    public <T> T getStickyEvent(Class<T> eventType) {\n        synchronized (stickyEvents) {\n            return eventType.cast(stickyEvents.get(eventType));\n        }\n    }\n\n    /**\n     * Remove and gets the recent sticky event for the given event type.\n     *\n     * @see #postSticky(Object)\n     */\n    public <T> T removeStickyEvent(Class<T> eventType) {\n        synchronized (stickyEvents) {\n            return eventType.cast(stickyEvents.remove(eventType));\n        }\n    }\n\n    /**\n     * Removes the sticky event if it equals to the given event.\n     *\n     * @return true if the events matched and the sticky event was removed.\n     */\n    public boolean removeStickyEvent(Object event) {\n        synchronized (stickyEvents) {\n            Class<?> eventType = event.getClass();\n            Object existingEvent = stickyEvents.get(eventType);\n            if (event.equals(existingEvent)) {\n                stickyEvents.remove(eventType);\n                return true;\n            } else {\n                return false;\n            }\n        }\n    }\n\n    /**\n     * Removes all sticky events.\n     */\n    public void removeAllStickyEvents() {\n        synchronized (stickyEvents) {\n            stickyEvents.clear();\n        }\n    }\n\n    public boolean hasSubscriberForEvent(Class<?> eventClass) {\n        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);\n        if (eventTypes != null) {\n            int countTypes = eventTypes.size();\n            for (int h = 0; h < countTypes; h++) {\n                Class<?> clazz = eventTypes.get(h);\n                CopyOnWriteArrayList<Subscription> subscriptions;\n                synchronized (this) {\n                    subscriptions = subscriptionsByEventType.get(clazz);\n                }\n                if (subscriptions != null && !subscriptions.isEmpty()) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {\n        Class<?> eventClass = event.getClass();\n        boolean subscriptionFound = false;\n        if (eventInheritance) {\n            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);\n            int countTypes = eventTypes.size();\n            for (int h = 0; h < countTypes; h++) {\n                Class<?> clazz = eventTypes.get(h);\n                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);\n            }\n        } else {\n            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);\n        }\n        if (!subscriptionFound) {\n            if (logNoSubscriberMessages) {\n                Log.d(TAG, \"No subscribers registered for event \" + eventClass);\n            }\n            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&\n                    eventClass != SubscriberExceptionEvent.class) {\n                post(new NoSubscriberEvent(this, event));\n            }\n        }\n    }\n\n    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {\n        CopyOnWriteArrayList<Subscription> subscriptions;\n        synchronized (this) {\n            subscriptions = subscriptionsByEventType.get(eventClass);\n        }\n        if (subscriptions != null && !subscriptions.isEmpty()) {\n            for (Subscription subscription : subscriptions) {\n                postingState.event = event;\n                postingState.subscription = subscription;\n                boolean aborted = false;\n                try {\n                    postToSubscription(subscription, event, postingState.isMainThread);\n                    aborted = postingState.canceled;\n                } finally {\n                    postingState.event = null;\n                    postingState.subscription = null;\n                    postingState.canceled = false;\n                }\n                if (aborted) {\n                    break;\n                }\n            }\n            return true;\n        }\n        return false;\n    }\n\n    private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {\n        switch (subscription.subscriberMethod.threadMode) {\n            case POSTING:\n                invokeSubscriber(subscription, event);\n                break;\n            case MAIN:\n                if (isMainThread) {\n                    invokeSubscriber(subscription, event);\n                } else {\n                    mainThreadPoster.enqueue(subscription, event);\n                }\n                break;\n            case BACKGROUND:\n                if (isMainThread) {\n                    backgroundPoster.enqueue(subscription, event);\n                } else {\n                    invokeSubscriber(subscription, event);\n                }\n                break;\n            case ASYNC:\n                asyncPoster.enqueue(subscription, event);\n                break;\n            default:\n                throw new IllegalStateException(\"Unknown thread mode: \" + subscription.subscriberMethod.threadMode);\n        }\n    }\n\n    /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */\n    private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {\n        synchronized (eventTypesCache) {\n            List<Class<?>> eventTypes = eventTypesCache.get(eventClass);\n            if (eventTypes == null) {\n                eventTypes = new ArrayList<>();\n                Class<?> clazz = eventClass;\n                while (clazz != null) {\n                    eventTypes.add(clazz);\n                    addInterfaces(eventTypes, clazz.getInterfaces());\n                    clazz = clazz.getSuperclass();\n                }\n                eventTypesCache.put(eventClass, eventTypes);\n            }\n            return eventTypes;\n        }\n    }\n\n    /** Recurses through super interfaces. */\n    static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {\n        for (Class<?> interfaceClass : interfaces) {\n            if (!eventTypes.contains(interfaceClass)) {\n                eventTypes.add(interfaceClass);\n                addInterfaces(eventTypes, interfaceClass.getInterfaces());\n            }\n        }\n    }\n\n    /**\n     * Invokes the subscriber if the subscriptions is still active. Skipping subscriptions prevents race conditions\n     * between {@link #unregister(Object)} and event delivery. Otherwise the event might be delivered after the\n     * subscriber unregistered. This is particularly important for main thread delivery and registrations bound to the\n     * live cycle of an Activity or Fragment.\n     */\n    void invokeSubscriber(PendingPost pendingPost) {\n        Object event = pendingPost.event;\n        Subscription subscription = pendingPost.subscription;\n        PendingPost.releasePendingPost(pendingPost);\n        if (subscription.active) {\n            invokeSubscriber(subscription, event);\n        }\n    }\n\n    void invokeSubscriber(Subscription subscription, Object event) {\n        try {\n            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);\n        } catch (InvocationTargetException e) {\n            handleSubscriberException(subscription, event, e.getCause());\n        } catch (IllegalAccessException e) {\n            throw new IllegalStateException(\"Unexpected exception\", e);\n        }\n    }\n\n    private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {\n        if (event instanceof SubscriberExceptionEvent) {\n            if (logSubscriberExceptions) {\n                // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log\n                Log.e(TAG, \"SubscriberExceptionEvent subscriber \" + subscription.subscriber.getClass()\n                        + \" threw an exception\", cause);\n                SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;\n                Log.e(TAG, \"Initial event \" + exEvent.causingEvent + \" caused exception in \"\n                        + exEvent.causingSubscriber, exEvent.throwable);\n            }\n        } else {\n            if (throwSubscriberException) {\n                throw new EventBusException(\"Invoking subscriber failed\", cause);\n            }\n            if (logSubscriberExceptions) {\n                Log.e(TAG, \"Could not dispatch event: \" + event.getClass() + \" to subscribing class \"\n                        + subscription.subscriber.getClass(), cause);\n            }\n            if (sendSubscriberExceptionEvent) {\n                SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event,\n                        subscription.subscriber);\n                post(exEvent);\n            }\n        }\n    }\n\n    /** For ThreadLocal, much faster to set (and get multiple values). */\n    final static class PostingThreadState {\n        final List<Object> eventQueue = new ArrayList<Object>();\n        boolean isPosting;\n        boolean isMainThread;\n        Subscription subscription;\n        Object event;\n        boolean canceled;\n    }\n\n    ExecutorService getExecutorService() {\n        return executorService;\n    }\n\n    // Just an idea: we could provide a callback to post() to be notified, an alternative would be events, of course...\n    /* public */interface PostCallback {\n        void onPostCompleted(List<SubscriberExceptionEvent> exceptionEvents);\n    }\n\n    @Override\n    public String toString() {\n        return \"EventBus[indexCount=\" + indexCount + \", eventInheritance=\" + eventInheritance + \"]\";\n    }\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/EventBusBuilder.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nimport org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n/**\n * Creates EventBus instances with custom parameters and also allows to install a custom default EventBus instance.\n * Create a new builder using {@link EventBus#builder()}.\n */\npublic class EventBusBuilder {\n    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();\n\n    boolean logSubscriberExceptions = true;\n    boolean logNoSubscriberMessages = true;\n    boolean sendSubscriberExceptionEvent = true;\n    boolean sendNoSubscriberEvent = true;\n    boolean throwSubscriberException;\n    boolean eventInheritance = true;\n    boolean ignoreGeneratedIndex;\n    boolean strictMethodVerification;\n    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;\n    List<Class<?>> skipMethodVerificationForClasses;\n    List<SubscriberInfoIndex> subscriberInfoIndexes;\n\n    EventBusBuilder() {\n    }\n\n    /** Default: true */\n    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {\n        this.logSubscriberExceptions = logSubscriberExceptions;\n        return this;\n    }\n\n    /** Default: true */\n    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {\n        this.logNoSubscriberMessages = logNoSubscriberMessages;\n        return this;\n    }\n\n    /** Default: true */\n    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {\n        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;\n        return this;\n    }\n\n    /** Default: true */\n    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {\n        this.sendNoSubscriberEvent = sendNoSubscriberEvent;\n        return this;\n    }\n\n    /**\n     * Fails if an subscriber throws an exception (default: false).\n     * <p/>\n     * Tip: Use this with BuildConfig.DEBUG to let the app crash in DEBUG mode (only). This way, you won't miss\n     * exceptions during development.\n     */\n    public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {\n        this.throwSubscriberException = throwSubscriberException;\n        return this;\n    }\n\n    /**\n     * By default, EventBus considers the event class hierarchy (subscribers to super classes will be notified).\n     * Switching this feature off will improve posting of events. For simple event classes extending Object directly,\n     * we measured a speed up of 20% for event posting. For more complex event hierarchies, the speed up should be\n     * >20%.\n     * <p/>\n     * However, keep in mind that event posting usually consumes just a small proportion of CPU time inside an app,\n     * unless it is posting at high rates, e.g. hundreds/thousands of events per second.\n     */\n    public EventBusBuilder eventInheritance(boolean eventInheritance) {\n        this.eventInheritance = eventInheritance;\n        return this;\n    }\n\n\n    /**\n     * Provide a custom thread pool to EventBus used for async and background event delivery. This is an advanced\n     * setting to that can break things: ensure the given ExecutorService won't get stuck to avoid undefined behavior.\n     */\n    public EventBusBuilder executorService(ExecutorService executorService) {\n        this.executorService = executorService;\n        return this;\n    }\n\n    /**\n     * Method name verification is done for methods starting with onEvent to avoid typos; using this method you can\n     * exclude subscriber classes from this check. Also disables checks for method modifiers (public, not static nor\n     * abstract).\n     */\n    public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {\n        if (skipMethodVerificationForClasses == null) {\n            skipMethodVerificationForClasses = new ArrayList<>();\n        }\n        skipMethodVerificationForClasses.add(clazz);\n        return this;\n    }\n\n    /** Forces the use of reflection even if there's a generated index (default: false). */\n    public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {\n        this.ignoreGeneratedIndex = ignoreGeneratedIndex;\n        return this;\n    }\n\n    /** Enables strict method verification (default: false). */\n    public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {\n        this.strictMethodVerification = strictMethodVerification;\n        return this;\n    }\n\n    /** Adds an index generated by EventBus' annotation preprocessor. */\n    public EventBusBuilder addIndex(SubscriberInfoIndex index) {\n        if(subscriberInfoIndexes == null) {\n            subscriberInfoIndexes = new ArrayList<>();\n        }\n        subscriberInfoIndexes.add(index);\n        return this;\n    }\n\n    /**\n     * Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be\n     * done only once before the first usage of the default EventBus.\n     *\n     * @throws EventBusException if there's already a default EventBus instance in place\n     */\n    public EventBus installDefaultEventBus() {\n        synchronized (EventBus.class) {\n            if (EventBus.defaultInstance != null) {\n                throw new EventBusException(\"Default instance already exists.\" +\n                        \" It may be only set once before it's used the first time to ensure consistent behavior.\");\n            }\n            EventBus.defaultInstance = build();\n            return EventBus.defaultInstance;\n        }\n    }\n\n    /** Builds an EventBus based on the current configuration. */\n    public EventBus build() {\n        return new EventBus(this);\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/EventBusException.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\n/**\n * An {@link RuntimeException} thrown in cases something went wrong inside EventBus.\n * \n * @author Markus\n * \n */\npublic class EventBusException extends RuntimeException {\n\n    private static final long serialVersionUID = -2912559384646531479L;\n\n    public EventBusException(String detailMessage) {\n        super(detailMessage);\n    }\n\n    public EventBusException(Throwable throwable) {\n        super(throwable);\n    }\n\n    public EventBusException(String detailMessage, Throwable throwable) {\n        super(detailMessage, throwable);\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/HandlerPoster.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.SystemClock;\n\nfinal class HandlerPoster extends Handler {\n\n    private final PendingPostQueue queue;\n    private final int maxMillisInsideHandleMessage;\n    private final EventBus eventBus;\n    private boolean handlerActive;\n\n    HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {\n        super(looper);\n        this.eventBus = eventBus;\n        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;\n        queue = new PendingPostQueue();\n    }\n\n    void enqueue(Subscription subscription, Object event) {\n        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);\n        synchronized (this) {\n            queue.enqueue(pendingPost);\n            if (!handlerActive) {\n                handlerActive = true;\n                if (!sendMessage(obtainMessage())) {\n                    throw new EventBusException(\"Could not send handler message\");\n                }\n            }\n        }\n    }\n\n    @Override\n    public void handleMessage(Message msg) {\n        boolean rescheduled = false;\n        try {\n            long started = SystemClock.uptimeMillis();\n            while (true) {\n                PendingPost pendingPost = queue.poll();\n                if (pendingPost == null) {\n                    synchronized (this) {\n                        // Check again, this time in synchronized\n                        pendingPost = queue.poll();\n                        if (pendingPost == null) {\n                            handlerActive = false;\n                            return;\n                        }\n                    }\n                }\n                eventBus.invokeSubscriber(pendingPost);\n                long timeInMethod = SystemClock.uptimeMillis() - started;\n                if (timeInMethod >= maxMillisInsideHandleMessage) {\n                    if (!sendMessage(obtainMessage())) {\n                        throw new EventBusException(\"Could not send handler message\");\n                    }\n                    rescheduled = true;\n                    return;\n                }\n            }\n        } finally {\n            handlerActive = rescheduled;\n        }\n    }\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/NoSubscriberEvent.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\n/**\n * This Event is posted by EventBus when no subscriber is found for a posted event.\n * \n * @author Markus\n */\npublic final class NoSubscriberEvent {\n    /** The {@link EventBus} instance to with the original event was posted to. */\n    public final EventBus eventBus;\n\n    /** The original event that could not be delivered to any subscriber. */\n    public final Object originalEvent;\n\n    public NoSubscriberEvent(EventBus eventBus, Object originalEvent) {\n        this.eventBus = eventBus;\n        this.originalEvent = originalEvent;\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/PendingPost.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nfinal class PendingPost {\n    private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();\n\n    Object event;\n    Subscription subscription;\n    PendingPost next;\n\n    private PendingPost(Object event, Subscription subscription) {\n        this.event = event;\n        this.subscription = subscription;\n    }\n\n    static PendingPost obtainPendingPost(Subscription subscription, Object event) {\n        synchronized (pendingPostPool) {\n            int size = pendingPostPool.size();\n            if (size > 0) {\n                PendingPost pendingPost = pendingPostPool.remove(size - 1);\n                pendingPost.event = event;\n                pendingPost.subscription = subscription;\n                pendingPost.next = null;\n                return pendingPost;\n            }\n        }\n        return new PendingPost(event, subscription);\n    }\n\n    static void releasePendingPost(PendingPost pendingPost) {\n        pendingPost.event = null;\n        pendingPost.subscription = null;\n        pendingPost.next = null;\n        synchronized (pendingPostPool) {\n            // Don't let the pool grow indefinitely\n            if (pendingPostPool.size() < 10000) {\n                pendingPostPool.add(pendingPost);\n            }\n        }\n    }\n\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/PendingPostQueue.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus;\n\nfinal class PendingPostQueue {\n    private PendingPost head;\n    private PendingPost tail;\n\n    synchronized void enqueue(PendingPost pendingPost) {\n        if (pendingPost == null) {\n            throw new NullPointerException(\"null cannot be enqueued\");\n        }\n        if (tail != null) {\n            tail.next = pendingPost;\n            tail = pendingPost;\n        } else if (head == null) {\n            head = tail = pendingPost;\n        } else {\n            throw new IllegalStateException(\"Head present, but no tail\");\n        }\n        notifyAll();\n    }\n\n    synchronized PendingPost poll() {\n        PendingPost pendingPost = head;\n        if (head != null) {\n            head = head.next;\n            if (head == null) {\n                tail = null;\n            }\n        }\n        return pendingPost;\n    }\n\n    synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {\n        if (head == null) {\n            wait(maxMillisToWait);\n        }\n        return poll();\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/Subscribe.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus;\n\n\nimport java.lang.annotation.Documented;\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Documented\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ElementType.METHOD})\npublic @interface Subscribe {\n    ThreadMode threadMode() default ThreadMode.POSTING;\n\n    /**\n     * If true, delivers the most recent sticky event (posted with\n     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).\n     */\n    boolean sticky() default false;\n\n    /** Subscriber priority to influence the order of event delivery.\n     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before\n     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of\n     * delivery among subscribers with different {@link ThreadMode}s! */\n    int priority() default 0;\n}\n\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/SubscriberExceptionEvent.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\n/**\n * This Event is posted by EventBus when an exception occurs inside a subscriber's event handling method.\n * \n * @author Markus\n */\npublic final class SubscriberExceptionEvent {\n    /** The {@link EventBus} instance to with the original event was posted to. */\n    public final EventBus eventBus;\n\n    /** The Throwable thrown by a subscriber. */\n    public final Throwable throwable;\n\n    /** The original event that could not be delivered to any subscriber. */\n    public final Object causingEvent;\n\n    /** The subscriber that threw the Throwable. */\n    public final Object causingSubscriber;\n\n    public SubscriberExceptionEvent(EventBus eventBus, Throwable throwable, Object causingEvent,\n            Object causingSubscriber) {\n        this.eventBus = eventBus;\n        this.throwable = throwable;\n        this.causingEvent = causingEvent;\n        this.causingSubscriber = causingSubscriber;\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/SubscriberMethod.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nimport java.lang.reflect.Method;\n\n/** Used internally by EventBus and generated subscriber indexes. */\npublic class SubscriberMethod {\n    final Method method;\n    final ThreadMode threadMode;\n    final Class<?> eventType;\n    final int priority;\n    final boolean sticky;\n    /** Used for efficient comparison */\n    String methodString;\n\n    public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {\n        this.method = method;\n        this.threadMode = threadMode;\n        this.eventType = eventType;\n        this.priority = priority;\n        this.sticky = sticky;\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other == this) {\n            return true;\n        } else if (other instanceof SubscriberMethod) {\n            checkMethodString();\n            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;\n            otherSubscriberMethod.checkMethodString();\n            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6\n            return methodString.equals(otherSubscriberMethod.methodString);\n        } else {\n            return false;\n        }\n    }\n\n    private synchronized void checkMethodString() {\n        if (methodString == null) {\n            // Method.toString has more overhead, just take relevant parts of the method\n            StringBuilder builder = new StringBuilder(64);\n            builder.append(method.getDeclaringClass().getName());\n            builder.append('#').append(method.getName());\n            builder.append('(').append(eventType.getName());\n            methodString = builder.toString();\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        return method.hashCode();\n    }\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/SubscriberMethodFinder.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nimport org.greenrobot.eventbus.meta.SubscriberInfo;\nimport org.greenrobot.eventbus.meta.SubscriberInfoIndex;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nclass SubscriberMethodFinder {\n    /*\n     * In newer class files, compilers may add methods. Those are called bridge or synthetic methods.\n     * EventBus must ignore both. There modifiers are not public but defined in the Java class file format:\n     * http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1\n     */\n    private static final int BRIDGE = 0x40;\n    private static final int SYNTHETIC = 0x1000;\n\n    private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;\n    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();\n\n    private List<SubscriberInfoIndex> subscriberInfoIndexes;\n    private final boolean strictMethodVerification;\n    private final boolean ignoreGeneratedIndex;\n\n    private static final int POOL_SIZE = 4;\n    private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];\n\n    SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,\n                           boolean ignoreGeneratedIndex) {\n        this.subscriberInfoIndexes = subscriberInfoIndexes;\n        this.strictMethodVerification = strictMethodVerification;\n        this.ignoreGeneratedIndex = ignoreGeneratedIndex;\n    }\n\n    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {\n        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);\n        if (subscriberMethods != null) {\n            return subscriberMethods;\n        }\n\n        if (ignoreGeneratedIndex) {\n            subscriberMethods = findUsingReflection(subscriberClass);\n        } else {\n            subscriberMethods = findUsingInfo(subscriberClass);\n        }\n        if (subscriberMethods.isEmpty()) {\n            throw new EventBusException(\"Subscriber \" + subscriberClass\n                    + \" and its super classes have no public methods with the @Subscribe annotation\");\n        } else {\n            METHOD_CACHE.put(subscriberClass, subscriberMethods);\n            return subscriberMethods;\n        }\n    }\n\n    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {\n        FindState findState = prepareFindState();\n        findState.initForSubscriber(subscriberClass);\n        while (findState.clazz != null) {\n            findState.subscriberInfo = getSubscriberInfo(findState);\n            if (findState.subscriberInfo != null) {\n                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();\n                for (SubscriberMethod subscriberMethod : array) {\n                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {\n                        findState.subscriberMethods.add(subscriberMethod);\n                    }\n                }\n            } else {\n                findUsingReflectionInSingleClass(findState);\n            }\n            findState.moveToSuperclass();\n        }\n        return getMethodsAndRelease(findState);\n    }\n\n    private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {\n        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);\n        findState.recycle();\n        synchronized (FIND_STATE_POOL) {\n            for (int i = 0; i < POOL_SIZE; i++) {\n                if (FIND_STATE_POOL[i] == null) {\n                    FIND_STATE_POOL[i] = findState;\n                    break;\n                }\n            }\n        }\n        return subscriberMethods;\n    }\n\n    private FindState prepareFindState() {\n        synchronized (FIND_STATE_POOL) {\n            for (int i = 0; i < POOL_SIZE; i++) {\n                FindState state = FIND_STATE_POOL[i];\n                if (state != null) {\n                    FIND_STATE_POOL[i] = null;\n                    return state;\n                }\n            }\n        }\n        return new FindState();\n    }\n\n    private SubscriberInfo getSubscriberInfo(FindState findState) {\n        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {\n            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();\n            if (findState.clazz == superclassInfo.getSubscriberClass()) {\n                return superclassInfo;\n            }\n        }\n        if (subscriberInfoIndexes != null) {\n            for (SubscriberInfoIndex index : subscriberInfoIndexes) {\n                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);\n                if (info != null) {\n                    return info;\n                }\n            }\n        }\n        return null;\n    }\n\n    private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {\n        FindState findState = prepareFindState();\n        findState.initForSubscriber(subscriberClass);\n        while (findState.clazz != null) {\n            findUsingReflectionInSingleClass(findState);\n            findState.moveToSuperclass();\n        }\n        return getMethodsAndRelease(findState);\n    }\n\n    private void findUsingReflectionInSingleClass(FindState findState) {\n        Method[] methods;\n        try {\n            // This is faster than getMethods, especially when subscribers are fat classes like Activities\n            methods = findState.clazz.getDeclaredMethods();\n        } catch (Throwable th) {\n            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149\n            methods = findState.clazz.getMethods();\n            findState.skipSuperClasses = true;\n        }\n        for (Method method : methods) {\n            int modifiers = method.getModifiers();\n            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {\n                Class<?>[] parameterTypes = method.getParameterTypes();\n                if (parameterTypes.length == 1) {\n                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);\n                    if (subscribeAnnotation != null) {\n                        Class<?> eventType = parameterTypes[0];\n                        if (findState.checkAdd(method, eventType)) {\n                            ThreadMode threadMode = subscribeAnnotation.threadMode();\n                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,\n                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));\n                        }\n                    }\n                } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {\n                    String methodName = method.getDeclaringClass().getName() + \".\" + method.getName();\n                    throw new EventBusException(\"@Subscribe method \" + methodName +\n                            \"must have exactly 1 parameter but has \" + parameterTypes.length);\n                }\n            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {\n                String methodName = method.getDeclaringClass().getName() + \".\" + method.getName();\n                throw new EventBusException(methodName +\n                        \" is a illegal @Subscribe method: must be public, non-static, and non-abstract\");\n            }\n        }\n    }\n\n    static void clearCaches() {\n        METHOD_CACHE.clear();\n    }\n\n    static class FindState {\n        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();\n        final Map<Class, Object> anyMethodByEventType = new HashMap<>();\n        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();\n        final StringBuilder methodKeyBuilder = new StringBuilder(128);\n\n        Class<?> subscriberClass;\n        Class<?> clazz;\n        boolean skipSuperClasses;\n        SubscriberInfo subscriberInfo;\n\n        void initForSubscriber(Class<?> subscriberClass) {\n            this.subscriberClass = clazz = subscriberClass;\n            skipSuperClasses = false;\n            subscriberInfo = null;\n        }\n\n        void recycle() {\n            subscriberMethods.clear();\n            anyMethodByEventType.clear();\n            subscriberClassByMethodKey.clear();\n            methodKeyBuilder.setLength(0);\n            subscriberClass = null;\n            clazz = null;\n            skipSuperClasses = false;\n            subscriberInfo = null;\n        }\n\n        boolean checkAdd(Method method, Class<?> eventType) {\n            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.\n            // Usually a subscriber doesn't have methods listening to the same event type.\n            Object existing = anyMethodByEventType.put(eventType, method);\n            if (existing == null) {\n                return true;\n            } else {\n                if (existing instanceof Method) {\n                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {\n                        // Paranoia check\n                        throw new IllegalStateException();\n                    }\n                    // Put any non-Method object to \"consume\" the existing Method\n                    anyMethodByEventType.put(eventType, this);\n                }\n                return checkAddWithMethodSignature(method, eventType);\n            }\n        }\n\n        private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {\n            methodKeyBuilder.setLength(0);\n            methodKeyBuilder.append(method.getName());\n            methodKeyBuilder.append('>').append(eventType.getName());\n\n            String methodKey = methodKeyBuilder.toString();\n            Class<?> methodClass = method.getDeclaringClass();\n            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);\n            if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {\n                // Only add if not already found in a sub class\n                return true;\n            } else {\n                // Revert the put, old class is further down the class hierarchy\n                subscriberClassByMethodKey.put(methodKey, methodClassOld);\n                return false;\n            }\n        }\n\n        void moveToSuperclass() {\n            if (skipSuperClasses) {\n                clazz = null;\n            } else {\n                clazz = clazz.getSuperclass();\n                String clazzName = clazz.getName();\n                /** Skip system classes, this just degrades performance. */\n                if (clazzName.startsWith(\"java.\") || clazzName.startsWith(\"javax.\") || clazzName.startsWith(\"android.\")) {\n                    clazz = null;\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/Subscription.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\nfinal class Subscription {\n    final Object subscriber;\n    final SubscriberMethod subscriberMethod;\n    /**\n     * Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery\n     * {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.\n     */\n    volatile boolean active;\n\n    Subscription(Object subscriber, SubscriberMethod subscriberMethod) {\n        this.subscriber = subscriber;\n        this.subscriberMethod = subscriberMethod;\n        active = true;\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (other instanceof Subscription) {\n            Subscription otherSubscription = (Subscription) other;\n            return subscriber == otherSubscription.subscriber\n                    && subscriberMethod.equals(otherSubscription.subscriberMethod);\n        } else {\n            return false;\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        return subscriber.hashCode() + subscriberMethod.methodString.hashCode();\n    }\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/ThreadMode.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus;\n\n/**\n * Each event handler method has a thread mode, which determines in which thread the method is to be called by EventBus.\n * EventBus takes care of threading independently from the posting thread.\n * \n * @see EventBus#register(Object)\n * @author Markus\n */\npublic enum ThreadMode {\n    /**\n     * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery\n     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for\n     * simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers\n     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.\n     */\n    POSTING,\n\n    /**\n     * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is\n     * the main thread, event handler methods will be called directly. Event handlers using this mode must return\n     * quickly to avoid blocking the main thread.\n     */\n    MAIN,\n\n    /**\n     * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods\n     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single\n     * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to\n     * return quickly to avoid blocking the background thread.\n     */\n    BACKGROUND,\n\n    /**\n     * Event handler methods are called in a separate thread. This is always independent from the posting thread and the\n     * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should\n     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number\n     * of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus\n     * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.\n     */\n    ASYNC\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/meta/AbstractSubscriberInfo.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus.meta;\n\nimport org.greenrobot.eventbus.EventBusException;\nimport org.greenrobot.eventbus.SubscriberMethod;\nimport org.greenrobot.eventbus.ThreadMode;\n\nimport java.lang.reflect.Method;\n\n/** Base class for generated subscriber meta info classes created by annotation processing. */\npublic abstract class AbstractSubscriberInfo implements SubscriberInfo {\n    private final Class subscriberClass;\n    private final Class<? extends SubscriberInfo> superSubscriberInfoClass;\n    private final boolean shouldCheckSuperclass;\n\n    protected AbstractSubscriberInfo(Class subscriberClass, Class<? extends SubscriberInfo> superSubscriberInfoClass,\n                                     boolean shouldCheckSuperclass) {\n        this.subscriberClass = subscriberClass;\n        this.superSubscriberInfoClass = superSubscriberInfoClass;\n        this.shouldCheckSuperclass = shouldCheckSuperclass;\n    }\n\n    @Override\n    public Class getSubscriberClass() {\n        return subscriberClass;\n    }\n\n    @Override\n    public SubscriberInfo getSuperSubscriberInfo() {\n        if(superSubscriberInfoClass == null) {\n            return null;\n        }\n        try {\n            return superSubscriberInfoClass.newInstance();\n        } catch (InstantiationException | IllegalAccessException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public boolean shouldCheckSuperclass() {\n        return shouldCheckSuperclass;\n    }\n\n    protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType) {\n        return createSubscriberMethod(methodName, eventType, ThreadMode.POSTING, 0, false);\n    }\n\n    protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode) {\n        return createSubscriberMethod(methodName, eventType, threadMode, 0, false);\n    }\n\n    protected SubscriberMethod createSubscriberMethod(String methodName, Class<?> eventType, ThreadMode threadMode,\n                                                      int priority, boolean sticky) {\n        try {\n            Method method = subscriberClass.getDeclaredMethod(methodName, eventType);\n            return new SubscriberMethod(method, eventType, threadMode, priority, sticky);\n        } catch (NoSuchMethodException e) {\n            throw new EventBusException(\"Could not find subscriber method in \" + subscriberClass +\n                    \". Maybe a missing ProGuard rule?\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/meta/SimpleSubscriberInfo.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus.meta;\n\nimport org.greenrobot.eventbus.SubscriberMethod;\n\n/**\n * Uses {@link SubscriberMethodInfo} objects to create {@link org.greenrobot.eventbus.SubscriberMethod} objects on demand.\n */\npublic class SimpleSubscriberInfo extends AbstractSubscriberInfo {\n\n    private final SubscriberMethodInfo[] methodInfos;\n\n    public SimpleSubscriberInfo(Class subscriberClass, boolean shouldCheckSuperclass, SubscriberMethodInfo[] methodInfos) {\n        super(subscriberClass, null, shouldCheckSuperclass);\n        this.methodInfos = methodInfos;\n    }\n\n    @Override\n    public synchronized SubscriberMethod[] getSubscriberMethods() {\n        int length = methodInfos.length;\n        SubscriberMethod[] methods = new SubscriberMethod[length];\n        for (int i = 0; i < length; i++) {\n            SubscriberMethodInfo info = methodInfos[i];\n            methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,\n                    info.priority, info.sticky);\n        }\n        return methods;\n    }\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfo.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus.meta;\n\nimport org.greenrobot.eventbus.SubscriberMethod;\n\n/** Base class for generated index classes created by annotation processing. */\npublic interface SubscriberInfo {\n    Class<?> getSubscriberClass();\n\n    SubscriberMethod[] getSubscriberMethods();\n\n    SubscriberInfo getSuperSubscriberInfo();\n\n    boolean shouldCheckSuperclass();\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus.meta;\n\n/**\n * Interface for generated indexes.\n */\npublic interface SubscriberInfoIndex {\n    SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/meta/SubscriberMethodInfo.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus.meta;\n\nimport org.greenrobot.eventbus.ThreadMode;\n\npublic class SubscriberMethodInfo {\n    final String methodName;\n    final ThreadMode threadMode;\n    final Class<?> eventType;\n    final int priority;\n    final boolean sticky;\n\n    public SubscriberMethodInfo(String methodName, Class<?> eventType, ThreadMode threadMode,\n                                int priority, boolean sticky) {\n        this.methodName = methodName;\n        this.threadMode = threadMode;\n        this.eventType = eventType;\n        this.priority = priority;\n        this.sticky = sticky;\n    }\n\n    public SubscriberMethodInfo(String methodName, Class<?> eventType) {\n        this(methodName, eventType, ThreadMode.POSTING, 0, false);\n    }\n\n    public SubscriberMethodInfo(String methodName, Class<?> eventType, ThreadMode threadMode) {\n        this(methodName, eventType, threadMode, 0, false);\n    }\n\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/AsyncExecutor.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus.util;\n\nimport android.app.Activity;\nimport android.util.Log;\n\nimport org.greenrobot.eventbus.EventBus;\n\nimport java.lang.reflect.Constructor;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\n\n/**\n * Executes an {@link RunnableEx} using a thread pool. Thrown exceptions are propagated by posting failure events of any\n * given type (default is {@link ThrowableFailureEvent}).\n * \n * @author Markus\n */\npublic class AsyncExecutor {\n\n    public static class Builder {\n        private Executor threadPool;\n        private Class<?> failureEventType;\n        private EventBus eventBus;\n\n        private Builder() {\n        }\n\n        public Builder threadPool(Executor threadPool) {\n            this.threadPool = threadPool;\n            return this;\n        }\n\n        public Builder failureEventType(Class<?> failureEventType) {\n            this.failureEventType = failureEventType;\n            return this;\n        }\n\n        public Builder eventBus(EventBus eventBus) {\n            this.eventBus = eventBus;\n            return this;\n        }\n\n        public AsyncExecutor build() {\n            return buildForScope(null);\n        }\n\n        public AsyncExecutor buildForActivityScope(Activity activity) {\n            return buildForScope(activity.getClass());\n        }\n        \n        public AsyncExecutor buildForScope(Object executionContext) {\n            if (eventBus == null) {\n                eventBus = EventBus.getDefault();\n            }\n            if (threadPool == null) {\n                threadPool = Executors.newCachedThreadPool();\n            }\n            if (failureEventType == null) {\n                failureEventType = ThrowableFailureEvent.class;\n            }\n            return new AsyncExecutor(threadPool, eventBus, failureEventType, executionContext);\n        }\n    }\n\n    /** Like {@link Runnable}, but the run method may throw an exception. */\n    public interface RunnableEx {\n        void run() throws Exception;\n    }\n\n    public static Builder builder() {\n        return new Builder();\n    }\n\n    public static AsyncExecutor create() {\n        return new Builder().build();\n    }\n\n    private final Executor threadPool;\n    private final Constructor<?> failureEventConstructor;\n    private final EventBus eventBus;\n    private final Object scope;\n\n    private AsyncExecutor(Executor threadPool, EventBus eventBus, Class<?> failureEventType, Object scope) {\n        this.threadPool = threadPool;\n        this.eventBus = eventBus;\n        this.scope = scope;\n        try {\n            failureEventConstructor = failureEventType.getConstructor(Throwable.class);\n        } catch (NoSuchMethodException e) {\n            throw new RuntimeException(\n                    \"Failure event class must have a constructor with one parameter of type Throwable\", e);\n        }\n    }\n\n    /** Posts an failure event if the given {@link RunnableEx} throws an Exception. */\n    public void execute(final RunnableEx runnable) {\n        threadPool.execute(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    runnable.run();\n                } catch (Exception e) {\n                    Object event;\n                    try {\n                        event = failureEventConstructor.newInstance(e);\n                    } catch (Exception e1) {\n                        Log.e(EventBus.TAG, \"Original exception:\", e);\n                        throw new RuntimeException(\"Could not create failure event\", e1);\n                    }\n                    if (event instanceof HasExecutionScope) {\n                        ((HasExecutionScope) event).setExecutionScope(scope);\n                    }\n                    eventBus.post(event);\n                }\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/ErrorDialogConfig.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus.util;\n\nimport android.content.res.Resources;\nimport android.util.Log;\n\nimport org.greenrobot.eventbus.EventBus;\n\npublic class ErrorDialogConfig {\n    final Resources resources;\n    final int defaultTitleId;\n    final int defaultErrorMsgId;\n    final ExceptionToResourceMapping mapping;\n\n    EventBus eventBus;\n    boolean logExceptions = true;\n    String tagForLoggingExceptions;\n    int defaultDialogIconId;\n    Class<?> defaultEventTypeOnDialogClosed;\n\n    public ErrorDialogConfig(Resources resources, int defaultTitleId, int defaultMsgId) {\n        this.resources = resources;\n        this.defaultTitleId = defaultTitleId;\n        this.defaultErrorMsgId = defaultMsgId;\n        mapping = new ExceptionToResourceMapping();\n    }\n\n    public ErrorDialogConfig addMapping(Class<? extends Throwable> clazz, int msgId) {\n        mapping.addMapping(clazz, msgId);\n        return this;\n    }\n\n    public int getMessageIdForThrowable(final Throwable throwable) {\n        Integer resId = mapping.mapThrowable(throwable);\n        if (resId != null) {\n            return resId;\n        } else {\n            Log.d(EventBus.TAG, \"No specific message ressource ID found for \" + throwable);\n            return defaultErrorMsgId;\n        }\n    }\n\n    public void setDefaultDialogIconId(int defaultDialogIconId) {\n        this.defaultDialogIconId = defaultDialogIconId;\n    }\n\n    public void setDefaultEventTypeOnDialogClosed(Class<?> defaultEventTypeOnDialogClosed) {\n        this.defaultEventTypeOnDialogClosed = defaultEventTypeOnDialogClosed;\n    }\n\n    public void disableExceptionLogging() {\n        logExceptions = false;\n    }\n\n    public void setTagForLoggingExceptions(String tagForLoggingExceptions) {\n        this.tagForLoggingExceptions = tagForLoggingExceptions;\n    }\n\n    public void setEventBus(EventBus eventBus) {\n        this.eventBus = eventBus;\n    }\n\n    /** eventBus!=null ? eventBus: EventBus.getDefault() */\n    EventBus getEventBus() {\n        return eventBus!=null ? eventBus: EventBus.getDefault();\n    }\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/ErrorDialogFragmentFactory.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus.util;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\n\n/**\n * Factory to allow injecting a more complex exception mapping; typically you would subclass one of {@link Honeycomb} or\n * {@link Support}.\n */\npublic abstract class ErrorDialogFragmentFactory<T> {\n    protected final ErrorDialogConfig config;\n\n    protected ErrorDialogFragmentFactory(ErrorDialogConfig config) {\n        this.config = config;\n    }\n\n    /**\n     * Prepares the fragment's arguments and creates the fragment. May be overridden to provide custom error fragments.\n     */\n    protected T prepareErrorFragment(ThrowableFailureEvent event, boolean finishAfterDialog,\n            Bundle argumentsForErrorDialog) {\n        if (event.isSuppressErrorUi()) {\n            // Show nothing by default\n            return null;\n        }\n        Bundle bundle;\n        if (argumentsForErrorDialog != null) {\n            bundle = (Bundle) argumentsForErrorDialog.clone();\n        } else {\n            bundle = new Bundle();\n        }\n\n        if (!bundle.containsKey(ErrorDialogManager.KEY_TITLE)) {\n            String title = getTitleFor(event, bundle);\n            bundle.putString(ErrorDialogManager.KEY_TITLE, title);\n        }\n        if (!bundle.containsKey(ErrorDialogManager.KEY_MESSAGE)) {\n            String message = getMessageFor(event, bundle);\n            bundle.putString(ErrorDialogManager.KEY_MESSAGE, message);\n        }\n        if (!bundle.containsKey(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG)) {\n            bundle.putBoolean(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG, finishAfterDialog);\n        }\n        if (!bundle.containsKey(ErrorDialogManager.KEY_EVENT_TYPE_ON_CLOSE)\n                && config.defaultEventTypeOnDialogClosed != null) {\n            bundle.putSerializable(ErrorDialogManager.KEY_EVENT_TYPE_ON_CLOSE, config.defaultEventTypeOnDialogClosed);\n        }\n        if (!bundle.containsKey(ErrorDialogManager.KEY_ICON_ID) && config.defaultDialogIconId != 0) {\n            bundle.putInt(ErrorDialogManager.KEY_ICON_ID, config.defaultDialogIconId);\n        }\n        return createErrorFragment(event, bundle);\n    }\n\n    /** Returns either a new Honeycomb+ or a new support library DialogFragment. */\n    protected abstract T createErrorFragment(ThrowableFailureEvent event, Bundle arguments);\n\n    /** May be overridden to provide custom error title. */\n    protected String getTitleFor(ThrowableFailureEvent event, Bundle arguments) {\n        return config.resources.getString(config.defaultTitleId);\n    }\n\n    /** May be overridden to provide custom error messages. */\n    protected String getMessageFor(ThrowableFailureEvent event, Bundle arguments) {\n        int msgResId = config.getMessageIdForThrowable(event.throwable);\n        return config.resources.getString(msgResId);\n    }\n\n    public static class Support extends ErrorDialogFragmentFactory<Fragment> {\n\n        public Support(ErrorDialogConfig config) {\n            super(config);\n        }\n\n        protected Fragment createErrorFragment(ThrowableFailureEvent event, Bundle arguments) {\n            ErrorDialogFragments.Support errorFragment = new ErrorDialogFragments.Support();\n            errorFragment.setArguments(arguments);\n            return errorFragment;\n        }\n\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    public static class Honeycomb extends ErrorDialogFragmentFactory<android.app.Fragment> {\n\n        public Honeycomb(ErrorDialogConfig config) {\n            super(config);\n        }\n\n        protected android.app.Fragment createErrorFragment(ThrowableFailureEvent event, Bundle arguments) {\n            ErrorDialogFragments.Honeycomb errorFragment = new ErrorDialogFragments.Honeycomb();\n            errorFragment.setArguments(arguments);\n            return errorFragment;\n        }\n\n    }\n}"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/ErrorDialogFragments.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus.util;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.DialogInterface.OnClickListener;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.DialogFragment;\n\nimport org.greenrobot.eventbus.EventBus;\n\npublic class ErrorDialogFragments {\n    /** TODO Use config:  Icon res ID to use for all error dialogs. May be configured by each app (optional). */\n    public static int ERROR_DIALOG_ICON = 0;\n\n    /** TODO Use config:  Event class to be fired on dismissing the dialog by the user. May be configured by each app. */\n    public static Class<?> EVENT_TYPE_ON_CLICK;\n\n    public static Dialog createDialog(Context context, Bundle arguments, OnClickListener onClickListener) {\n        AlertDialog.Builder builder = new AlertDialog.Builder(context);\n        builder.setTitle(arguments.getString(ErrorDialogManager.KEY_TITLE));\n        builder.setMessage(arguments.getString(ErrorDialogManager.KEY_MESSAGE));\n        if (ERROR_DIALOG_ICON != 0) {\n            builder.setIcon(ERROR_DIALOG_ICON);\n        }\n        builder.setPositiveButton(android.R.string.ok, onClickListener);\n        return builder.create();\n    }\n\n    public static void handleOnClick(DialogInterface dialog, int which, Activity activity, Bundle arguments) {\n        if (EVENT_TYPE_ON_CLICK != null) {\n            Object event;\n            try {\n                event = EVENT_TYPE_ON_CLICK.newInstance();\n            } catch (Exception e) {\n                throw new RuntimeException(\"Event cannot be constructed\", e);\n            }\n            EventBus eventBus = ErrorDialogManager.factory.config.getEventBus();\n            eventBus.post(event);\n        }\n        boolean finish = arguments.getBoolean(ErrorDialogManager.KEY_FINISH_AFTER_DIALOG, false);\n        if (finish && activity != null) {\n            activity.finish();\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    public static class Honeycomb extends android.app.DialogFragment implements OnClickListener {\n        @Override\n        public Dialog onCreateDialog(Bundle savedInstanceState) {\n            return createDialog(getActivity(), getArguments(), this);\n        }\n\n        @Override\n        public void onClick(DialogInterface dialog, int which) {\n            handleOnClick(dialog, which, getActivity(), getArguments());\n        }\n    }\n\n    public static class Support extends DialogFragment implements OnClickListener {\n        @Override\n        public Dialog onCreateDialog(Bundle savedInstanceState) {\n            return createDialog(getActivity(), getArguments(), this);\n        }\n\n        @Override\n        public void onClick(DialogInterface dialog, int which) {\n            handleOnClick(dialog, which, getActivity(), getArguments());\n        }\n    }\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/ErrorDialogManager.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus.util;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.app.Application;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.DialogFragment;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentActivity;\nimport android.support.v4.app.FragmentManager;\nimport android.util.Log;\n\nimport org.greenrobot.eventbus.EventBus;\n\n/**\n * Central class for app that want to use event based error dialogs.<br/>\n * <br/>\n * How to use:\n * <ol>\n * <li>Set the {@link #factory} to configure dialogs for your app, typically in {@link Application#onCreate()}</li>\n * <li>Use one of {@link #attachTo(Activity)}, {@link #attachTo(Activity, boolean)} or\n * {@link #attachTo(Activity, boolean, Bundle)} in your Activity, typically in onCreate.</li>\n * </ol>\n * \n * For more complex mappings, you can supply your own {@link ErrorDialogFragmentFactory}.\n * \n * @author Markus\n */\npublic class ErrorDialogManager {\n\n    public static class SupportManagerFragment extends Fragment {\n        protected boolean finishAfterDialog;\n        protected Bundle argumentsForErrorDialog;\n        private EventBus eventBus;\n        private boolean skipRegisterOnNextResume;\n        private Object executionScope;\n\n        @Override\n        public void onCreate(Bundle savedInstanceState) {\n            super.onCreate(savedInstanceState);\n            eventBus = ErrorDialogManager.factory.config.getEventBus();\n            eventBus.register(this);\n            skipRegisterOnNextResume = true;\n        }\n\n        @Override\n        public void onResume() {\n            super.onResume();\n            if (skipRegisterOnNextResume) {\n                // registered in onCreate, skip registration in this run\n                skipRegisterOnNextResume = false;\n            } else {\n                eventBus = ErrorDialogManager.factory.config.getEventBus();\n                eventBus.register(this);\n            }\n        }\n\n        @Override\n        public void onPause() {\n            eventBus.unregister(this);\n            super.onPause();\n        }\n\n        public void onEventMainThread(ThrowableFailureEvent event) {\n            if (!isInExecutionScope(executionScope, event)) {\n                return;\n            }\n            checkLogException(event);\n            // Execute pending commits before finding to avoid multiple error fragments being shown\n            FragmentManager fm = getFragmentManager();\n            fm.executePendingTransactions();\n\n            DialogFragment existingFragment = (DialogFragment) fm.findFragmentByTag(TAG_ERROR_DIALOG);\n            if (existingFragment != null) {\n                // Just show the latest error\n                existingFragment.dismiss();\n            }\n\n            android.support.v4.app.DialogFragment errorFragment = (android.support.v4.app.DialogFragment) factory\n                    .prepareErrorFragment(event, finishAfterDialog, argumentsForErrorDialog);\n            if (errorFragment != null) {\n                errorFragment.show(fm, TAG_ERROR_DIALOG);\n            }\n        }\n\n        public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog,\n                Bundle argumentsForErrorDialog) {\n            FragmentManager fm = ((FragmentActivity) activity).getSupportFragmentManager();\n            SupportManagerFragment fragment = (SupportManagerFragment) fm.findFragmentByTag(TAG_ERROR_DIALOG_MANAGER);\n            if (fragment == null) {\n                fragment = new SupportManagerFragment();\n                fm.beginTransaction().add(fragment, TAG_ERROR_DIALOG_MANAGER).commit();\n                fm.executePendingTransactions();\n            }\n            fragment.finishAfterDialog = finishAfterDialog;\n            fragment.argumentsForErrorDialog = argumentsForErrorDialog;\n            fragment.executionScope = executionScope;\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    public static class HoneycombManagerFragment extends android.app.Fragment {\n        protected boolean finishAfterDialog;\n        protected Bundle argumentsForErrorDialog;\n        private EventBus eventBus;\n        private Object executionScope;\n\n        @Override\n        public void onResume() {\n            super.onResume();\n            eventBus = ErrorDialogManager.factory.config.getEventBus();\n            eventBus.register(this);\n        }\n\n        @Override\n        public void onPause() {\n            eventBus.unregister(this);\n            super.onPause();\n        }\n\n        public void onEventMainThread(ThrowableFailureEvent event) {\n            if (!isInExecutionScope(executionScope, event)) {\n                return;\n            }\n            checkLogException(event);\n\n            // Execute pending commits before finding to avoid multiple error fragments being shown\n            android.app.FragmentManager fm = getFragmentManager();\n            fm.executePendingTransactions();\n\n            android.app.DialogFragment existingFragment = (android.app.DialogFragment) fm\n                    .findFragmentByTag(TAG_ERROR_DIALOG);\n            if (existingFragment != null) {\n                // Just show the latest error\n                existingFragment.dismiss();\n            }\n\n            android.app.DialogFragment errorFragment = (android.app.DialogFragment) factory.prepareErrorFragment(event,\n                    finishAfterDialog, argumentsForErrorDialog);\n            if (errorFragment != null) {\n                errorFragment.show(fm, TAG_ERROR_DIALOG);\n            }\n        }\n\n        public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, Bundle argumentsForErrorDialog) {\n            android.app.FragmentManager fm = activity.getFragmentManager();\n            HoneycombManagerFragment fragment = (HoneycombManagerFragment) fm\n                    .findFragmentByTag(TAG_ERROR_DIALOG_MANAGER);\n            if (fragment == null) {\n                fragment = new HoneycombManagerFragment();\n                fm.beginTransaction().add(fragment, TAG_ERROR_DIALOG_MANAGER).commit();\n                fm.executePendingTransactions();\n            }\n            fragment.finishAfterDialog = finishAfterDialog;\n            fragment.argumentsForErrorDialog = argumentsForErrorDialog;\n            fragment.executionScope = executionScope;\n        }\n    }\n\n    /** Must be set by the application. */\n    public static ErrorDialogFragmentFactory<?> factory;\n\n    protected static final String TAG_ERROR_DIALOG = \"de.greenrobot.eventbus.error_dialog\";\n    protected static final String TAG_ERROR_DIALOG_MANAGER = \"de.greenrobot.eventbus.error_dialog_manager\";\n\n    public static final String KEY_TITLE = \"de.greenrobot.eventbus.errordialog.title\";\n    public static final String KEY_MESSAGE = \"de.greenrobot.eventbus.errordialog.message\";\n    public static final String KEY_FINISH_AFTER_DIALOG = \"de.greenrobot.eventbus.errordialog.finish_after_dialog\";\n    public static final String KEY_ICON_ID = \"de.greenrobot.eventbus.errordialog.icon_id\";\n    public static final String KEY_EVENT_TYPE_ON_CLOSE = \"de.greenrobot.eventbus.errordialog.event_type_on_close\";\n\n    /** Scope is limited to the activity's class. */\n    public static void attachTo(Activity activity) {\n        attachTo(activity, false, null);\n    }\n\n    /** Scope is limited to the activity's class. */\n    public static void attachTo(Activity activity, boolean finishAfterDialog) {\n        attachTo(activity, finishAfterDialog, null);\n    }\n\n    /** Scope is limited to the activity's class. */\n    public static void attachTo(Activity activity, boolean finishAfterDialog, Bundle argumentsForErrorDialog) {\n        Object executionScope = activity.getClass();\n        attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog);\n    }\n    \n    public static void attachTo(Activity activity, Object executionScope, boolean finishAfterDialog, Bundle argumentsForErrorDialog) {\n        if (factory == null) {\n            throw new RuntimeException(\"You must set the static factory field to configure error dialogs for your app.\");\n        }\n        if (isSupportActivity(activity)) {\n            SupportManagerFragment.attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog);\n        } else {\n            HoneycombManagerFragment.attachTo(activity, executionScope, finishAfterDialog, argumentsForErrorDialog);\n        }\n    }\n\n    private static boolean isSupportActivity(Activity activity) {\n        boolean isSupport = false;\n        for (Class<?> c = activity.getClass().getSuperclass();; c = c.getSuperclass()) {\n            if (c == null) {\n                throw new RuntimeException(\"Illegal activity type: \" + activity.getClass());\n            }\n            String name = c.getName();\n            if (name.equals(\"android.support.v4.app.FragmentActivity\")) {\n                isSupport = true;\n                break;\n            } else if (name.startsWith(\"com.actionbarsherlock.app\")\n                    && (name.endsWith(\".SherlockActivity\") || name.endsWith(\".SherlockListActivity\") || name\n                            .endsWith(\".SherlockPreferenceActivity\"))) {\n                throw new RuntimeException(\"Please use SherlockFragmentActivity. Illegal activity: \" + name);\n            } else if (name.equals(\"android.app.Activity\")) {\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {\n                    throw new RuntimeException(\n                            \"Illegal activity without fragment support. Either use Android 3.0+ or android.support.v4.app.FragmentActivity.\");\n                }\n                break;\n            }\n        }\n        return isSupport;\n    }\n\n    protected static void checkLogException(ThrowableFailureEvent event) {\n        if (factory.config.logExceptions) {\n            String tag = factory.config.tagForLoggingExceptions;\n            if (tag == null) {\n                tag = EventBus.TAG;\n            }\n            Log.i(tag, \"Error dialog manager received exception\", event.throwable);\n        }\n    }\n\n    private static boolean isInExecutionScope(Object executionScope, ThrowableFailureEvent event) {\n        if (event != null) {\n            Object eventExecutionScope = event.getExecutionScope();\n            if (eventExecutionScope != null && !eventExecutionScope.equals(executionScope)) {\n                // Event not in our scope, do nothing\n                return false;\n            }\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/ExceptionToResourceMapping.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus.util;\n\nimport android.util.Log;\n\nimport org.greenrobot.eventbus.EventBus;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\n\n\n/**\n * Maps throwables to texts for error dialogs. Use Config to configure the mapping.\n * \n * @author Markus\n */\npublic class ExceptionToResourceMapping {\n\n    public final Map<Class<? extends Throwable>, Integer> throwableToMsgIdMap;\n\n    public ExceptionToResourceMapping() {\n        throwableToMsgIdMap = new HashMap<Class<? extends Throwable>, Integer>();\n    }\n\n    /** Looks at the exception and its causes trying to find an ID. */\n    public Integer mapThrowable(final Throwable throwable) {\n        Throwable throwableToCheck = throwable;\n        int depthToGo = 20;\n\n        while (true) {\n            Integer resId = mapThrowableFlat(throwableToCheck);\n            if (resId != null) {\n                return resId;\n            } else {\n                throwableToCheck = throwableToCheck.getCause();\n                depthToGo--;\n                if (depthToGo <= 0 || throwableToCheck == throwable || throwableToCheck == null) {\n                    Log.d(EventBus.TAG, \"No specific message ressource ID found for \" + throwable);\n                    // return config.defaultErrorMsgId;\n                    return null;\n                }\n            }\n        }\n\n    }\n\n    /** Mapping without checking the cause (done in mapThrowable). */\n    protected Integer mapThrowableFlat(Throwable throwable) {\n        Class<? extends Throwable> throwableClass = throwable.getClass();\n        Integer resId = throwableToMsgIdMap.get(throwableClass);\n        if (resId == null) {\n            Class<? extends Throwable> closestClass = null;\n            Set<Entry<Class<? extends Throwable>, Integer>> mappings = throwableToMsgIdMap.entrySet();\n            for (Entry<Class<? extends Throwable>, Integer> mapping : mappings) {\n                Class<? extends Throwable> candidate = mapping.getKey();\n                if (candidate.isAssignableFrom(throwableClass)) {\n                    if (closestClass == null || closestClass.isAssignableFrom(candidate)) {\n                        closestClass = candidate;\n                        resId = mapping.getValue();\n                    }\n                }\n            }\n\n        }\n        return resId;\n    }\n\n    public ExceptionToResourceMapping addMapping(Class<? extends Throwable> clazz, int msgId) {\n        throwableToMsgIdMap.put(clazz, msgId);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/HasExecutionScope.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.greenrobot.eventbus.util;\n\npublic interface HasExecutionScope {\n    Object getExecutionScope();\n\n    void setExecutionScope(Object executionScope);\n\n}\n"
  },
  {
    "path": "EventBus/src/org/greenrobot/eventbus/util/ThrowableFailureEvent.java",
    "content": "/*\n * Copyright (C) 2012-2016 Markus Junginger, greenrobot (http://greenrobot.org)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.greenrobot.eventbus.util;\n\n/**\n * A generic failure event, which can be used by apps to propagate thrown exceptions. Also used in conjunction with\n * {@link ErrorDialogManager}.\n */\npublic class ThrowableFailureEvent implements HasExecutionScope {\n    protected final Throwable throwable;\n    protected final boolean suppressErrorUi;\n    private Object executionContext;\n\n    public ThrowableFailureEvent(Throwable throwable) {\n        this.throwable = throwable;\n        suppressErrorUi = false;\n    }\n\n    /**\n     * @param suppressErrorUi\n     *            true indicates to the receiver that no error UI (e.g. dialog) should now displayed.\n     */\n    public ThrowableFailureEvent(Throwable throwable, boolean suppressErrorUi) {\n        this.throwable = throwable;\n        this.suppressErrorUi = suppressErrorUi;\n    }\n\n    public Throwable getThrowable() {\n        return throwable;\n    }\n\n    public boolean isSuppressErrorUi() {\n        return suppressErrorUi;\n    }\n\n    public Object getExecutionScope() {\n        return executionContext;\n    }\n\n    public void setExecutionScope(Object executionContext) {\n        this.executionContext = executionContext;\n    }\n    \n}\n"
  },
  {
    "path": "EventBus3.0_Sample/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "EventBus3.0_Sample/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.3\"\n\n    defaultConfig {\n        applicationId \"com.harvic.tryeventbus2\"\n        minSdkVersion 15\n        targetSdkVersion 23\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n        }\n    }\n}\n\ndependencies {\n    compile 'com.android.support:support-v4:23.3.0'\n    compile project(':EventBus')\n}\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.harvic.tryeventbus2\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"14\"\n        android:targetSdkVersion=\"14\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\" >\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/app_name\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".SecondActivity\"\n            android:label=\"@string/title_activity_second\" >\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/java/com/harvic/other/FirstEvent.java",
    "content": "package com.harvic.other;\n\npublic class FirstEvent {\n\n\tprivate String mMsg;\n\tpublic FirstEvent(String msg) {\n\t\t// TODO Auto-generated constructor stub\n\t\tmMsg = msg;\n\t}\n\tpublic String getMsg(){\n\t\treturn mMsg;\n\t}\n}\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/java/com/harvic/other/SecondEvent.java",
    "content": "package com.harvic.other;\n\npublic class SecondEvent{\n\n\tprivate String mMsg;\n\tpublic SecondEvent(String msg) {\n\t\t// TODO Auto-generated constructor stub\n\t\tthis.mMsg = msg;\n\t}\n\tpublic String getMsg(){\n\t\treturn mMsg;\n\t}\n}\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/java/com/harvic/other/ThirdEvent.java",
    "content": "package com.harvic.other;\n\npublic class ThirdEvent {\n\n\tprivate String mMsg;\n\tpublic ThirdEvent(String msg) {\n\t\t// TODO Auto-generated constructor stub\n\t\tmMsg = msg;\n\t}\n\tpublic String getMsg(){\n\t\treturn mMsg;\n\t}\n}\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/java/com/harvic/tryeventbus2/MainActivity.java",
    "content": "package com.harvic.tryeventbus2;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.TextView;\n\nimport com.harvic.other.FirstEvent;\nimport com.harvic.other.SecondEvent;\nimport com.harvic.other.ThirdEvent;\n\nimport org.greenrobot.eventbus.EventBus;\nimport org.greenrobot.eventbus.Subscribe;\nimport org.greenrobot.eventbus.ThreadMode;\n\n\npublic class MainActivity extends Activity {\n\n\tprivate Button btn;\n\tprivate TextView tv;\n\tprivate EventBus eventBus;\n\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_main);\n\n\t\t//1.注册\n\t\tEventBus.getDefault().register(this);\n\n\t\tbtn = (Button) findViewById(R.id.btn_try);\n\t\ttv = (TextView) findViewById(R.id.tv);\n\n\t\tbtn.setOnClickListener(new View.OnClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\t// TODO Auto-generated method stub\n\t\t\t\tIntent intent = new Intent(getApplicationContext(),\n\t\t\t\t\t\tSecondActivity.class);\n\t\t\t\tstartActivity(intent);\n\t\t\t}\n\t\t});\n\t}\n\n\n\t@Subscribe(threadMode = ThreadMode.MAIN,sticky = false,priority = 80)\n\tpublic void testOnEventMainThread(FirstEvent event) {\n\n\t\tLog.d(\"harvic\", \"testOnEventMainThread:\" + event.getMsg()+\",priority=80\"+\",sticky==false\");\n\t\tLog.d(\"harvic\",\"Thread-name==\"+Thread.currentThread().getName());\n\t\ttv.setText(event.getMsg());\n\t}\n\n\t@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 71)\n\tpublic void onEventMainThread(FirstEvent event) {\n\t\tLog.d(\"harvic\", \"onEventMainThread:\" + event.getMsg()+\",priority=71,sticky = true\");\n\t\tLog.d(\"harvic\",\"Thread-name==\"+Thread.currentThread().getName());\n\t\ttv.setText(event.getMsg());\n\t}\n\n\t@Subscribe(threadMode = ThreadMode.BACKGROUND)\n\tpublic void onEventBackgroundThread(SecondEvent event){\n\t\tLog.d(\"harvic\", \"onEventBackground:\" + event.getMsg());\n\t\tLog.d(\"harvic\",\"Thread-name==\"+Thread.currentThread().getName());\n//\t\ttv.setText(event.getMsg());\n\t}\n\t@Subscribe(threadMode = ThreadMode.ASYNC)\n\tpublic void onEventAsync(SecondEvent event){\n\t\tLog.d(\"harvic\", \"onEventAsync:\" + event.getMsg());\n\t\tLog.d(\"harvic\",\"Thread-name==\"+Thread.currentThread().getName());\n//\t\ttv.setText(event.getMsg());\n\t}\n\n\n\t@Subscribe(threadMode = ThreadMode.POSTING)\n\tpublic void onEvent(ThirdEvent event) {\n\t\tLog.d(\"harvic\", \"OnEvent:\" + event.getMsg());\n\t\tLog.d(\"harvic\",\"Thread-name==\"+Thread.currentThread().getName());\n\t\ttv.setText(event.getMsg());\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\t//取消注册\n\t\tEventBus.getDefault().unregister(this);\n\t}\n}\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/java/com/harvic/tryeventbus2/SecondActivity.java",
    "content": "package com.harvic.tryeventbus2;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.Button;\n\nimport com.harvic.other.FirstEvent;\nimport com.harvic.other.SecondEvent;\nimport com.harvic.other.ThirdEvent;\n\nimport org.greenrobot.eventbus.EventBus;\n\npublic class SecondActivity extends Activity {\n\tprivate Button btn_FirstEvent, btn_SecondEvent, btn_ThirdEvent;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.activity_second);\n\t\tbtn_FirstEvent = (Button) findViewById(R.id.btn_first_event);\n\t\tbtn_SecondEvent = (Button) findViewById(R.id.btn_second_event);\n\t\tbtn_ThirdEvent = (Button) findViewById(R.id.btn_third_event);\n\n\t\tbtn_FirstEvent.setOnClickListener(new View.OnClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tEventBus.getDefault().post(\n\t\t\t\t\t\tnew FirstEvent(\"FirstEvent 阿福\"));\n\t\t\t}\n\t\t});\n\t\t\n\t\tbtn_SecondEvent.setOnClickListener(new View.OnClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\n\t\t\t\tEventBus.getDefault().post(\n\t\t\t\t\t\tnew SecondEvent(\"SecondEvent 硅谷\"));\n\t\t\t}\n\t\t});\n\n\t\tbtn_ThirdEvent.setOnClickListener(new View.OnClickListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tEventBus.getDefault().post(\n\t\t\t\t\t\tnew ThirdEvent(\"ThirdEvent btn clicked\"));\n\n\t\t\t}\n\t\t});\n\n\t}\n\n}\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n    \n    <Button \n        android:id=\"@+id/btn_try\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"btn_bty\"/>\n\n    <TextView\n        android:id=\"@+id/tv\"\n        android:text=\"还没有内容\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/layout/activity_second.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\"com.harvic.try_eventbus_1.SecondActivity\" >\n\n    <Button \n        android:id=\"@+id/btn_first_event\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"First Event\"/>\n    \n    <Button android:id=\"@+id/btn_second_event\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Second Event\"/>\n    \n    <Button android:id=\"@+id/btn_third_event\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Third Event\"/>\n\n</LinearLayout>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/menu/main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"com.harvic.tryeventbus2.MainActivity\" >\n\n    <item\n        android:id=\"@+id/action_settings\"\n        android:orderInCategory=\"100\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/action_settings\"/>\n\n</menu>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/menu/second.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"com.harvic.tryeventbus2.SecondActivity\" >\n\n    <item\n        android:id=\"@+id/action_settings\"\n        android:orderInCategory=\"100\"\n        android:showAsAction=\"never\"\n        android:title=\"@string/action_settings\"/>\n\n</menu>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/values/dimens.xml",
    "content": "<resources>\n\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">TryEventBus2</string>\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"title_activity_second\">SecondActivity</string>\n\n</resources>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Light\">\n        <!--\n            Theme customizations available in newer API levels can go in\n            res/values-vXX/styles.xml, while customizations related to\n            backward-compatibility can go here.\n        -->\n    </style>\n\n    <!-- Application theme. -->\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n        <!-- All customizations that are NOT specific to a particular API-level can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/values-v11/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme for API 11+. This theme completely replaces\n        AppBaseTheme from res/values/styles.xml on API 11+ devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Holo.Light\">\n        <!-- API 11 theme customizations can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/values-v14/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme for API 14+. This theme completely replaces\n        AppBaseTheme from BOTH res/values/styles.xml and\n        res/values-v11/styles.xml on API 14+ devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Holo.Light.DarkActionBar\">\n        <!-- API 14 theme customizations can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "EventBus3.0_Sample/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n\n    <!--\n         Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively).\n    -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n\n</resources>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion '24.0.3'\n\n    defaultConfig {\n        applicationId \"fm.jiecao.jiecaovideoplayer\"\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 47\n        versionName \"4.8.3\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    testCompile 'junit:junit:4.12'\n    compile project(':jcvideoplayer-lib')\n    compile 'com.github.bumptech.glide:glide:3.5.2'\n    compile 'com.squareup.picasso:picasso:2.5.1'\n    compile 'com.facebook.fresco:fresco:0.9.0'\n    compile 'com.mcxiaoke.volley:library:1.0.7'\n    compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4'\n\n    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'\n    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'\n    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'\n\n    compile 'com.android.support:recyclerview-v7:24.2.1'\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/Nathen/WorkEnv/android-sdk-macosx/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/androidTest/java/fm/jiecao/jiecaovideoplayer/ApplicationTest.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"fm.jiecao.jiecaovideoplayer\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n\n    <application\n        android:name=\".DemoApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".MainActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".PlayDirectlyActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n\n        <activity\n            android:name=\".ListViewActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".ListViewNormalActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".ListViewViewpagerActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".ListViewMultiHolderActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n\n        <activity\n            android:name=\".UIActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".UISmallChangeActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".UIBigChangeActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".UIImageLoaderActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".RecyclerViewNormalActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".AutoTinyActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".AutoTinyListActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".AutoTinyNormalActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n        <activity\n            android:name=\".WebViewActivity\"\n            android:configChanges=\"orientation|screenSize|keyboardHidden\"\n            android:screenOrientation=\"portrait\" />\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/assets/jcvd.html",
    "content": "<!DOCTYPE html>\n<html>\n<body>\n<div class=\"content\"></div>\n<div id=\"cont\" style=\"width:100%;height:200px\">\n</div>\n<div style=\"height:100px\"></div>\n<h3 style=\"width:100%;text-align:center\">This is webview</h3>\n<div style=\"height:700px\"></div>\n<div id=\"cont1\" style=\"width:65%;height:200px;float:right;\">\n</div>\n<div style=\"height:800px\"></div>\n<script>\n    var cont=document.getElementById(\"cont\");\n    jcvd.adViewJieCaoVideoPlayer(-1,200,0,0)\n    <!--jcvd.adViewJieCaoVideoPlayer(cont1.width,cont1.height,cont1.offsetTop,cont1.offsetLeft)-->\n\n    var cont1=document.getElementById(\"cont1\");\n    jcvd.adViewJieCaoVideoPlayer(210,120,cont1.offsetTop,cont1.offsetLeft)\n\n</script>\n</body>\n</html>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/AutoTinyActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\n\n/**\n * Created by Nathen on 16/8/23.\n */\npublic class AutoTinyActivity extends AppCompatActivity implements View.OnClickListener {\n\n    Button normal, list;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"AutoTinyWindow\");\n        setContentView(R.layout.activity_auto_tiny);\n        normal = (Button) findViewById(R.id.screen_normal);\n        list = (Button) findViewById(R.id.screen_list);\n\n        normal.setOnClickListener(this);\n        list.setOnClickListener(this);\n\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.screen_normal:\n                startActivity(new Intent(this, AutoTinyNormalActivity.class));\n                break;\n            case R.id.screen_list:\n                startActivity(new Intent(this, AutoTinyListActivity.class));\n                break;\n        }\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/AutoTinyListActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.widget.AbsListView;\nimport android.widget.ListView;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\n\n/**\n * Created by Nathen on 16/8/23.\n */\npublic class AutoTinyListActivity extends AppCompatActivity {\n\n    ListView listView;\n    VideoListAdapter adapterVideoList;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"AutoTinyList\");\n        setContentView(R.layout.activity_listview_content);\n\n        listView = (ListView) findViewById(R.id.listview);\n        adapterVideoList = new VideoListAdapter(this);\n        listView.setAdapter(adapterVideoList);\n        listView.setOnScrollListener(new AbsListView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(AbsListView view, int scrollState) {\n\n            }\n\n            @Override\n            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n                JCVideoPlayer.onScroll();\n            }\n        });\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/AutoTinyNormalActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.widget.AbsListView;\nimport android.widget.LinearLayout;\nimport android.widget.ListAdapter;\nimport android.widget.ListView;\nimport android.widget.SimpleAdapter;\n\nimport com.squareup.picasso.Picasso;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen on 16/8/23.\n */\npublic class AutoTinyNormalActivity extends AppCompatActivity implements AbsListView.OnScrollListener {\n    ListView listView;\n    LinearLayout headerLayout;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"AutoTinyNormal\");\n        setContentView(R.layout.activity_listview_content);\n\n        listView = (ListView) findViewById(R.id.listview);\n        headerLayout = (LinearLayout) getLayoutInflater().inflate(R.layout.header_auto_tiny_normal, null);\n        listView.addHeaderView(headerLayout);\n\n        JCVideoPlayerStandard jcVideoPlayerStandard = (JCVideoPlayerStandard) headerLayout.findViewById(R.id.jc_video);\n        jcVideoPlayerStandard.setUp(\"http://2449.vod.myqcloud.com/2449_22ca37a6ea9011e5acaaf51d105342e3.f20.mp4\"\n                , JCVideoPlayerStandard.SCREEN_LAYOUT_NORMAL, \"嫂子坐这\");\n        Picasso.with(this)\n                .load(\"http://cos.myqcloud.com/1000264/qcloud_video_attachment/842646334/vod_cover/cover1458036374.jpg\")\n                .into(jcVideoPlayerStandard.thumbImageView);\n\n        Map<String, String> keyValuePair = new HashMap<>();\n        keyValuePair.put(\"key\", \"list item\");\n        List<Map<String, String>> list = new ArrayList<>();\n        for (int i = 0; i < 50; i++) {\n            list.add(keyValuePair);\n        }\n\n        ListAdapter adapter = new SimpleAdapter(this, list,\n                android.R.layout.simple_list_item_1, new String[]{\"key\"}, new int[]{android.R.id.text1});\n\n        listView.setAdapter(adapter);\n        listView.setOnScrollListener(this);\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    public void onScrollStateChanged(AbsListView view, int scrollState) {\n\n    }\n\n    @Override\n    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {\n        JCVideoPlayer.onScroll();\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/CustomView/JCVideoPlayerStandardAutoComplete.java",
    "content": "package fm.jiecao.jiecaovideoplayer.CustomView;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen on 2016/11/26.\n */\n\npublic class JCVideoPlayerStandardAutoComplete extends JCVideoPlayerStandard {\n    public JCVideoPlayerStandardAutoComplete(Context context) {\n        super(context);\n    }\n\n    public JCVideoPlayerStandardAutoComplete(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public void onAutoCompletion() {\n        if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n            setUiWitStateAndScreen(CURRENT_STATE_AUTO_COMPLETE);\n        } else {\n            super.onAutoCompletion();\n        }\n\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/CustomView/JCVideoPlayerStandardFresco.java",
    "content": "package fm.jiecao.jiecaovideoplayer.CustomView;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.ImageView;\nimport android.widget.ProgressBar;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.facebook.drawee.view.SimpleDraweeView;\n\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport fm.jiecao.jcvideoplayer_lib.JCUserActionStandard;\nimport fm.jiecao.jcvideoplayer_lib.JCUtils;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jiecaovideoplayer.R;\n\n/**\n * Created by Nathen\n * On 2016/05/01 22:59\n */\npublic class JCVideoPlayerStandardFresco extends JCVideoPlayer {\n\n    public ImageView backButton;\n    public ProgressBar bottomProgressBar, loadingProgressBar;\n    public TextView titleTextView;\n    public SimpleDraweeView thumbImageView;\n    public ImageView coverImageView;\n    public ImageView tinyBackImageView;\n\n    protected static Timer DISSMISS_CONTROL_VIEW_TIMER;\n    protected DismissControlViewTimerTask mDismissControlViewTimerTask;\n\n    protected static JCUserActionStandard JC_USER_EVENT_STANDARD;\n\n    public JCVideoPlayerStandardFresco(Context context) {\n        super(context);\n    }\n\n    public JCVideoPlayerStandardFresco(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public void init(Context context) {\n        super.init(context);\n        bottomProgressBar = (ProgressBar) findViewById(R.id.bottom_progressbar);\n        titleTextView = (TextView) findViewById(R.id.title);\n        backButton = (ImageView) findViewById(R.id.back);\n        thumbImageView = (SimpleDraweeView) findViewById(R.id.thumb);\n        coverImageView = (ImageView) findViewById(R.id.cover);\n        loadingProgressBar = (ProgressBar) findViewById(R.id.loading);\n        tinyBackImageView = (ImageView) findViewById(R.id.back_tiny);\n\n        thumbImageView.setOnClickListener(this);\n        backButton.setOnClickListener(this);\n        tinyBackImageView.setOnClickListener(this);\n\n    }\n\n    @Override\n    public boolean setUp(String url, int screen, Object... objects) {\n        if (objects.length == 0) return false;\n        if (super.setUp(url, screen, objects)) {\n            titleTextView.setText(objects[0].toString());\n            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n                fullscreenButton.setImageResource(R.drawable.jc_shrink);\n                backButton.setVisibility(View.VISIBLE);\n                tinyBackImageView.setVisibility(View.INVISIBLE);\n            } else if (currentScreen == SCREEN_LAYOUT_LIST) {\n                fullscreenButton.setImageResource(R.drawable.jc_enlarge);\n                backButton.setVisibility(View.GONE);\n                tinyBackImageView.setVisibility(View.INVISIBLE);\n            } else if (currentScreen == SCREEN_WINDOW_TINY) {\n                tinyBackImageView.setVisibility(View.VISIBLE);\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public int getLayoutId() {\n        return R.layout.layout_standard_fresco;\n    }\n\n    @Override\n    public void setUiWitStateAndScreen(int state) {\n        super.setUiWitStateAndScreen(state);\n        switch (currentState) {\n            case CURRENT_STATE_NORMAL:\n                changeUiToNormal();\n                break;\n            case CURRENT_STATE_PREPARING:\n                changeUiToPreparingShow();\n                startDismissControlViewTimer();\n                break;\n            case CURRENT_STATE_PLAYING:\n                changeUiToPlayingShow();\n                startDismissControlViewTimer();\n                break;\n            case CURRENT_STATE_PAUSE:\n                changeUiToPauseShow();\n                cancelDismissControlViewTimer();\n                break;\n            case CURRENT_STATE_ERROR:\n                changeUiToError();\n                break;\n            case CURRENT_STATE_AUTO_COMPLETE:\n                changeUiToCompleteShow();\n                cancelDismissControlViewTimer();\n                bottomProgressBar.setProgress(100);\n                break;\n            case CURRENT_STATE_PLAYING_BUFFERING_START:\n                changeUiToPlayingBufferingShow();\n                break;\n        }\n    }\n\n    @Override\n    public boolean onTouch(View v, MotionEvent event) {\n        int id = v.getId();\n        if (id == R.id.surface_container) {\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    break;\n                case MotionEvent.ACTION_UP:\n                    startDismissControlViewTimer();\n                    if (mChangePosition) {\n                        int duration = getDuration();\n                        int progress = mSeekTimePosition * 100 / (duration == 0 ? 1 : duration);\n                        bottomProgressBar.setProgress(progress);\n                    }\n                    if (!mChangePosition && !mChangeVolume) {\n                        onClickUiToggle();\n                    }\n                    break;\n            }\n        } else if (id == R.id.progress) {\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    cancelDismissControlViewTimer();\n                    break;\n                case MotionEvent.ACTION_UP:\n                    startDismissControlViewTimer();\n                    break;\n            }\n        }\n        return super.onTouch(v, event);\n    }\n\n    @Override\n    public void onClick(View v) {\n        super.onClick(v);\n        int i = v.getId();\n        if (i == R.id.thumb) {\n            if (TextUtils.isEmpty(url)) {\n                Toast.makeText(getContext(), getResources().getString(R.string.no_url), Toast.LENGTH_SHORT).show();\n                return;\n            }\n            if (currentState == CURRENT_STATE_NORMAL) {\n                if (!url.startsWith(\"file\") && !JCUtils.isWifiConnected(getContext()) && !WIFI_TIP_DIALOG_SHOWED) {\n                    showWifiDialog();\n                    return;\n                }\n                startPlayLogic();\n            } else if (currentState == CURRENT_STATE_AUTO_COMPLETE) {\n                onClickUiToggle();\n            }\n        } else if (i == R.id.surface_container) {\n            if (JC_USER_EVENT_STANDARD != null && isCurrentMediaListener()) {\n//                if (mIfCurrentIsFullscreen) {\n//                    JC_USER_EVENT_STANDARD.onClickBlankFullscreen(url, objects);\n//                } else {\n//                    JC_USER_EVENT_STANDARD.onClickBlank(url, objects);\n//                }\n            }\n            startDismissControlViewTimer();\n        } else if (i == R.id.back) {\n            backPress();\n        } else if (i == R.id.back_tiny) {\n            backPress();\n        }\n    }\n\n    @Override\n    public void showWifiDialog() {\n        super.showWifiDialog();\n        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());\n        builder.setMessage(getResources().getString(R.string.tips_not_wifi));\n        builder.setPositiveButton(getResources().getString(R.string.tips_not_wifi_confirm), new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                dialog.dismiss();\n                startPlayLogic();\n                WIFI_TIP_DIALOG_SHOWED = true;\n            }\n        });\n        builder.setNegativeButton(getResources().getString(R.string.tips_not_wifi_cancel), new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                dialog.dismiss();\n            }\n        });\n        builder.create().show();\n    }\n\n    @Override\n    public void onStartTrackingTouch(SeekBar seekBar) {\n        super.onStartTrackingTouch(seekBar);\n        cancelDismissControlViewTimer();\n    }\n\n    @Override\n    public void onStopTrackingTouch(SeekBar seekBar) {\n        super.onStopTrackingTouch(seekBar);\n        startDismissControlViewTimer();\n    }\n\n    private void startPlayLogic() {\n        onEvent(JCUserActionStandard.ON_CLICK_START_THUMB);\n        prepareVideo();\n        startDismissControlViewTimer();\n    }\n\n    private void onClickUiToggle() {\n        if (currentState == CURRENT_STATE_PREPARING) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPreparingClear();\n            } else {\n                changeUiToPreparingShow();\n            }\n        } else if (currentState == CURRENT_STATE_PLAYING) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPlayingClear();\n            } else {\n                changeUiToPlayingShow();\n            }\n        } else if (currentState == CURRENT_STATE_PAUSE) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPauseClear();\n            } else {\n                changeUiToPauseShow();\n            }\n        } else if (currentState == CURRENT_STATE_AUTO_COMPLETE) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToCompleteClear();\n            } else {\n                changeUiToCompleteShow();\n            }\n        } else if (currentState == CURRENT_STATE_PLAYING_BUFFERING_START) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPlayingBufferingClear();\n            } else {\n                changeUiToPlayingBufferingShow();\n            }\n        }\n    }\n\n    @Override\n    public void setProgressAndTime(int progress, int secProgress, int currentTime, int totalTime) {\n        super.setProgressAndTime(progress, secProgress, currentTime, totalTime);\n        if (progress != 0) bottomProgressBar.setProgress(progress);\n        if (secProgress != 0) bottomProgressBar.setSecondaryProgress(secProgress);\n    }\n\n    @Override\n    public void resetProgressAndTime() {\n        super.resetProgressAndTime();\n        bottomProgressBar.setProgress(0);\n        bottomProgressBar.setSecondaryProgress(0);\n    }\n\n    //Unified management Ui\n    public void changeUiToNormal() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n    }\n\n    public void changeUiToPreparingShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToPreparingClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToPlayingShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToPlayingClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToPauseShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToPauseClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToPlayingBufferingShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToPlayingBufferingClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToCompleteShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToCompleteClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void changeUiToError() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    private void setAllControlsVisible(int topCon, int bottomCon, int startBtn, int loadingPro,\n                                       int thumbImg, int coverImg, int bottomPro) {\n        topContainer.setVisibility(topCon);\n        bottomContainer.setVisibility(bottomCon);\n        startButton.setVisibility(startBtn);\n        loadingProgressBar.setVisibility(loadingPro);\n        thumbImageView.setVisibility(thumbImg);\n        coverImageView.setVisibility(coverImg);\n        bottomProgressBar.setVisibility(bottomPro);\n    }\n\n    private void updateStartImage() {\n        if (currentState == CURRENT_STATE_PLAYING) {\n            startButton.setImageResource(R.drawable.jc_click_pause_selector);\n        } else if (currentState == CURRENT_STATE_ERROR) {\n            startButton.setImageResource(R.drawable.jc_click_error_selector);\n        } else {\n            startButton.setImageResource(R.drawable.jc_click_play_selector);\n        }\n    }\n\n    protected Dialog mProgressDialog;\n    protected ProgressBar mDialogProgressBar;\n    protected TextView mDialogSeekTime;\n    protected TextView mDialogTotalTime;\n    protected ImageView mDialogIcon;\n\n    @Override\n    public void showProgressDialog(float deltaX, String seekTime, int seekTimePosition, String totalTime, int totalTimeDuration) {\n        super.showProgressDialog(deltaX, seekTime, seekTimePosition, totalTime, totalTimeDuration);\n        if (mProgressDialog == null) {\n            View localView = LayoutInflater.from(getContext()).inflate(R.layout.jc_progress_dialog, null);\n            mDialogProgressBar = ((ProgressBar) localView.findViewById(R.id.duration_progressbar));\n            mDialogSeekTime = ((TextView) localView.findViewById(R.id.tv_current));\n            mDialogTotalTime = ((TextView) localView.findViewById(R.id.tv_duration));\n            mDialogIcon = ((ImageView) localView.findViewById(R.id.duration_image_tip));\n            mProgressDialog = new Dialog(getContext(), R.style.jc_style_dialog_progress);\n            mProgressDialog.setContentView(localView);\n            mProgressDialog.getWindow().addFlags(Window.FEATURE_ACTION_BAR);\n            mProgressDialog.getWindow().addFlags(32);\n            mProgressDialog.getWindow().addFlags(16);\n            mProgressDialog.getWindow().setLayout(-2, -2);\n            WindowManager.LayoutParams localLayoutParams = mProgressDialog.getWindow().getAttributes();\n            localLayoutParams.gravity = 49;\n            localLayoutParams.y = getResources().getDimensionPixelOffset(R.dimen.jc_progress_dialog_margin_top);\n            mProgressDialog.getWindow().setAttributes(localLayoutParams);\n        }\n        if (!mProgressDialog.isShowing()) {\n            mProgressDialog.show();\n        }\n\n        mDialogSeekTime.setText(seekTime);\n        mDialogTotalTime.setText(\" / \" + totalTime);\n        mDialogProgressBar.setProgress(seekTimePosition * 100 / totalTimeDuration);\n        if (deltaX > 0) {\n            mDialogIcon.setBackgroundResource(R.drawable.jc_forward_icon);\n        } else {\n            mDialogIcon.setBackgroundResource(R.drawable.jc_backward_icon);\n        }\n\n    }\n\n    @Override\n    public void dismissProgressDialog() {\n        super.dismissProgressDialog();\n        if (mProgressDialog != null) {\n            mProgressDialog.dismiss();\n        }\n    }\n\n\n    protected Dialog mVolumeDialog;\n    protected ProgressBar mDialogVolumeProgressBar;\n\n    @Override\n    public void showVolumeDialog(float deltaY, int volumePercent) {\n        super.showVolumeDialog(deltaY, volumePercent);\n        if (mVolumeDialog == null) {\n            View localView = LayoutInflater.from(getContext()).inflate(R.layout.jc_volume_dialog, null);\n            mDialogVolumeProgressBar = ((ProgressBar) localView.findViewById(R.id.volume_progressbar));\n            mVolumeDialog = new Dialog(getContext(), R.style.jc_style_dialog_progress);\n            mVolumeDialog.setContentView(localView);\n            mVolumeDialog.getWindow().addFlags(8);\n            mVolumeDialog.getWindow().addFlags(32);\n            mVolumeDialog.getWindow().addFlags(16);\n            mVolumeDialog.getWindow().setLayout(-2, -2);\n            WindowManager.LayoutParams localLayoutParams = mVolumeDialog.getWindow().getAttributes();\n            localLayoutParams.gravity = 19;\n            localLayoutParams.x = getContext().getResources().getDimensionPixelOffset(R.dimen.jc_volume_dialog_margin_left);\n            mVolumeDialog.getWindow().setAttributes(localLayoutParams);\n        }\n        if (!mVolumeDialog.isShowing()) {\n            mVolumeDialog.show();\n        }\n\n        mDialogVolumeProgressBar.setProgress(volumePercent);\n    }\n\n    @Override\n    public void dismissVolumeDialog() {\n        super.dismissVolumeDialog();\n        if (mVolumeDialog != null) {\n            mVolumeDialog.dismiss();\n        }\n    }\n\n    private void startDismissControlViewTimer() {\n        cancelDismissControlViewTimer();\n        DISSMISS_CONTROL_VIEW_TIMER = new Timer();\n        mDismissControlViewTimerTask = new DismissControlViewTimerTask();\n        DISSMISS_CONTROL_VIEW_TIMER.schedule(mDismissControlViewTimerTask, 2500);\n    }\n\n    private void cancelDismissControlViewTimer() {\n        if (DISSMISS_CONTROL_VIEW_TIMER != null) {\n            DISSMISS_CONTROL_VIEW_TIMER.cancel();\n        }\n        if (mDismissControlViewTimerTask != null) {\n            mDismissControlViewTimerTask.cancel();\n        }\n\n    }\n\n    protected class DismissControlViewTimerTask extends TimerTask {\n\n        @Override\n        public void run() {\n            if (currentState != CURRENT_STATE_NORMAL\n                    && currentState != CURRENT_STATE_ERROR\n                    && currentState != CURRENT_STATE_AUTO_COMPLETE) {\n                if (getContext() != null && getContext() instanceof Activity) {\n                    ((Activity) getContext()).runOnUiThread(new Runnable() {\n                        @Override\n                        public void run() {\n                            bottomContainer.setVisibility(View.INVISIBLE);\n                            topContainer.setVisibility(View.INVISIBLE);\n                            bottomProgressBar.setVisibility(View.VISIBLE);\n                            startButton.setVisibility(View.INVISIBLE);\n                        }\n                    });\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/CustomView/JCVideoPlayerStandardShowShareButtonAfterFullscreen.java",
    "content": "package fm.jiecao.jiecaovideoplayer.CustomView;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.Toast;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\nimport fm.jiecao.jiecaovideoplayer.R;\n\n/**\n * Created by Nathen\n * On 2016/04/22 00:54\n */\npublic class JCVideoPlayerStandardShowShareButtonAfterFullscreen extends JCVideoPlayerStandard {\n\n    public ImageView shareButton;\n\n    public JCVideoPlayerStandardShowShareButtonAfterFullscreen(Context context) {\n        super(context);\n    }\n\n    public JCVideoPlayerStandardShowShareButtonAfterFullscreen(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public void init(Context context) {\n        super.init(context);\n        shareButton = (ImageView) findViewById(R.id.share);\n        shareButton.setOnClickListener(this);\n\n    }\n\n    @Override\n    public int getLayoutId() {\n        return R.layout.layout_standard_with_share_button;\n    }\n\n    @Override\n    public void onClick(View v) {\n        super.onClick(v);\n        if (v.getId() == R.id.share) {\n            Toast.makeText(getContext(), \"Whatever the icon means\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    @Override\n    public boolean setUp(String url, int screen, Object... objects) {\n        if (super.setUp(url, screen, objects)) {\n            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n                shareButton.setVisibility(View.VISIBLE);\n            } else {\n                shareButton.setVisibility(View.INVISIBLE);\n            }\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/CustomView/JCVideoPlayerStandardShowTextureViewAfterAutoComplete.java",
    "content": "package fm.jiecao.jiecaovideoplayer.CustomView;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen on 2016/11/6.\n */\n\npublic class JCVideoPlayerStandardShowTextureViewAfterAutoComplete extends JCVideoPlayerStandard {\n    public JCVideoPlayerStandardShowTextureViewAfterAutoComplete(Context context) {\n        super(context);\n    }\n\n    public JCVideoPlayerStandardShowTextureViewAfterAutoComplete(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public void setUiWitStateAndScreen(int state) {\n        super.setUiWitStateAndScreen(state);\n        if (state == CURRENT_STATE_AUTO_COMPLETE) {\n            thumbImageView.setVisibility(View.GONE);\n        }\n    }\n\n    @Override\n    public void onClickUiToggle() {\n        super.onClickUiToggle();\n        if (currentState == CURRENT_STATE_AUTO_COMPLETE) {\n            thumbImageView.setVisibility(View.GONE);\n        }\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/CustomView/JCVideoPlayerStandardShowTitleAfterFullscreen.java",
    "content": "package fm.jiecao.jiecaovideoplayer.CustomView;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen\n * On 2016/04/27 10:49\n */\npublic class JCVideoPlayerStandardShowTitleAfterFullscreen extends JCVideoPlayerStandard {\n    public JCVideoPlayerStandardShowTitleAfterFullscreen(Context context) {\n        super(context);\n    }\n\n    public JCVideoPlayerStandardShowTitleAfterFullscreen(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public boolean setUp(String url, int screen, Object... objects) {\n        if (super.setUp(url, screen, objects)) {\n            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n                titleTextView.setVisibility(View.VISIBLE);\n            } else {\n                titleTextView.setVisibility(View.INVISIBLE);\n            }\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/DemoApplication.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.app.Application;\nimport android.graphics.Bitmap;\nimport android.graphics.Color;\nimport android.graphics.drawable.ColorDrawable;\n\nimport com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator;\nimport com.nostra13.universalimageloader.core.DisplayImageOptions;\nimport com.nostra13.universalimageloader.core.ImageLoader;\nimport com.nostra13.universalimageloader.core.ImageLoaderConfiguration;\nimport com.nostra13.universalimageloader.core.assist.ImageScaleType;\nimport com.nostra13.universalimageloader.core.assist.QueueProcessingType;\nimport com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;\n\n/**\n * Created by Nathen\n * On 2015/12/01 11:29\n */\npublic class DemoApplication extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n//        LeakCanary.install(this);\n        initUniversalImageLoader();\n\n        //it is public static, you can set this everywhere\n        //JCVideoPlayer.TOOL_BAR_EXIST = false;\n        //JCVideoPlayer.ACTION_BAR_EXIST = false;\n    }\n\n    private void initUniversalImageLoader() {\n        ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(getApplicationContext());\n        config.threadPriority(Thread.NORM_PRIORITY - 2);\n        config.denyCacheImageMultipleSizesInMemory();\n        config.diskCacheFileNameGenerator(new Md5FileNameGenerator());\n        config.diskCacheSize(50 * 1024 * 1024); // 50 MiB\n        config.tasksProcessingOrder(QueueProcessingType.LIFO);\n        config.writeDebugLogs(); // Remove for releaseAllVideos app\n        config.defaultDisplayImageOptions(getDefaultDisplayImageOption());\n        // Initialize ImageLoader with configuration.\n        ImageLoader.getInstance().init(config.build());\n    }\n\n    public static DisplayImageOptions getDefaultDisplayImageOption() {\n        DisplayImageOptions options = new DisplayImageOptions.Builder()\n                .showImageOnLoading(new ColorDrawable(Color.parseColor(\"#f0f0f0\")))\n                .resetViewBeforeLoading(true)\n                .cacheInMemory(true)\n                .cacheOnDisk(true)\n                .considerExifParams(true)\n                .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)\n                .bitmapConfig(Bitmap.Config.RGB_565)\n                .displayer(new FadeInBitmapDisplayer(500)) // 设置图片渐显的时间\n//                .delayBeforeLoading(300)  // 下载前的延迟时间\n                .build();\n        return options;\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/ListViewActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\n\n/**\n * Created by Nathen on 16/7/31.\n */\npublic class ListViewActivity extends AppCompatActivity implements View.OnClickListener {\n    Button mNormalList, mViewPagerList, mMultiHolderList, mRecyleView;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"About ListView\");\n        setContentView(R.layout.activity_listview);\n\n        mNormalList = (Button) findViewById(R.id.normal_list);\n        mViewPagerList = (Button) findViewById(R.id.viewpayer_list);\n        mMultiHolderList = (Button) findViewById(R.id.multi_holder_list);\n        mRecyleView = (Button) findViewById(R.id.recyleview);\n\n        mNormalList.setOnClickListener(this);\n        mViewPagerList.setOnClickListener(this);\n        mMultiHolderList.setOnClickListener(this);\n        mRecyleView.setOnClickListener(this);\n\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.normal_list:\n                startActivity(new Intent(ListViewActivity.this, ListViewNormalActivity.class));\n                break;\n            case R.id.viewpayer_list:\n                startActivity(new Intent(ListViewActivity.this, ListViewViewpagerActivity.class));\n                break;\n            case R.id.multi_holder_list:\n                startActivity(new Intent(ListViewActivity.this, ListViewMultiHolderActivity.class));\n                break;\n            case R.id.recyleview:\n                startActivity(new Intent(ListViewActivity.this, RecyclerViewNormalActivity.class));\n                break;\n        }\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/ListViewMultiHolderActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.LayoutInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.squareup.picasso.Picasso;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen\n * On 2016/05/23 21:34\n */\npublic class ListViewMultiHolderActivity extends AppCompatActivity {\n    ListView listView;\n    VideoListAdapter mAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_listview_content);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"MultiHolderListView\");\n\n\n        listView = (ListView) findViewById(R.id.listview);\n        mAdapter = new VideoListAdapter(this);\n        listView.setAdapter(mAdapter);\n    }\n\n\n    public class VideoListAdapter extends BaseAdapter {\n\n        int[] viewtype = {0, 0, 0, 1, 0, 0, 0, 1, 0, 0};//1 = jcvd, 0 = textView\n\n        Context context;\n        LayoutInflater mInflater;\n\n        public VideoListAdapter(Context context) {\n            this.context = context;\n            mInflater = LayoutInflater.from(context);\n        }\n\n        @Override\n        public int getCount() {\n            return viewtype.length;\n        }\n\n        @Override\n        public Object getItem(int position) {\n            return null;\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return position;\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            //This is the point\n            if (convertView != null && convertView.getTag() != null && convertView.getTag() instanceof VideoHolder) {\n                ((VideoHolder) convertView.getTag()).jcVideoPlayer.release();\n            }\n            if (getItemViewType(position) == 1) {\n                VideoHolder viewHolder;\n                if (convertView != null && convertView.getTag() != null && convertView.getTag() instanceof VideoHolder) {\n                    viewHolder = (VideoHolder) convertView.getTag();\n                } else {\n                    viewHolder = new VideoHolder();\n                    convertView = mInflater.inflate(R.layout.item_videoview, null);\n                    viewHolder.jcVideoPlayer = (JCVideoPlayerStandard) convertView.findViewById(R.id.videoplayer);\n                    convertView.setTag(viewHolder);\n                }\n\n                viewHolder.jcVideoPlayer.setUp(\n                        VideoConstant.videoUrls[position], JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                        VideoConstant.videoTitles[position]);\n\n                Picasso.with(ListViewMultiHolderActivity.this)\n                        .load(VideoConstant.videoThumbs[position])\n                        .into(viewHolder.jcVideoPlayer.thumbImageView);\n            } else {\n                TextViewHolder textViewHolder;\n                if (convertView != null && convertView.getTag() != null && convertView.getTag() instanceof TextViewHolder) {\n                    textViewHolder = (TextViewHolder) convertView.getTag();\n                } else {\n                    textViewHolder = new TextViewHolder();\n                    LayoutInflater mInflater = LayoutInflater.from(context);\n                    convertView = mInflater.inflate(R.layout.item_textview, null);\n                    textViewHolder.textView = (TextView) convertView.findViewById(R.id.textview);\n                    convertView.setTag(textViewHolder);\n                }\n            }\n            return convertView;\n        }\n\n        @Override\n        public int getItemViewType(int position) {\n            return viewtype[position];\n        }\n\n        @Override\n        public int getViewTypeCount() {\n            return 2;\n        }\n\n        class VideoHolder {\n            JCVideoPlayerStandard jcVideoPlayer;\n        }\n\n        class TextViewHolder {\n            TextView textView;\n        }\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/ListViewNormalActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.hardware.Sensor;\nimport android.hardware.SensorManager;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.widget.ListView;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\n\n/**\n * Created by Nathen on 16/7/31.\n */\npublic class ListViewNormalActivity extends AppCompatActivity {\n    ListView listView;\n    VideoListAdapter adapterVideoList;\n\n    SensorManager sensorManager;\n    JCVideoPlayer.JCAutoFullscreenListener sensorEventListener;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"NormalListView\");\n        setContentView(R.layout.activity_listview_content);\n\n        listView = (ListView) findViewById(R.id.listview);\n        adapterVideoList = new VideoListAdapter(this);\n        listView.setAdapter(adapterVideoList);\n\n        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);\n        sensorEventListener = new JCVideoPlayer.JCAutoFullscreenListener();\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        Sensor accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);\n        sensorManager.registerListener(sensorEventListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        sensorManager.unregisterListener(sensorEventListener);\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/ListViewViewpagerActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.ListView;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\n\n/**\n * Created by Nathen\n * On 2016/02/07 01:01\n */\npublic class ListViewViewpagerActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_listview_viewpager);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"ViewPagerAndListView\");\n\n        List<View> listViews = new ArrayList<>();\n        ListView listView1 = (ListView) getLayoutInflater().inflate(R.layout.layout_list, null);\n        ListView listView2 = (ListView) getLayoutInflater().inflate(R.layout.layout_list, null);\n        ListView listView3 = (ListView) getLayoutInflater().inflate(R.layout.layout_list, null);\n\n        listView1.setAdapter(new VideoListAdapter(this));\n        listView2.setAdapter(new VideoListAdapter(this));\n        listView3.setAdapter(new VideoListAdapter(this));\n\n        listViews.add(listView1);\n        listViews.add(listView2);\n        listViews.add(listView3);\n\n        MyAdapter myAdapter = new MyAdapter(listViews);\n        ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);\n        viewPager.setAdapter(myAdapter);\n        viewPager.setOnPageChangeListener(this);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {\n\n    }\n\n    @Override\n    public void onPageSelected(int position) {\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public void onPageScrollStateChanged(int state) {\n\n    }\n\n    public class MyAdapter extends PagerAdapter {\n\n        List<View> viewLists;\n\n        public MyAdapter(List<View> lists) {\n            viewLists = lists;\n        }\n\n        @Override\n        public int getCount() {\n            // TODO Auto-generated method stub\n            return viewLists.size();\n        }\n\n        @Override\n        public boolean isViewFromObject(View arg0, Object arg1) {\n            // TODO Auto-generated method stub\n            return arg0 == arg1;\n        }\n\n        @Override\n        public void destroyItem(View view, int position, Object object) {\n            ((ViewPager) view).removeView(viewLists.get(position));\n        }\n\n        @Override\n        public Object instantiateItem(View view, int position) {\n            ((ViewPager) view).addView(viewLists.get(position), 0);\n            return viewLists.get(position);\n        }\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/MainActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.content.Intent;\nimport android.hardware.Sensor;\nimport android.hardware.SensorManager;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Button;\n\nimport com.squareup.picasso.Picasso;\n\nimport fm.jiecao.jcvideoplayer_lib.JCUserAction;\nimport fm.jiecao.jcvideoplayer_lib.JCUserActionStandard;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerSimple;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen on 16/7/22.\n */\npublic class MainActivity extends AppCompatActivity implements View.OnClickListener {\n\n    JCVideoPlayer.JCAutoFullscreenListener mSensorEventListener;\n    SensorManager mSensorManager;\n\n\n    JCVideoPlayerStandard mJcVideoPlayerStandard;\n    JCVideoPlayerSimple mJcVideoPlayerSimple;\n\n    Button mTinyWindow, mAutoTinyWindow, mAboutListView, mAboutUI, mPlayDirectly, mAboutWebView;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        mTinyWindow = (Button) findViewById(R.id.tiny_window);\n        mAutoTinyWindow = (Button) findViewById(R.id.auto_tiny_window);\n        mAboutUI = (Button) findViewById(R.id.play_directly_without_layout);\n        mAboutListView = (Button) findViewById(R.id.about_listview);\n        mPlayDirectly = (Button) findViewById(R.id.about_ui);\n        mAboutWebView = (Button) findViewById(R.id.about_webview);\n\n        mTinyWindow.setOnClickListener(this);\n        mAutoTinyWindow.setOnClickListener(this);\n        mAboutListView.setOnClickListener(this);\n        mAboutUI.setOnClickListener(this);\n        mPlayDirectly.setOnClickListener(this);\n        mAboutWebView.setOnClickListener(this);\n\n        mJcVideoPlayerSimple = (JCVideoPlayerSimple) findViewById(R.id.simple_demo);\n        mJcVideoPlayerSimple.setUp(\"http://devimages.apple.com/iphone/samples/bipbop/gear1/prog_index.m3u8\"\n                , JCVideoPlayerStandard.SCREEN_LAYOUT_NORMAL, \"嫂子在家吗\");\n\n        mJcVideoPlayerStandard = (JCVideoPlayerStandard) findViewById(R.id.jc_video);\n        mJcVideoPlayerStandard.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\"\n                , JCVideoPlayerStandard.SCREEN_LAYOUT_NORMAL, \"嫂子真嘚瑟\");\n        Picasso.with(this)\n                .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                .into(mJcVideoPlayerStandard.thumbImageView);\n        mJcVideoPlayerStandard.looping = true;\n\n        JCVideoPlayer.setJcUserAction(new MyUserActionStandard());\n        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);\n        mSensorEventListener = new JCVideoPlayer.JCAutoFullscreenListener();\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        Sensor accelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);\n        mSensorManager.registerListener(mSensorEventListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        mSensorManager.unregisterListener(mSensorEventListener);\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.tiny_window:\n                mJcVideoPlayerStandard.startWindowTiny();\n                break;\n            case R.id.auto_tiny_window:\n                startActivity(new Intent(MainActivity.this, AutoTinyActivity.class));\n                break;\n            case R.id.play_directly_without_layout:\n                startActivity(new Intent(MainActivity.this, PlayDirectlyActivity.class));\n                break;\n            case R.id.about_listview:\n                startActivity(new Intent(MainActivity.this, ListViewActivity.class));\n                break;\n            case R.id.about_ui:\n                startActivity(new Intent(MainActivity.this, UIActivity.class));\n                break;\n            case R.id.about_webview:\n                startActivity(new Intent(MainActivity.this, WebViewActivity.class));\n                break;\n        }\n    }\n\n    class MyUserActionStandard implements JCUserActionStandard {\n\n        @Override\n        public void onEvent(int type, String url, int screen, Object... objects) {\n            switch (type) {\n                case JCUserAction.ON_CLICK_START_ICON:\n                    Log.i(\"USER_EVENT\", \"ON_CLICK_START_ICON\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_CLICK_START_ERROR:\n                    Log.i(\"USER_EVENT\", \"ON_CLICK_START_ERROR\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_CLICK_START_AUTO_COMPLETE:\n                    Log.i(\"USER_EVENT\", \"ON_CLICK_START_AUTO_COMPLETE\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_CLICK_PAUSE:\n                    Log.i(\"USER_EVENT\", \"ON_CLICK_PAUSE\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_CLICK_RESUME:\n                    Log.i(\"USER_EVENT\", \"ON_CLICK_RESUME\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_SEEK_POSITION:\n                    Log.i(\"USER_EVENT\", \"ON_SEEK_POSITION\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_AUTO_COMPLETE:\n                    Log.i(\"USER_EVENT\", \"ON_AUTO_COMPLETE\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_ENTER_FULLSCREEN:\n                    Log.i(\"USER_EVENT\", \"ON_ENTER_FULLSCREEN\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_QUIT_FULLSCREEN:\n                    Log.i(\"USER_EVENT\", \"ON_QUIT_FULLSCREEN\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_ENTER_TINYSCREEN:\n                    Log.i(\"USER_EVENT\", \"ON_ENTER_TINYSCREEN\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_QUIT_TINYSCREEN:\n                    Log.i(\"USER_EVENT\", \"ON_QUIT_TINYSCREEN\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_TOUCH_SCREEN_SEEK_VOLUME:\n                    Log.i(\"USER_EVENT\", \"ON_TOUCH_SCREEN_SEEK_VOLUME\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserAction.ON_TOUCH_SCREEN_SEEK_POSITION:\n                    Log.i(\"USER_EVENT\", \"ON_TOUCH_SCREEN_SEEK_POSITION\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n\n                case JCUserActionStandard.ON_CLICK_START_THUMB:\n                    Log.i(\"USER_EVENT\", \"ON_CLICK_START_THUMB\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                case JCUserActionStandard.ON_CLICK_BLANK:\n                    Log.i(\"USER_EVENT\", \"ON_CLICK_BLANK\" + \" title is : \" + (objects.length == 0 ? \"\" : objects[0]) + \" url is : \" + url + \" screen is : \" + screen);\n                    break;\n                default:\n                    Log.i(\"USER_EVENT\", \"unknow\");\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/PlayDirectlyActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.Toast;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen on 16/7/31.\n */\npublic class PlayDirectlyActivity extends AppCompatActivity implements View.OnClickListener {\n    Button mStartFullscreen, mStartTiny;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_listview);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"PlayDirectlyWithoutLayout\");\n        setContentView(R.layout.activity_directly_play);\n\n        mStartFullscreen = (Button) findViewById(R.id.fullscreen);\n        mStartTiny = (Button) findViewById(R.id.tiny_window);\n\n        mStartFullscreen.setOnClickListener(this);\n        mStartTiny.setOnClickListener(this);\n\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.fullscreen:\n                JCVideoPlayerStandard.startFullscreen(this, JCVideoPlayerStandard.class, \"http://2449.vod.myqcloud.com/2449_22ca37a6ea9011e5acaaf51d105342e3.f20.mp4\", \"嫂子辛苦了\");\n                break;\n            case R.id.tiny_window:\n                Toast.makeText(PlayDirectlyActivity.this, \"Comming Soon\", Toast.LENGTH_SHORT).show();\n                break;\n        }\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/RecyclerViewNormalActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerManager;\n\n/**\n * Created by yujunkui on 16/8/29.\n */\npublic class RecyclerViewNormalActivity extends AppCompatActivity {\n    RecyclerView recyclerView;\n    RecyclerViewVideoAdapter adapterVideoList;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"NormalRecyclerView\");\n        setContentView(R.layout.activity_recyclerview_content);\n\n        recyclerView = (RecyclerView) findViewById(R.id.recyclerview);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n\n        adapterVideoList = new RecyclerViewVideoAdapter(this);\n        recyclerView.setAdapter(adapterVideoList);\n        recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {\n            @Override\n            public void onChildViewAttachedToWindow(View view) {\n\n            }\n\n            @Override\n            public void onChildViewDetachedFromWindow(View view) {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayer videoPlayer = (JCVideoPlayer) JCVideoPlayerManager.getFirst();\n                    if (((ViewGroup) view).indexOfChild(videoPlayer) != -1 && videoPlayer.currentState == JCVideoPlayer.CURRENT_STATE_PLAYING) {\n                        JCVideoPlayer.releaseAllVideos();\n                    }\n                }\n            }\n        });\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/RecyclerViewVideoAdapter.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.squareup.picasso.Picasso;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\npublic class RecyclerViewVideoAdapter extends RecyclerView.Adapter<RecyclerViewVideoAdapter.MyViewHolder> {\n\n    int[] videoIndexs = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};\n    private Context context;\n    public static final String TAG = \"RecyclerViewVideoAdapter\";\n\n    public RecyclerViewVideoAdapter(Context context) {\n        this.context = context;\n    }\n\n    @Override\n    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        MyViewHolder holder = new MyViewHolder(LayoutInflater.from(\n                context).inflate(R.layout.item_videoview, parent,\n                false));\n        return holder;\n    }\n\n    @Override\n    public void onBindViewHolder(MyViewHolder holder, int position) {\n        Log.i(TAG, \"onBindViewHolder [\" + holder.jcVideoPlayer.hashCode() + \"] position=\" + position);\n\n        holder.jcVideoPlayer.setUp(\n                VideoConstant.videoUrls[position], JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                VideoConstant.videoTitles[position]);\n        Picasso.with(holder.jcVideoPlayer.getContext())\n                .load(VideoConstant.videoThumbs[position])\n                .into(holder.jcVideoPlayer.thumbImageView);\n    }\n\n    @Override\n    public int getItemCount() {\n        return videoIndexs.length;\n    }\n\n    class MyViewHolder extends RecyclerView.ViewHolder {\n        JCVideoPlayerStandard jcVideoPlayer;\n\n        public MyViewHolder(View itemView) {\n            super(itemView);\n            jcVideoPlayer = (JCVideoPlayerStandard) itemView.findViewById(R.id.videoplayer);\n        }\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/UIActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.Toast;\n\n/**\n * Created by Nathen on 16/7/31.\n */\npublic class UIActivity extends AppCompatActivity implements View.OnClickListener {\n    Button mSmallChange, mBigChange, mImageLoader;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"About UI\");\n        setContentView(R.layout.activity_ui);\n\n        mSmallChange = (Button) findViewById(R.id.small_change);\n        mBigChange = (Button) findViewById(R.id.big_change);\n        mImageLoader = (Button) findViewById(R.id.imageloader);\n\n        mSmallChange.setOnClickListener(this);\n        mBigChange.setOnClickListener(this);\n        mImageLoader.setOnClickListener(this);\n\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.small_change:\n                startActivity(new Intent(UIActivity.this, UISmallChangeActivity.class));\n                break;\n            case R.id.big_change:\n                Toast.makeText(UIActivity.this, \"Comming Soon\", Toast.LENGTH_SHORT).show();\n//                startActivity(new Intent(UIActivity.this, UIBigChangeActivity.class));\n                break;\n            case R.id.imageloader:\n                startActivity(new Intent(UIActivity.this, UIImageLoaderActivity.class));\n                break;\n        }\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/UIBigChangeActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\n\n/**\n * Created by Nathen on 16/7/31.\n */\npublic class UIBigChangeActivity extends AppCompatActivity {\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"BigChangeUI\");\n        setContentView(R.layout.activity_ui_big_change);\n\n\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/UIImageLoaderActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.util.LruCache;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.view.View;\n\nimport com.android.volley.RequestQueue;\nimport com.android.volley.toolbox.Volley;\nimport com.bumptech.glide.Glide;\nimport com.facebook.drawee.backends.pipeline.Fresco;\nimport com.nostra13.universalimageloader.core.ImageLoader;\nimport com.squareup.picasso.Picasso;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\nimport fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardFresco;\n\n/**\n * Created by Nathen on 16/7/31.\n */\npublic class UIImageLoaderActivity extends AppCompatActivity {\n    JCVideoPlayerStandard videoController1, videoController2, videoController3, videoController4;\n    JCVideoPlayerStandardFresco videoController5;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Fresco.initialize(this);\n        setContentView(R.layout.activity_loadimage);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"LoadImageDemo\");\n\n        videoController1 = (JCVideoPlayerStandard) findViewById(R.id.videocontroller1);\n        videoController1.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                \"嫂子抓住\");\n        ImageLoader.getInstance().displayImage(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\",\n                videoController1.thumbImageView);\n\n        videoController2 = (JCVideoPlayerStandard) findViewById(R.id.videocontroller2);\n        videoController2.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                \"嫂子别晃\");\n        Glide.with(this)\n                .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                .into(videoController2.thumbImageView);\n\n        videoController3 = (JCVideoPlayerStandard) findViewById(R.id.videocontroller3);\n        videoController3.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                \"嫂子别躲\");\n        Picasso.with(this)\n                .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                .into(videoController3.thumbImageView);\n\n        videoController4 = (JCVideoPlayerStandard) findViewById(R.id.videocontroller4);\n        videoController4.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                \"嫂子别忘了\");\n        RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());\n        com.android.volley.toolbox.ImageLoader imageLoader = new com.android.volley.toolbox.ImageLoader(mQueue, new BitmapCache());\n        com.android.volley.toolbox.ImageLoader.ImageListener listener =\n                com.android.volley.toolbox.ImageLoader.getImageListener(videoController4.thumbImageView, R.mipmap.ic_launcher, R.mipmap.ic_launcher);\n        imageLoader.get(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\", listener);\n\n        videoController5 = (JCVideoPlayerStandardFresco) findViewById(R.id.videocontroller5);\n        videoController5.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                \"嫂子打电话\");\n        Uri uri = Uri.parse(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\");\n        videoController5.thumbImageView.setImageURI(uri);\n\n        View vv = findViewById(R.id.scroll);\n        vv.setOnScrollChangeListener(new View.OnScrollChangeListener() {\n            @Override\n            public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {\n                videoController1.onScrollChange();\n            }\n        });\n    }\n\n    public class BitmapCache implements com.android.volley.toolbox.ImageLoader.ImageCache {\n        private LruCache<String, Bitmap> cache;\n\n        public BitmapCache() {\n            cache = new LruCache<String, Bitmap>(8 * 1024 * 1024) {\n                @Override\n                protected int sizeOf(String key, Bitmap bitmap) {\n                    return bitmap.getRowBytes() * bitmap.getHeight();\n                }\n            };\n        }\n\n        @Override\n        public Bitmap getBitmap(String url) {\n            return cache.get(url);\n        }\n\n        @Override\n        public void putBitmap(String url, Bitmap bitmap) {\n            cache.put(url, bitmap);\n        }\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/UISmallChangeActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\n\nimport com.squareup.picasso.Picasso;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardAutoComplete;\nimport fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardShowShareButtonAfterFullscreen;\nimport fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardShowTextureViewAfterAutoComplete;\nimport fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardShowTitleAfterFullscreen;\n\n/**\n * Created by Nathen on 16/7/31.\n */\npublic class UISmallChangeActivity extends AppCompatActivity {\n    JCVideoPlayerStandardShowShareButtonAfterFullscreen jcVideoPlayerStandardWithShareButton;\n    JCVideoPlayerStandardShowTitleAfterFullscreen jcVideoPlayerStandardShowTitleAfterFullscreen;\n    JCVideoPlayerStandardShowTextureViewAfterAutoComplete jcVideoPlayerStandardShowTextureViewAfterAutoComplete;\n    JCVideoPlayerStandardAutoComplete jcVideoPlayerStandardAutoComplete;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"SmallChangeUI\");\n        setContentView(R.layout.activity_ui_small_change);\n\n        jcVideoPlayerStandardWithShareButton = (JCVideoPlayerStandardShowShareButtonAfterFullscreen) findViewById(R.id.custom_videoplayer_standard_with_share_button);\n        jcVideoPlayerStandardWithShareButton.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_NORMAL\n                , \"嫂子上飞机\");\n        Picasso.with(this)\n                .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                .into(jcVideoPlayerStandardWithShareButton.thumbImageView);\n\n\n        jcVideoPlayerStandardShowTitleAfterFullscreen = (JCVideoPlayerStandardShowTitleAfterFullscreen) findViewById(R.id.custom_videoplayer_standard_show_title_after_fullscreen);\n        jcVideoPlayerStandardShowTitleAfterFullscreen.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_NORMAL\n                , \"嫂子看电视\");\n        Picasso.with(this)\n                .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                .into(jcVideoPlayerStandardShowTitleAfterFullscreen.thumbImageView);\n\n        jcVideoPlayerStandardShowTextureViewAfterAutoComplete = (JCVideoPlayerStandardShowTextureViewAfterAutoComplete) findViewById(R.id.custom_videoplayer_standard_show_textureview_aoto_complete);\n        jcVideoPlayerStandardShowTextureViewAfterAutoComplete.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_NORMAL\n                , \"嫂子还在浪\");\n        Picasso.with(this)\n                .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                .into(jcVideoPlayerStandardShowTextureViewAfterAutoComplete.thumbImageView);\n\n        jcVideoPlayerStandardAutoComplete = (JCVideoPlayerStandardAutoComplete) findViewById(R.id.custom_videoplayer_standard_aoto_complete);\n        jcVideoPlayerStandardAutoComplete.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\", JCVideoPlayer.SCREEN_LAYOUT_NORMAL\n                , \"嫂子没来\");\n        Picasso.with(this)\n                .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                .into(jcVideoPlayerStandardAutoComplete.thumbImageView);\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/VideoConstant.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\n/**\n * Created by shengjun on 16/9/10.\n */\npublic class VideoConstant {\n\n    public static String[] videoUrls = {\n            \"http://video.jiecao.fm/8/17/bGQS3BQQWUYrlzP1K4Tg4Q__.mp4\",\n            \"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\",\n            \"http://video.jiecao.fm/8/18/%E5%A4%A7%E5%AD%A6.mp4\",\n            \"http://video.jiecao.fm/8/16/%E8%B7%B3%E8%88%9E.mp4\",\n            \"http://video.jiecao.fm/8/16/%E9%B8%AD%E5%AD%90.mp4\",\n            \"http://video.jiecao.fm/8/16/%E9%A9%BC%E8%83%8C.mp4\",\n            \"http://video.jiecao.fm/8/16/%E4%BF%AF%E5%8D%A7%E6%92%91.mp4\",\n            \"http://video.jiecao.fm/5/1/%E8%87%AA%E5%8F%96%E5%85%B6%E8%BE%B1.mp4\",\n            \"http://gslb.miaopai.com/stream/ed5HCfnhovu3tyIQAiv60Q__.mp4\",\n            \"http://2449.vod.myqcloud.com/2449_22ca37a6ea9011e5acaaf51d105342e3.f20.mp4\"};\n\n    public static String[] videoThumbs = {\n            \"http://img4.jiecaojingxuan.com/2016/8/17/bd7ffc84-8407-4037-a078-7d922ce0fb0f.jpg\",\n            \"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\",\n            \"http://img4.jiecaojingxuan.com/2016/8/18/ccd86ca1-66c7-4331-9450-a3b7f765424a.png\",\n            \"http://img4.jiecaojingxuan.com/2016/8/16/2adde364-9be1-4864-b4b9-0b0bcc81ef2e.jpg\",\n            \"http://img4.jiecaojingxuan.com/2016/8/16/2a877211-4b68-4e3a-87be-6d2730faef27.png\",\n            \"http://img4.jiecaojingxuan.com/2016/8/16/aaeb5da9-ac50-4712-a28d-863fe40f1fc6.png\",\n            \"http://img4.jiecaojingxuan.com/2016/8/16/e565f9cc-eedc-45f0-99f8-5b0fa3aed567.jpg\",\n            \"http://img4.jiecaojingxuan.com/2016/5/1/3430ec64-e6a7-4d8e-b044-9d408e075b7c.jpg\",\n            \"http://img4.jiecaojingxuan.com/2016/3/14/2204a578-609b-440e-8af7-a0ee17ff3aee.jpg\",\n            \"http://cos.myqcloud.com/1000264/qcloud_video_attachment/842646334/vod_cover/cover1458036374.jpg\"};\n\n    public static String[] videoTitles = {\n            \"嫂子出来\",\n            \"嫂子溢出\",\n            \"嫂子我姓王\",\n            \"嫂子趴好了\",\n            \"嫂子很渴\",\n            \"嫂子这样不好\",\n            \"嫂子别笑\",\n            \"嫂子坐火车\",\n            \"嫂子打游戏\",\n            \"嫂子稳当的\"};\n\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/VideoListAdapter.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.content.Context;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\n\nimport com.squareup.picasso.Picasso;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen\n * On 2016/02/07 01:20\n */\npublic class VideoListAdapter extends BaseAdapter {\n\n    public static final String TAG = \"JieCaoVideoPlayer\";\n\n    int[] videoIndexs = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};\n    Context context;\n\n    public VideoListAdapter(Context context) {\n        this.context = context;\n    }\n\n    @Override\n    public int getCount() {\n        return videoIndexs.length;\n    }\n\n    @Override\n    public Object getItem(int position) {\n        return null;\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return position;\n    }\n\n    @Override\n    public View getView(int position, View convertView, ViewGroup parent) {\n        Log.e(TAG, \"why you always getview\");\n\n        ViewHolder viewHolder;\n        if (null == convertView) {\n            viewHolder = new ViewHolder();\n            LayoutInflater mInflater = LayoutInflater.from(context);\n            convertView = mInflater.inflate(R.layout.item_videoview, null);\n            viewHolder.jcVideoPlayer = (JCVideoPlayerStandard) convertView.findViewById(R.id.videoplayer);\n            convertView.setTag(viewHolder);\n        } else {\n            viewHolder = (ViewHolder) convertView.getTag();\n        }\n\n        viewHolder.jcVideoPlayer.setUp(\n                VideoConstant.videoUrls[position], JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                VideoConstant.videoTitles[position]);\n\n        Picasso.with(convertView.getContext())\n                .load(VideoConstant.videoThumbs[position])\n                .into(viewHolder.jcVideoPlayer.thumbImageView);\n        return convertView;\n    }\n\n    class ViewHolder {\n        JCVideoPlayerStandard jcVideoPlayer;\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/java/fm/jiecao/jiecaovideoplayer/WebViewActivity.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MenuItem;\nimport android.view.ViewGroup;\nimport android.webkit.JavascriptInterface;\nimport android.webkit.WebView;\nimport android.widget.AbsoluteLayout;\n\nimport com.squareup.picasso.Picasso;\n\nimport fm.jiecao.jcvideoplayer_lib.JCUtils;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * Created by Nathen on 16/10/13.\n */\n\npublic class WebViewActivity extends AppCompatActivity {\n    WebView mWebView;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        getSupportActionBar().setDisplayShowHomeEnabled(true);\n        getSupportActionBar().setDisplayShowTitleEnabled(true);\n        getSupportActionBar().setDisplayUseLogoEnabled(false);\n        getSupportActionBar().setTitle(\"AboutWebView\");\n        setContentView(R.layout.activity_webview);\n        mWebView = (WebView) findViewById(R.id.webview);\n        mWebView.getSettings().setJavaScriptEnabled(true);\n        mWebView.addJavascriptInterface(new JCCallBack(), \"jcvd\");\n        mWebView.loadUrl(\"file:///android_asset/jcvd.html\");\n    }\n\n    public class JCCallBack {\n\n        @JavascriptInterface\n        public void adViewJieCaoVideoPlayer(final int width, final int height, final int top, final int left) {\n            runOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    JCVideoPlayerStandard webVieo = new JCVideoPlayerStandard(WebViewActivity.this);\n                    webVieo.setUp(\"http://video.jiecao.fm/8/17/%E6%8A%AB%E8%90%A8.mp4\",\n                            JCVideoPlayer.SCREEN_LAYOUT_LIST, \"嫂子好困\");\n                    Picasso.with(WebViewActivity.this)\n                            .load(\"http://img4.jiecaojingxuan.com/2016/8/17/f2dbd12e-b1cb-4daf-aff1-8c6be2f64d1a.jpg\")\n                            .into(webVieo.thumbImageView);\n                    ViewGroup.LayoutParams ll = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);\n                    AbsoluteLayout.LayoutParams layoutParams = new AbsoluteLayout.LayoutParams(ll);\n                    layoutParams.y = JCUtils.dip2px(WebViewActivity.this, top);\n                    layoutParams.x = JCUtils.dip2px(WebViewActivity.this, left);\n                    layoutParams.height = JCUtils.dip2px(WebViewActivity.this, height);\n                    layoutParams.width = JCUtils.dip2px(WebViewActivity.this, width);\n                    mWebView.addView(webVieo, layoutParams);\n                }\n            });\n\n        }\n    }\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case android.R.id.home:\n                finish();\n                break;\n        }\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/drawable/share_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@mipmap/share_normal\" android:state_pressed=\"false\" />\n    <item android:drawable=\"@mipmap/share_pressed\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@mipmap/share_normal\" />\n</selector>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/drawable/skin_seek_progress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:id=\"@android:id/background\">\n        <shape>\n            <solid android:color=\"#cc999999\" />\n            <size android:height=\"3.0dip\" />\n            <corners android:radius=\"1.5dip\" />\n        </shape>\n    </item>\n    <item android:id=\"@android:id/secondaryProgress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#9f4ef1\" />\n                <size android:height=\"3.0dip\" />\n                <corners android:radius=\"1.5dip\" />\n            </shape>\n        </clip>\n    </item>\n    <item android:id=\"@android:id/progress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#0add2c\" />\n                <size android:height=\"3.0dip\" />\n                <corners android:radius=\"1.5dip\" />\n            </shape>\n        </clip>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_auto_tiny.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <Button\n        android:id=\"@+id/screen_normal\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Screen normal\" />\n\n    <Button\n        android:id=\"@+id/screen_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"Screen list\" />\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_auto_tiny_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_directly_play.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <Button\n        android:id=\"@+id/fullscreen\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Fullscreen\" />\n\n    <Button\n        android:id=\"@+id/tiny_window\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"TinyWindow\" />\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_listview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <Button\n        android:id=\"@+id/normal_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Normal ListView\" />\n\n    <Button\n        android:id=\"@+id/viewpayer_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"ViewPager And ListView\" />\n\n    <Button\n        android:id=\"@+id/multi_holder_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"Multi Holder ListView\" />\n\n    <Button\n        android:id=\"@+id/recyleview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"Recyleview\" />\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_listview_content.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <ListView\n        android:id=\"@+id/listview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:scrollbars=\"none\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_listview_viewpager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <android.support.v4.view.ViewPager\n        android:id=\"@+id/viewPager\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_loadimage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"3dp\">\n\n    <ScrollView\n        android:id=\"@+id/scroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"none\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"3dp\"\n                android:text=\"ImageLoader\" />\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\">\n\n                <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n                    android:id=\"@+id/videocontroller1\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"200dp\" />\n            </LinearLayout>\n\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Glide\" />\n\n            <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n                android:id=\"@+id/videocontroller2\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\"\n                android:layout_marginTop=\"2dp\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Picasso\" />\n\n            <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n                android:id=\"@+id/videocontroller3\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\"\n                android:layout_marginTop=\"2dp\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Volley\" />\n\n            <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n                android:id=\"@+id/videocontroller4\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\"\n                android:layout_marginTop=\"2dp\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Fresco\" />\n\n            <fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardFresco\n                android:id=\"@+id/videocontroller5\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\"\n                android:layout_marginTop=\"2dp\" />\n        </LinearLayout>\n    </ScrollView>\n\n</LinearLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"none\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"3dp\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Base simple ui example\" />\n\n            <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerSimple\n                android:id=\"@+id/simple_demo\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"3dp\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Recommended standard ui example\" />\n\n            <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n                android:id=\"@+id/jc_video\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\" />\n\n            <Button\n                android:id=\"@+id/tiny_window\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"8dp\"\n                android:text=\"VideoPlayer above is playing click me to tiny window\" />\n\n            <Button\n                android:id=\"@+id/auto_tiny_window\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"8dp\"\n                android:text=\"Auto tiny window\" />\n\n            <Button\n                android:id=\"@+id/play_directly_without_layout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"8dp\"\n                android:text=\"Play Directly Without Layout\" />\n\n            <Button\n                android:id=\"@+id/about_listview\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginTop=\"8dp\"\n                android:text=\"About ListView\" />\n\n            <Button\n                android:id=\"@+id/about_ui\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginBottom=\"8dp\"\n                android:layout_marginTop=\"8dp\"\n                android:text=\"About Ui\" />\n\n            <Button\n                android:id=\"@+id/about_webview\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center_horizontal\"\n                android:layout_marginBottom=\"8dp\"\n                android:text=\"About WebView\" />\n        </LinearLayout>\n    </ScrollView>\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_recyclerview_content.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <android.support.v7.widget.RecyclerView\n        android:id=\"@+id/recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"none\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_ui.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <Button\n        android:id=\"@+id/small_change\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Small change Ui\" />\n\n    <Button\n        android:id=\"@+id/big_change\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"Big change Ui\" />\n\n    <Button\n        android:id=\"@+id/imageloader\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_horizontal\"\n        android:text=\"ImageLoader\" />\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_ui_big_change.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:orientation=\"vertical\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_ui_small_change.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <ScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"3dp\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Standard ui with share button, visible after fullscreen\" />\n\n            <fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardShowShareButtonAfterFullscreen\n                android:id=\"@+id/custom_videoplayer_standard_with_share_button\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"3dp\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Standard ui show title after fullscreen\" />\n\n            <fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardShowTitleAfterFullscreen\n                android:id=\"@+id/custom_videoplayer_standard_show_title_after_fullscreen\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"3dp\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Standard ui show textureview after auto complete\" />\n\n            <fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardShowTextureViewAfterAutoComplete\n                android:id=\"@+id/custom_videoplayer_standard_show_textureview_aoto_complete\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"3dp\"\n                android:layout_marginTop=\"18dp\"\n                android:text=\"Standard ui show textureview after auto complete\" />\n\n            <fm.jiecao.jiecaovideoplayer.CustomView.JCVideoPlayerStandardAutoComplete\n                android:id=\"@+id/custom_videoplayer_standard_aoto_complete\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"200dp\" />\n        </LinearLayout>\n    </ScrollView>\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/activity_webview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <WebView\n        android:id=\"@+id/webview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/header_auto_tiny_normal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n        android:id=\"@+id/jc_video\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\" />\n\n</LinearLayout>"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/item_textview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/textview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\"\n        android:gravity=\"center\"\n        android:text=\"This is a holder with textview\"\n        android:textSize=\"18sp\"\n        android:textStyle=\"bold\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/item_videoview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n        android:id=\"@+id/videoplayer\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/layout_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ListView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/listview\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:scrollbars=\"none\" />\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/layout_standard_fresco.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/black\">\n\n    <RelativeLayout\n        android:id=\"@+id/surface_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\">\n\n    </RelativeLayout>\n\n\n    <fm.jiecao.jcvideoplayer_lib.JCResizeImageView\n        android:id=\"@+id/cache\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\" />\n\n    <ImageView\n        android:id=\"@+id/cover\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentStart=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"#222222\" />\n\n    <com.facebook.drawee.view.SimpleDraweeView\n        android:id=\"@+id/thumb\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentStart=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"#000000\"\n        android:scaleType=\"fitCenter\" />\n\n    <LinearLayout\n        android:id=\"@+id/layout_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"#99000000\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:visibility=\"invisible\">\n\n        <TextView\n            android:id=\"@+id/current\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <SeekBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_weight=\"1.0\"\n            android:background=\"@null\"\n            android:max=\"100\"\n            android:maxHeight=\"4dp\"\n            android:minHeight=\"4dp\"\n            android:paddingBottom=\"8dp\"\n            android:paddingTop=\"8dp\"\n            android:progressDrawable=\"@drawable/jc_seek_progress\"\n            android:thumb=\"@drawable/jc_seek_thumb\" />\n\n        <TextView\n            android:id=\"@+id/total\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <ImageView\n            android:id=\"@+id/fullscreen\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"fill_parent\"\n            android:paddingRight=\"16dp\"\n            android:scaleType=\"center\"\n            android:src=\"@drawable/jc_enlarge\" />\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/bottom_progressbar\"\n        style=\"?android:attr/progressBarStyleHorizontal\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1.5dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:max=\"100\"\n        android:progressDrawable=\"@drawable/jc_progress\" />\n\n    <ImageView\n        android:id=\"@+id/back_tiny\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_marginLeft=\"6dp\"\n        android:layout_marginTop=\"6dp\"\n        android:background=\"@drawable/jc_click_back_tiny_selector\"\n        android:visibility=\"visible\" />\n\n    <LinearLayout\n        android:id=\"@+id/layout_top\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:background=\"@drawable/jc_title_bg\"\n        android:gravity=\"center_vertical\">\n\n        <ImageView\n            android:id=\"@+id/back\"\n            android:layout_width=\"48dp\"\n            android:layout_height=\"48dp\"\n            android:paddingLeft=\"10dp\"\n            android:scaleType=\"centerInside\"\n            android:src=\"@drawable/jc_back\" />\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:paddingLeft=\"10dp\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"18sp\" />\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/loading\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:indeterminateDrawable=\"@drawable/jc_loading\"\n        android:visibility=\"invisible\" />\n\n    <ImageView\n        android:id=\"@+id/start\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_gravity=\"center_vertical\"\n        android:src=\"@drawable/jc_click_play_selector\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/layout/layout_standard_with_share_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/black\">\n\n    <RelativeLayout\n        android:id=\"@+id/surface_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\">\n\n    </RelativeLayout>\n\n\n    <fm.jiecao.jcvideoplayer_lib.JCResizeImageView\n        android:id=\"@+id/cache\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\" />\n\n    <ImageView\n        android:id=\"@+id/cover\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentStart=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"#222222\" />\n\n    <ImageView\n        android:id=\"@+id/thumb\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentStart=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"#000000\"\n        android:scaleType=\"fitCenter\" />\n\n    <LinearLayout\n        android:id=\"@+id/layout_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"#99000000\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:visibility=\"invisible\">\n\n        <TextView\n            android:id=\"@+id/current\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <SeekBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_weight=\"1.0\"\n            android:background=\"@null\"\n            android:max=\"100\"\n            android:maxHeight=\"4dp\"\n            android:minHeight=\"4dp\"\n            android:paddingBottom=\"8dp\"\n            android:paddingTop=\"8dp\"\n            android:progressDrawable=\"@drawable/jc_seek_progress\"\n            android:thumb=\"@drawable/jc_seek_thumb\" />\n\n        <TextView\n            android:id=\"@+id/total\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <ImageView\n            android:id=\"@+id/fullscreen\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"fill_parent\"\n            android:paddingRight=\"16dp\"\n            android:scaleType=\"center\"\n            android:src=\"@drawable/jc_enlarge\" />\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/bottom_progressbar\"\n        style=\"?android:attr/progressBarStyleHorizontal\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1.5dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:max=\"100\"\n        android:progressDrawable=\"@drawable/jc_progress\" />\n\n    <ImageView\n        android:id=\"@+id/back_tiny\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_marginLeft=\"6dp\"\n        android:layout_marginTop=\"6dp\"\n        android:background=\"@drawable/jc_click_back_tiny_selector\"\n        android:visibility=\"visible\" />\n\n    <RelativeLayout\n        android:id=\"@+id/layout_top\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:background=\"@drawable/jc_title_bg\"\n        android:gravity=\"center_vertical\">\n\n        <ImageView\n            android:id=\"@+id/back\"\n            android:layout_width=\"48dp\"\n            android:layout_height=\"48dp\"\n            android:paddingLeft=\"10dp\"\n            android:scaleType=\"centerInside\"\n            android:src=\"@drawable/jc_back\" />\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toEndOf=\"@+id/back\"\n            android:layout_toRightOf=\"@+id/back\"\n            android:paddingLeft=\"10dp\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"18sp\" />\n\n        <ImageView\n            android:id=\"@+id/share\"\n            android:layout_width=\"48dp\"\n            android:layout_height=\"48dp\"\n            android:layout_alignParentEnd=\"true\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_marginEnd=\"35dp\"\n            android:layout_marginRight=\"35dp\"\n            android:padding=\"6dp\"\n            android:src=\"@drawable/share_selector\" />\n    </RelativeLayout>\n\n    <ProgressBar\n        android:id=\"@+id/loading\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:indeterminateDrawable=\"@drawable/jc_loading\"\n        android:visibility=\"invisible\" />\n\n    <ImageView\n        android:id=\"@+id/start\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_gravity=\"center_vertical\"\n        android:src=\"@drawable/jc_click_play_selector\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n    <color name=\"bottom_bg\">#9900ff00</color>\n</resources>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">JCVD</string>\n    <string name=\"app_name_full\">JieCaoVideoPlayer</string>\n</resources>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "JieCaoVideoPlayer-develop/src/test/java/fm/jiecao/jiecaovideoplayer/ExampleUnitTest.java",
    "content": "package fm.jiecao.jiecaovideoplayer;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/.gitignore",
    "content": "/build\n/bin\n\n"
  },
  {
    "path": "MaterialRefresh_library/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n    resourcePrefix \"MaterialRefreshLayout\"\t//这个随便填\n    defaultConfig {\n        minSdkVersion 11\n        targetSdkVersion 22\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'com.android.support:support-v4:23.1.1'\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"com.cjj\"/>\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/CircleProgressBar.java",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.cjj;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.RadialGradient;\nimport android.graphics.Shader;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.ShapeDrawable;\nimport android.graphics.drawable.shapes.OvalShape;\nimport android.net.Uri;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.widget.ImageView;\n\npublic class CircleProgressBar extends ImageView implements MaterialHeadListener {\n\n    private static final int KEY_SHADOW_COLOR = 0x1E000000;\n    private static final int FILL_SHADOW_COLOR = 0x3D000000;\n    // PX\n    private static final float X_OFFSET = 0f;\n    private static final float Y_OFFSET = 1.75f;\n    private static final float SHADOW_RADIUS = 3.5f;\n    private static final int SHADOW_ELEVATION = 4;\n\n\n    public static final int DEFAULT_CIRCLE_BG_LIGHT = 0xFFFAFAFA;\n    private static final int DEFAULT_CIRCLE_DIAMETER = 40;\n    private static final int STROKE_WIDTH_LARGE = 3;\n    public static final int DEFAULT_TEXT_SIZE = 9;\n\n    private Animation.AnimationListener mListener;\n    private int mShadowRadius;\n    private int mBackGroundColor;\n    private int mProgressColor;\n    private int mProgressStokeWidth;\n    private int mArrowWidth;\n    private int mArrowHeight;\n    private int mProgress;\n    private int mMax;\n    private int mDiameter;\n    private int mInnerRadius;\n    private Paint mTextPaint;\n    private int mTextColor;\n    private int mTextSize;\n    private boolean mIfDrawText;\n    private boolean mShowArrow;\n    public MaterialProgressDrawable mProgressDrawable;\n    private ShapeDrawable mBgCircle;\n    private boolean mCircleBackgroundEnabled;\n    private int[] mColors = new int[]{Color.BLACK};\n\n    public CircleProgressBar(Context context) {\n        super(context);\n        init(context, null, 0);\n\n    }\n\n    public CircleProgressBar(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context, attrs, 0);\n\n    }\n\n    public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\n        final TypedArray a = context.obtainStyledAttributes(\n                attrs, R.styleable.CircleProgressBar, defStyleAttr, 0);\n\n        final float density = getContext().getResources().getDisplayMetrics().density;\n\n        mBackGroundColor = a.getColor(\n                R.styleable.CircleProgressBar_mlpb_background_color, DEFAULT_CIRCLE_BG_LIGHT);\n\n        mProgressColor = a.getColor(\n                R.styleable.CircleProgressBar_mlpb_progress_color, DEFAULT_CIRCLE_BG_LIGHT);\n        mColors = new int[]{mProgressColor};\n\n        mInnerRadius = a.getDimensionPixelOffset(\n                R.styleable.CircleProgressBar_mlpb_inner_radius, -1);\n\n        mProgressStokeWidth = a.getDimensionPixelOffset(\n                R.styleable.CircleProgressBar_mlpb_progress_stoke_width, (int) (STROKE_WIDTH_LARGE * density));\n        mArrowWidth = a.getDimensionPixelOffset(\n                R.styleable.CircleProgressBar_mlpb_arrow_width, -1);\n        mArrowHeight = a.getDimensionPixelOffset(\n                R.styleable.CircleProgressBar_mlpb_arrow_height, -1);\n        mTextSize = a.getDimensionPixelOffset(\n                R.styleable.CircleProgressBar_mlpb_progress_text_size, (int) (DEFAULT_TEXT_SIZE * density));\n        mTextColor = a.getColor(\n                R.styleable.CircleProgressBar_mlpb_progress_text_color, Color.BLACK);\n\n        mShowArrow = a.getBoolean(R.styleable.CircleProgressBar_mlpb_show_arrow, false);\n        mCircleBackgroundEnabled = a.getBoolean(R.styleable.CircleProgressBar_mlpb_enable_circle_background, true);\n\n\n        mProgress = a.getInt(R.styleable.CircleProgressBar_mlpb_progress, 0);\n        mMax = a.getInt(R.styleable.CircleProgressBar_mlpb_max, 100);\n        int textVisible = a.getInt(R.styleable.CircleProgressBar_mlpb_progress_text_visibility, 1);\n        if (textVisible != 1) {\n            mIfDrawText = true;\n        }\n\n        mTextPaint = new Paint();\n        mTextPaint.setStyle(Paint.Style.FILL);\n        mTextPaint.setColor(mTextColor);\n        mTextPaint.setTextSize(mTextSize);\n        mTextPaint.setAntiAlias(true);\n        a.recycle();\n        mProgressDrawable = new MaterialProgressDrawable(getContext(), this);\n        mProgressDrawable.setStartEndTrim(0, (float) 0.75);\n        super.setImageDrawable(mProgressDrawable);\n    }\n\n    public void setProgressBackGroundColor(int color) {\n        this.mBackGroundColor = color;\n        invalidate();\n    }\n\n    public void setTextColor(int color) {\n        this.mTextColor = color;\n    }\n\n    private boolean elevationSupported() {\n        return android.os.Build.VERSION.SDK_INT >= 21;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        if (!elevationSupported()) {\n            setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight()\n                    + mShadowRadius * 2);\n        }\n    }\n\n    public int getProgressStokeWidth() {\n        return mProgressStokeWidth;\n    }\n\n    public void setProgressStokeWidth(int mProgressStokeWidth) {\n        final float density = getContext().getResources().getDisplayMetrics().density;\n        this.mProgressStokeWidth = (int) (mProgressStokeWidth * density);\n        invalidate();\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n        final float density = getContext().getResources().getDisplayMetrics().density;\n        mDiameter = Math.min(getMeasuredWidth(), getMeasuredHeight());\n        if (mDiameter <= 0) {\n            mDiameter = (int) density * DEFAULT_CIRCLE_DIAMETER;\n        }\n        if (getBackground() == null && mCircleBackgroundEnabled) {\n            final int shadowYOffset = (int) (density * Y_OFFSET);\n            final int shadowXOffset = (int) (density * X_OFFSET);\n            mShadowRadius = (int) (density * SHADOW_RADIUS);\n\n            if (elevationSupported()) {\n                mBgCircle = new ShapeDrawable(new OvalShape());\n                ViewCompat.setElevation(this, SHADOW_ELEVATION * density);\n            } else {\n                OvalShape oval = new OvalShadow(mShadowRadius, mDiameter - mShadowRadius * 2);\n                mBgCircle = new ShapeDrawable(oval);\n                ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, mBgCircle.getPaint());\n                mBgCircle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,\n                        KEY_SHADOW_COLOR);\n                final int padding = (int) mShadowRadius;\n                // set padding so the inner image sits correctly within the shadow.\n                setPadding(padding, padding, padding, padding);\n            }\n            mBgCircle.getPaint().setColor(mBackGroundColor);\n            setBackgroundDrawable(mBgCircle);\n        }\n        mProgressDrawable.setBackgroundColor(mBackGroundColor);\n        mProgressDrawable.setColorSchemeColors(mColors);\n        mProgressDrawable.setSizeParameters(mDiameter, mDiameter,\n                mInnerRadius <= 0 ? (mDiameter - mProgressStokeWidth * 2) / 4 : mInnerRadius,\n                mProgressStokeWidth,\n                mArrowWidth < 0 ? mProgressStokeWidth * 4 : mArrowWidth,\n                mArrowHeight < 0 ? mProgressStokeWidth * 2 : mArrowHeight);\n        if (isShowArrow()) {\n            mProgressDrawable.showArrowOnFirstStart(true);\n            mProgressDrawable.setArrowScale(1f);\n            mProgressDrawable.showArrow(true);\n        }\n        super.setImageDrawable(null);\n        super.setImageDrawable(mProgressDrawable);\n        mProgressDrawable.setAlpha(255);\n        if (getVisibility() == VISIBLE) {\n            mProgressDrawable.setStartEndTrim(0, (float) 0.8);\n        }\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        if (mIfDrawText) {\n            String text = String.format(\"%s%%\", mProgress);\n            int x = getWidth() / 2 - text.length() * mTextSize / 4;\n            int y = getHeight() / 2 + mTextSize / 4;\n            canvas.drawText(text, x, y, mTextPaint);\n        }\n    }\n\n    @Override\n    final public void setImageResource(int resId) {\n\n    }\n\n\n    public boolean isShowArrow() {\n        return mShowArrow;\n    }\n\n    public void setShowArrow(boolean showArrow) {\n        this.mShowArrow = showArrow;\n        invalidate();\n    }\n\n\n    @Override\n    final public void setImageURI(Uri uri) {\n        super.setImageURI(uri);\n    }\n\n    @Override\n    final public void setImageDrawable(Drawable drawable) {\n    }\n\n    public void setAnimationListener(Animation.AnimationListener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    public void onAnimationStart() {\n        super.onAnimationStart();\n        if (mListener != null) {\n            mListener.onAnimationStart(getAnimation());\n        }\n    }\n\n    @Override\n    public void onAnimationEnd() {\n        super.onAnimationEnd();\n        if (mListener != null) {\n            mListener.onAnimationEnd(getAnimation());\n        }\n    }\n\n\n    /**\n     * Set the color resources used in the progress animation from color resources.\n     * The first color will also be the color of the bar that grows in response\n     * to a user swipe gesture.\n     *\n     * @param colorResIds\n     */\n    public void setColorSchemeResources(int... colorResIds) {\n        final Resources res = getResources();\n        int[] colorRes = new int[colorResIds.length];\n        for (int i = 0; i < colorResIds.length; i++) {\n            colorRes[i] = res.getColor(colorResIds[i]);\n        }\n        setColorSchemeColors(colorRes);\n    }\n\n    /**\n     * Set the colors used in the progress animation. The first\n     * color will also be the color of the bar that grows in response to a user\n     * swipe gesture.\n     *\n     * @param colors\n     */\n    public void setColorSchemeColors(int... colors) {\n        mColors = colors;\n        if (mProgressDrawable != null) {\n            mProgressDrawable.setColorSchemeColors(colors);\n        }\n    }\n\n    /**\n     * Update the background color of the mBgCircle image view.\n     */\n    public void setBackgroundColor(int colorRes) {\n        if (getBackground() instanceof ShapeDrawable) {\n            final Resources res = getResources();\n            ((ShapeDrawable) getBackground()).getPaint().setColor(res.getColor(colorRes));\n        }\n    }\n\n    public boolean isShowProgressText() {\n        return mIfDrawText;\n    }\n\n    public void setShowProgressText(boolean mIfDrawText) {\n        this.mIfDrawText = mIfDrawText;\n    }\n\n    public int getMax() {\n        return mMax;\n    }\n\n    public void setMax(int max) {\n        mMax = max;\n    }\n\n    public int getProgress() {\n        return mProgress;\n    }\n\n    public void setProgress(int progress) {\n        if (getMax() > 0) {\n            mProgress = progress;\n        }\n        invalidate();\n        Log.i(\"cjj_log\", \"progress------->>>>\" + progress);\n    }\n\n\n    public boolean circleBackgroundEnabled() {\n        return mCircleBackgroundEnabled;\n    }\n\n    public void setCircleBackgroundEnabled(boolean enableCircleBackground) {\n        this.mCircleBackgroundEnabled = enableCircleBackground;\n        invalidate();\n    }\n\n    @Override\n    public int getVisibility() {\n        return super.getVisibility();\n    }\n\n    @Override\n    public void setVisibility(int visibility) {\n        super.setVisibility(visibility);\n//        if (mProgressDrawable != null) {\n//            mProgressDrawable.setVisible(visibility == VISIBLE, false);\n//            if (visibility != VISIBLE) {\n//                mProgressDrawable.stop();\n//            } else {\n//                if (mProgressDrawable.isRunning()) {\n//                    mProgressDrawable.stop();\n//                }\n//                mProgressDrawable.start();\n//            }\n//        }\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        if (mProgressDrawable != null) {\n            mProgressDrawable.stop();\n            mProgressDrawable.setVisible(getVisibility() == VISIBLE, false);\n        }\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        if (mProgressDrawable != null) {\n            mProgressDrawable.stop();\n            mProgressDrawable.setVisible(false, false);\n        }\n    }\n\n    @Override\n    public void onComlete(MaterialRefreshLayout materialRefreshLayout) {\n        if (mProgressDrawable != null) {\n            mProgressDrawable.stop();\n        }\n        setVisibility(View.INVISIBLE);\n    }\n\n    @Override\n    public void onBegin(MaterialRefreshLayout materialRefreshLayout) {\n        setVisibility(View.VISIBLE);\n//        mProgressDrawable.setStartEndTrim(0, (float) 0.75);\n    }\n\n    @Override\n    public void onPull(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n\n        mProgressDrawable.setProgressRotation(fraction);\n    }\n\n    @Override\n    public void onRelease(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n\n    }\n\n    @Override\n    public void onRefreshing(MaterialRefreshLayout materialRefreshLayout) {\n        if (mProgressDrawable != null) {\n            mProgressDrawable.start();\n        }\n    }\n\n\n    private class OvalShadow extends OvalShape {\n        private RadialGradient mRadialGradient;\n        private int mShadowRadius;\n        private Paint mShadowPaint;\n        private int mCircleDiameter;\n\n        public OvalShadow(int shadowRadius, int circleDiameter) {\n            super();\n            mShadowPaint = new Paint();\n            mShadowRadius = shadowRadius;\n            mCircleDiameter = circleDiameter;\n            mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,\n                    mShadowRadius, new int[]{\n                    FILL_SHADOW_COLOR, Color.TRANSPARENT\n            }, null, Shader.TileMode.CLAMP);\n            mShadowPaint.setShader(mRadialGradient);\n        }\n\n        @Override\n        public void draw(Canvas canvas, Paint paint) {\n            final int viewWidth = CircleProgressBar.this.getWidth();\n            final int viewHeight = CircleProgressBar.this.getHeight();\n            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),\n                    mShadowPaint);\n            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);\n        }\n    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialFoodView.java",
    "content": "package com.cjj;\n\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\npublic class MaterialFoodView extends FrameLayout implements MaterialHeadListener {\n    private MaterialWaveView materialWaveView;\n    private CircleProgressBar circleProgressBar;\n    private int waveColor;\n    private int progressTextColor;\n    private int[] progress_colors;\n    private int progressStokeWidth;\n    private boolean isShowArrow, isShowProgressBg;\n    private int progressValue, progressValueMax;\n    private int textType;\n    private int progressBg;\n    private int progressSize;\n    private MaterialHeadListener listener;\n\n\n    public MaterialFoodView(Context context) {\n        this(context, null);\n    }\n\n    public MaterialFoodView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MaterialFoodView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init(attrs, defStyle);\n    }\n\n\n    protected void init(AttributeSet attrs, int defStyle) {\n        if (isInEditMode()) return;\n        setClipToPadding(false);\n        setWillNotDraw(false);\n    }\n\n    public int getWaveColor() {\n        return waveColor;\n    }\n\n    public void setWaveColor(int waveColor) {\n        this.waveColor = waveColor;\n        if (null != materialWaveView) {\n            materialWaveView.setColor(this.waveColor);\n        }\n    }\n\n    public void setProgressSize(int progressSize) {\n        this.progressSize = progressSize;\n    }\n\n    public void setProgressBg(int progressBg) {\n        this.progressBg = progressBg;\n    }\n\n    public void setIsProgressBg(boolean isShowProgressBg) {\n        this.isShowProgressBg = isShowProgressBg;\n    }\n\n    public void setProgressTextColor(int textColor) {\n        this.progressTextColor = textColor;\n    }\n\n    public void setProgressColors(int[] colors) {\n        this.progress_colors = colors;\n    }\n\n    public void setTextType(int textType) {\n        this.textType = textType;\n    }\n\n    public void setProgressValue(int value) {\n        this.progressValue = value;\n        this.post(new Runnable() {\n            @Override\n            public void run() {\n                if (circleProgressBar != null) {\n                    circleProgressBar.setProgress(progressValue);\n                }\n            }\n        });\n\n    }\n\n    public void setProgressValueMax(int value) {\n        this.progressValueMax = value;\n    }\n\n    public void setProgressStokeWidth(int w) {\n        this.progressStokeWidth = w;\n    }\n\n    public void showProgressArrow(boolean isShowArrow) {\n        this.isShowArrow = isShowArrow;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        materialWaveView = new MaterialWaveView(getContext());\n        materialWaveView.setColor(waveColor);\n        addView(materialWaveView);\n\n        circleProgressBar = new CircleProgressBar(getContext());\n        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(Util.dip2px(getContext(), progressSize), Util.dip2px(getContext(), progressSize));\n        layoutParams.gravity = Gravity.CENTER;\n        circleProgressBar.setLayoutParams(layoutParams);\n        circleProgressBar.setColorSchemeColors(progress_colors);\n        circleProgressBar.setProgressStokeWidth(progressStokeWidth);\n        circleProgressBar.setShowArrow(isShowArrow);\n        circleProgressBar.setShowProgressText(textType == 0);\n        circleProgressBar.setTextColor(progressTextColor);\n        circleProgressBar.setProgress(progressValue);\n        circleProgressBar.setMax(progressValueMax);\n        circleProgressBar.setCircleBackgroundEnabled(isShowProgressBg);\n        circleProgressBar.setProgressBackGroundColor(progressBg);\n        addView(circleProgressBar);\n    }\n\n    @Override\n    public void onComlete(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onComlete(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onComlete(materialRefreshLayout);\n            ViewCompat.setTranslationY(circleProgressBar, 0);\n            ViewCompat.setScaleX(circleProgressBar, 0);\n            ViewCompat.setScaleY(circleProgressBar, 0);\n        }\n\n\n    }\n\n    @Override\n    public void onBegin(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onBegin(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onBegin(materialRefreshLayout);\n            ViewCompat.setScaleX(circleProgressBar, 1);\n            ViewCompat.setScaleY(circleProgressBar, 1);\n        }\n    }\n\n    @Override\n    public void onPull(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n        if (materialWaveView != null) {\n            materialWaveView.onPull(materialRefreshLayout, fraction);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onPull(materialRefreshLayout, fraction);\n            float a = Util.limitValue(1, fraction);\n            ViewCompat.setScaleX(circleProgressBar, 1);\n            ViewCompat.setScaleY(circleProgressBar, 1);\n            ViewCompat.setAlpha(circleProgressBar, a);\n        }\n    }\n\n    @Override\n    public void onRelease(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n\n    }\n\n    @Override\n    public void onRefreshing(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onRefreshing(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onRefreshing(materialRefreshLayout);\n        }\n    }\n\n}\n\n\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialFooterView.java",
    "content": "package com.cjj;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.widget.FrameLayout;\n\npublic class MaterialFooterView extends FrameLayout implements MaterialHeadListener {\n    private MaterialWaveView materialWaveView;\n    private CircleProgressBar circleProgressBar;\n    private int waveColor;\n    private int progressTextColor;\n    private int[] progress_colors;\n    private int progressStokeWidth;\n    private boolean isShowArrow, isShowProgressBg;\n    private int progressValue, progressValueMax;\n    private int textType;\n    private int progressBg;\n    private int progressSize;\n    private MaterialHeadListener listener;\n\n    public MaterialFooterView(Context context) {\n        this(context, null);\n    }\n\n    public MaterialFooterView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MaterialFooterView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init(attrs, defStyle);\n    }\n\n\n    protected void init(AttributeSet attrs, int defStyle) {\n        if (isInEditMode()) return;\n        setClipToPadding(false);\n        setWillNotDraw(false);\n    }\n\n    public int getWaveColor() {\n        return waveColor;\n    }\n\n    public void setWaveColor(int waveColor) {\n        this.waveColor = waveColor;\n        if (null != materialWaveView) {\n            materialWaveView.setColor(this.waveColor);\n        }\n    }\n\n    public void setProgressSize(int progressSize) {\n        this.progressSize = progressSize;\n    }\n\n    public void setProgressBg(int progressBg) {\n        this.progressBg = progressBg;\n        if(circleProgressBar!=null)\n        circleProgressBar.setProgressBackGroundColor(progressBg);\n    }\n\n    public void setIsProgressBg(boolean isShowProgressBg) {\n        this.isShowProgressBg = isShowProgressBg;\n        if(circleProgressBar!=null)\n        circleProgressBar.setCircleBackgroundEnabled(isShowProgressBg);\n    }\n\n    public void setProgressTextColor(int textColor) {\n        this.progressTextColor = textColor;\n    }\n\n    public void setProgressColors(int[] colors) {\n        this.progress_colors = colors;\n        if(circleProgressBar!=null)\n        circleProgressBar.setColorSchemeColors(progress_colors);\n    }\n\n    public void setTextType(int textType) {\n        this.textType = textType;\n    }\n\n    public void setProgressValue(int value) {\n        this.progressValue = value;\n        this.post(new Runnable() {\n            @Override\n            public void run() {\n                if (circleProgressBar != null) {\n                    circleProgressBar.setProgress(progressValue);\n                }\n            }\n        });\n\n    }\n\n    public void setProgressValueMax(int value) {\n        this.progressValueMax = value;\n    }\n\n    public void setProgressStokeWidth(int w) {\n        this.progressStokeWidth = w;\n        if(circleProgressBar!=null)\n        circleProgressBar.setProgressStokeWidth(progressStokeWidth);\n    }\n\n    public void showProgressArrow(boolean isShowArrow) {\n        this.isShowArrow = isShowArrow;\n        if(circleProgressBar!=null)\n        circleProgressBar.setShowArrow(isShowArrow);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n\n        final float density = getContext().getResources().getDisplayMetrics().density;\n\n        materialWaveView = new MaterialWaveView(getContext());\n        materialWaveView.setColor(waveColor);\n        addView(materialWaveView);\n\n        circleProgressBar = new CircleProgressBar(getContext());\n        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams((int) density * progressSize, (int) density * progressSize);\n        layoutParams.gravity = Gravity.CENTER;\n        circleProgressBar.setLayoutParams(layoutParams);\n        circleProgressBar.setColorSchemeColors(progress_colors);\n        circleProgressBar.setProgressStokeWidth(progressStokeWidth);\n        circleProgressBar.setShowArrow(isShowArrow);\n        circleProgressBar.setShowProgressText(textType == 0);\n        circleProgressBar.setTextColor(progressTextColor);\n        circleProgressBar.setProgress(progressValue);\n        circleProgressBar.setMax(progressValueMax);\n        circleProgressBar.setCircleBackgroundEnabled(isShowProgressBg);\n        circleProgressBar.setProgressBackGroundColor(progressBg);\n        addView(circleProgressBar);\n    }\n\n    @Override\n    public void onComlete(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onComlete(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onComlete(materialRefreshLayout);\n            ViewCompat.setTranslationY(circleProgressBar, 0);\n            ViewCompat.setScaleX(circleProgressBar, 0);\n            ViewCompat.setScaleY(circleProgressBar, 0);\n        }\n\n\n    }\n\n    @Override\n    public void onBegin(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onBegin(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onBegin(materialRefreshLayout);\n            ViewCompat.setScaleX(circleProgressBar, 1);\n            ViewCompat.setScaleY(circleProgressBar, 1);\n        }\n    }\n\n    @Override\n    public void onPull(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n        if (materialWaveView != null) {\n            materialWaveView.onPull(materialRefreshLayout, fraction);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onPull(materialRefreshLayout, fraction);\n            float a = Util.limitValue(1, fraction);\n            ViewCompat.setScaleX(circleProgressBar, 1);\n            ViewCompat.setScaleY(circleProgressBar, 1);\n            ViewCompat.setAlpha(circleProgressBar, a);\n        }\n    }\n\n    @Override\n    public void onRelease(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n\n    }\n\n    @Override\n    public void onRefreshing(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onRefreshing(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onRefreshing(materialRefreshLayout);\n        }\n    }\n\n}\n\n\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialHeadListener.java",
    "content": "package com.cjj;\n/*\n* Copyright (C) 2015 Pedro Paulo de Amorim\n*\n* Licensed under the Apache License, Version 2.0 (the \"License\");\n* you may not use this file except in compliance with the License.\n* You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing, software\n* distributed under the License is distributed on an \"AS IS\" BASIS,\n* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n* See the License for the specific language governing permissions and\n* limitations under the License.\n*/\npublic interface MaterialHeadListener {\n    void onComlete(MaterialRefreshLayout materialRefreshLayout);\n    void onBegin(MaterialRefreshLayout materialRefreshLayout);\n    void onPull(MaterialRefreshLayout materialRefreshLayout, float fraction);\n    void onRelease(MaterialRefreshLayout materialRefreshLayout, float fraction);\n    void onRefreshing(MaterialRefreshLayout materialRefreshLayout);\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialHeadView.java",
    "content": "package com.cjj;\n\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\npublic class MaterialHeadView extends FrameLayout implements MaterialHeadListener{\n    private MaterialWaveView materialWaveView;\n    private CircleProgressBar circleProgressBar;\n    private int waveColor;\n    private int progressTextColor;\n    private int[] progress_colors;\n    private int progressStokeWidth;\n    private boolean isShowArrow,isShowProgressBg;\n    private int progressValue,progressValueMax;\n    private int textType;\n    private int progressBg;\n    private int progressSize;\n    private MaterialHeadListener listener;\n\n\n    public MaterialHeadView(Context context) {\n        this(context, null);\n    }\n\n    public MaterialHeadView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MaterialHeadView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init(attrs, defStyle);\n    }\n\n\n    protected void init(AttributeSet attrs, int defStyle) {\n        if (isInEditMode()) return;\n        setClipToPadding(false);\n        setWillNotDraw(false);\n    }\n\n    public int getWaveColor() {\n        return waveColor;\n    }\n\n    public void setWaveColor(int waveColor) {\n        this.waveColor = waveColor;\n        if(null!= materialWaveView)\n        {\n            materialWaveView.setColor( this.waveColor );\n        }\n    }\n\n    public void setProgressSize(int progressSize)\n    {\n        this.progressSize = progressSize;\n    }\n\n    public void setProgressBg(int progressBg)\n    {\n        this.progressBg = progressBg;\n    }\n\n    public void setIsProgressBg(boolean isShowProgressBg)\n    {\n        this.isShowProgressBg = isShowProgressBg;\n    }\n\n    public void setProgressTextColor(int textColor)\n    {\n        this.progressTextColor = textColor;\n    }\n\n    public void setProgressColors(int[] colors)\n    {\n        this.progress_colors = colors;\n    }\n\n    public void setTextType(int textType)\n    {\n        this.textType = textType;\n    }\n\n    public void setProgressValue(int value)\n    {\n        this.progressValue = value;\n        this.post(new Runnable() {\n            @Override\n            public void run() {\n                if (circleProgressBar != null) {\n                    circleProgressBar.setProgress(progressValue);\n                }\n            }\n        });\n\n    }\n\n    public void setProgressValueMax(int value)\n    {\n        this.progressValueMax = value;\n    }\n\n    public void setProgressStokeWidth(int w)\n    {\n        this.progressStokeWidth = w;\n    }\n\n    public void showProgressArrow(boolean isShowArrow)\n    {\n        this.isShowArrow = isShowArrow;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        materialWaveView = new MaterialWaveView(getContext());\n        materialWaveView.setColor(waveColor);\n        addView(materialWaveView);\n\n        circleProgressBar = new CircleProgressBar(getContext());\n        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(Util.dip2px(getContext(),progressSize),Util.dip2px(getContext(),progressSize));\n        layoutParams.gravity = Gravity.CENTER;\n        circleProgressBar.setLayoutParams(layoutParams);\n        circleProgressBar.setColorSchemeColors(progress_colors);\n        circleProgressBar.setProgressStokeWidth(progressStokeWidth);\n        circleProgressBar.setShowArrow(isShowArrow);\n        circleProgressBar.setShowProgressText(textType == 0);\n        circleProgressBar.setTextColor(progressTextColor);\n        circleProgressBar.setProgress(progressValue);\n        circleProgressBar.setMax(progressValueMax);\n        circleProgressBar.setCircleBackgroundEnabled(isShowProgressBg);\n        circleProgressBar.setProgressBackGroundColor(progressBg);\n        addView(circleProgressBar);\n    }\n\n    @Override\n    public void onComlete(MaterialRefreshLayout materialRefreshLayout) {\n        if(materialWaveView != null)\n        {\n            materialWaveView.onComlete(materialRefreshLayout);\n        }\n        if(circleProgressBar != null)\n        {\n            circleProgressBar.onComlete(materialRefreshLayout);\n            ViewCompat.setTranslationY(circleProgressBar,0);\n            ViewCompat.setScaleX(circleProgressBar, 0);\n            ViewCompat.setScaleY(circleProgressBar,0);\n        }\n\n    }\n\n    @Override\n    public void onBegin(MaterialRefreshLayout materialRefreshLayout) {\n        if(materialWaveView != null)\n        {\n            materialWaveView.onBegin(materialRefreshLayout);\n        }\n        if(circleProgressBar != null)\n        {\n            circleProgressBar.onBegin(materialRefreshLayout);\n        }\n    }\n\n    @Override\n    public void onPull(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n        if(materialWaveView != null)\n        {\n            materialWaveView.onPull(materialRefreshLayout, fraction);\n        }\n        if(circleProgressBar != null)\n        {\n            circleProgressBar.onPull(materialRefreshLayout, fraction);\n            float a = Util.limitValue(1,fraction);\n            ViewCompat.setScaleX(circleProgressBar, 1);\n            ViewCompat.setScaleY(circleProgressBar, 1);\n            ViewCompat.setAlpha(circleProgressBar, a);\n        }\n    }\n\n    @Override\n    public void onRelease(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n\n    }\n\n    @Override\n    public void onRefreshing(MaterialRefreshLayout materialRefreshLayout) {\n        if(materialWaveView != null)\n        {\n            materialWaveView.onRefreshing(materialRefreshLayout);\n        }\n        if(circleProgressBar != null)\n        {\n            circleProgressBar.onRefreshing(materialRefreshLayout);\n        }\n    }\n\n\n\n//    public void scaleView(View v,float a,float b) {\n//        ObjectAnimator ax = ObjectAnimator.ofFloat(v,\"scaleX\",a,b);\n//        ObjectAnimator ay = ObjectAnimator.ofFloat(v,\"scaleY\",a,b);\n//        AnimatorSet animSet = new AnimatorSet();\n//        animSet.play(ax).with(ay);\n//        animSet.setDuration(200);\n//        animSet.start();\n//    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialHeaderView.java",
    "content": "package com.cjj;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.widget.FrameLayout;\n\npublic class MaterialHeaderView extends FrameLayout implements MaterialHeadListener {\n\n    private final static String Tag = MaterialHeaderView.class.getSimpleName();\n    private MaterialWaveView materialWaveView;\n    private CircleProgressBar circleProgressBar;\n    private int waveColor;\n    private int progressTextColor;\n    private int[] progress_colors;\n    private int progressStokeWidth;\n    private boolean isShowArrow, isShowProgressBg;\n    private int progressValue, progressValueMax;\n    private int textType;\n    private int progressBg;\n    private int progressSize;\n    private static float density;\n\n    public MaterialHeaderView(Context context) {\n        this(context, null);\n    }\n\n    public MaterialHeaderView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MaterialHeaderView(Context context, AttributeSet attrs, int defStyle) {\n        super(context, attrs, defStyle);\n        init(attrs, defStyle);\n    }\n\n\n    protected void init(AttributeSet attrs, int defStyle) {\n        if (isInEditMode()) return;\n        setClipToPadding(false);\n        setWillNotDraw(false);\n    }\n\n    public int getWaveColor() {\n        return waveColor;\n    }\n\n    public void setWaveColor(int waveColor) {\n        this.waveColor = waveColor;\n        if (null != materialWaveView) {\n            materialWaveView.setColor(this.waveColor);\n        }\n    }\n\n    public void setProgressSize(int progressSize) {\n        this.progressSize = progressSize;\n        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams((int) density * progressSize, (int) density * progressSize);\n        layoutParams.gravity = Gravity.CENTER;\n        if(circleProgressBar!=null)\n        circleProgressBar.setLayoutParams(layoutParams);\n    }\n\n    public void setProgressBg(int progressBg) {\n        this.progressBg = progressBg;\n        if(circleProgressBar!=null)\n        circleProgressBar.setProgressBackGroundColor(progressBg);\n    }\n\n    public void setIsProgressBg(boolean isShowProgressBg) {\n        this.isShowProgressBg = isShowProgressBg;\n        if(circleProgressBar!=null)\n        circleProgressBar.setCircleBackgroundEnabled(isShowProgressBg);\n    }\n\n    public void setProgressTextColor(int textColor) {\n        this.progressTextColor = textColor;\n    }\n\n    public void setProgressColors(int[] colors) {\n        this.progress_colors = colors;\n        if(circleProgressBar!=null)\n        circleProgressBar.setColorSchemeColors(progress_colors);\n    }\n\n    public void setTextType(int textType) {\n        this.textType = textType;\n    }\n\n    public void setProgressValue(int value) {\n        this.progressValue = value;\n        this.post(new Runnable() {\n            @Override\n            public void run() {\n                if (circleProgressBar != null) {\n                    circleProgressBar.setProgress(progressValue);\n                }\n            }\n        });\n\n    }\n\n    public void setProgressValueMax(int value) {\n        this.progressValueMax = value;\n    }\n\n    public void setProgressStokeWidth(int w) {\n        this.progressStokeWidth = w;\n        if(circleProgressBar!=null)\n            circleProgressBar.setProgressStokeWidth(progressStokeWidth);\n    }\n\n    public void showProgressArrow(boolean isShowArrow) {\n        this.isShowArrow = isShowArrow;\n        if(circleProgressBar!=null)\n        circleProgressBar.setShowArrow(isShowArrow);\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        density = getContext().getResources().getDisplayMetrics().density;\n        materialWaveView = new MaterialWaveView(getContext());\n        materialWaveView.setColor(waveColor);\n        addView(materialWaveView);\n\n        circleProgressBar = new CircleProgressBar(getContext());\n        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams((int) density * progressSize, (int) density * progressSize);\n        layoutParams.gravity = Gravity.CENTER;\n        circleProgressBar.setLayoutParams(layoutParams);\n        circleProgressBar.setColorSchemeColors(progress_colors);\n        circleProgressBar.setProgressStokeWidth(progressStokeWidth);\n        circleProgressBar.setShowArrow(isShowArrow);\n        circleProgressBar.setShowProgressText(textType == 0);\n        circleProgressBar.setTextColor(progressTextColor);\n        circleProgressBar.setProgress(progressValue);\n        circleProgressBar.setMax(progressValueMax);\n        circleProgressBar.setCircleBackgroundEnabled(isShowProgressBg);\n        circleProgressBar.setProgressBackGroundColor(progressBg);\n        addView(circleProgressBar);\n    }\n\n    @Override\n    public void onComlete(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onComlete(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onComlete(materialRefreshLayout);\n            ViewCompat.setTranslationY(circleProgressBar, 0);\n            ViewCompat.setScaleX(circleProgressBar, 0);\n            ViewCompat.setScaleY(circleProgressBar, 0);\n        }\n\n    }\n\n    @Override\n    public void onBegin(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onBegin(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            ViewCompat.setScaleX(circleProgressBar, 0.001f);\n            ViewCompat.setScaleY(circleProgressBar, 0.001f);\n            circleProgressBar.onBegin(materialRefreshLayout);\n        }\n    }\n\n    @Override\n    public void onPull(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n        if (materialWaveView != null) {\n            materialWaveView.onPull(materialRefreshLayout, fraction);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onPull(materialRefreshLayout, fraction);\n            float a = Util.limitValue(1, fraction);\n            ViewCompat.setScaleX(circleProgressBar, a);\n            ViewCompat.setScaleY(circleProgressBar, a);\n            ViewCompat.setAlpha(circleProgressBar, a);\n        }\n    }\n\n    @Override\n    public void onRelease(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n\n    }\n\n    @Override\n    public void onRefreshing(MaterialRefreshLayout materialRefreshLayout) {\n        if (materialWaveView != null) {\n            materialWaveView.onRefreshing(materialRefreshLayout);\n        }\n        if (circleProgressBar != null) {\n            circleProgressBar.onRefreshing(materialRefreshLayout);\n        }\n    }\n\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialProgressDrawable.java",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.cjj;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Style;\nimport android.graphics.Path;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.drawable.Animatable;\nimport android.graphics.drawable.Drawable;\n\nimport android.util.DisplayMetrics;\nimport android.view.View;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.Animation;\nimport android.view.animation.Interpolator;\nimport android.view.animation.LinearInterpolator;\nimport android.view.animation.Transformation;\n\nimport java.util.ArrayList;\n\npublic class MaterialProgressDrawable extends Drawable implements Animatable {\n    // Maps to ProgressBar.Large style\n    public static final int LARGE = 0;\n    // Maps to ProgressBar default style\n    public static final int DEFAULT = 1;\n    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();\n    private static final Interpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator();\n    private static final Interpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator();\n    private static final Interpolator EASE_INTERPOLATOR = new AccelerateDecelerateInterpolator();\n    // Maps to ProgressBar default style\n    private static final int CIRCLE_DIAMETER = 40;\n    private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width\n    private static final float STROKE_WIDTH = 2.5f;\n    // Maps to ProgressBar.Large style\n    private static final int CIRCLE_DIAMETER_LARGE = 56;\n    private static final float CENTER_RADIUS_LARGE = 12.5f;\n    static final float STROKE_WIDTH_LARGE = 3f;\n    /**\n     * The duration of a single progress spin in milliseconds.\n     */\n    private static final int ANIMATION_DURATION = 1000 * 80 / 60;\n    /**\n     * The number of points in the progress \"star\".\n     */\n    private static final float NUM_POINTS = 5f;\n    /**\n     * Layout info for the arrowhead in dp\n     */\n    private static final int ARROW_WIDTH = 10;\n    private static final int ARROW_HEIGHT = 5;\n    private static final float ARROW_OFFSET_ANGLE = 0;\n    /**\n     * Layout info for the arrowhead for the large spinner in dp\n     */\n    static final int ARROW_WIDTH_LARGE = 12;\n    static final int ARROW_HEIGHT_LARGE = 6;\n    private static final float MAX_PROGRESS_ARC = .8f;\n    private final int[] COLORS = new int[]{\n            Color.BLACK\n    };\n    /**\n     * The list of animators operating on this drawable.\n     */\n    private final ArrayList<Animation> mAnimators = new ArrayList<Animation>();\n    /**\n     * The indicator ring, used to manage animation state.\n     */\n    private final Ring mRing;\n    private final Callback mCallback = new Callback() {\n        @Override\n        public void invalidateDrawable(Drawable d) {\n            invalidateSelf();\n        }\n\n        @Override\n        public void scheduleDrawable(Drawable d, Runnable what, long when) {\n            scheduleSelf(what, when);\n        }\n\n        @Override\n        public void unscheduleDrawable(Drawable d, Runnable what) {\n            unscheduleSelf(what);\n        }\n    };\n    boolean mFinishing;\n    /**\n     * Canvas rotation in degrees.\n     */\n    private float mRotation;\n    private Resources mResources;\n    private View mAnimExcutor;\n    private Animation mAnimation;\n    private float mRotationCount;\n    private double mWidth;\n    private double mHeight;\n    private boolean mShowArrowOnFirstStart = false;\n\n    public MaterialProgressDrawable(Context context, View animExcutor) {\n        mAnimExcutor = animExcutor;\n        mResources = context.getResources();\n\n        mRing = new Ring(mCallback);\n        mRing.setColors(COLORS);\n\n        updateSizes(DEFAULT);\n        setupAnimators();\n    }\n\n    public void setSizeParameters(double progressCircleWidth, double progressCircleHeight,\n                                   double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {\n        final Ring ring = mRing;\n        mWidth = progressCircleWidth;\n        mHeight = progressCircleHeight ;\n        ring.setStrokeWidth((float) strokeWidth );\n        ring.setCenterRadius(centerRadius);\n        ring.setColorIndex(0);\n        ring.setArrowDimensions(arrowWidth , arrowHeight );\n        ring.setInsets((int) mWidth, (int) mHeight);\n    }\n\n    /**\n     * Set the overall size for the progress spinner. This updates the radius\n     * and stroke width of the ring.\n     *\n\n     */\n    public void updateSizes(@ProgressDrawableSize int size) {\n        final DisplayMetrics metrics = mResources.getDisplayMetrics();\n        final float screenDensity = metrics.density;\n\n        if (size == LARGE) {\n            setSizeParameters(CIRCLE_DIAMETER_LARGE*screenDensity, CIRCLE_DIAMETER_LARGE*screenDensity, CENTER_RADIUS_LARGE*screenDensity,\n                    STROKE_WIDTH_LARGE*screenDensity, ARROW_WIDTH_LARGE*screenDensity, ARROW_HEIGHT_LARGE*screenDensity);\n        } else {\n            setSizeParameters(CIRCLE_DIAMETER*screenDensity, CIRCLE_DIAMETER*screenDensity, CENTER_RADIUS*screenDensity, STROKE_WIDTH*screenDensity,\n                    ARROW_WIDTH*screenDensity, ARROW_HEIGHT*screenDensity);\n        }\n    }\n\n    /**\n     * @param show Set to true to display the arrowhead on the progress spinner.\n     */\n    public void showArrow(boolean show) {\n        mRing.setShowArrow(show);\n    }\n\n    /**\n     * @param scale Set the scale of the arrowhead for the spinner.\n     */\n    public void setArrowScale(float scale) {\n        mRing.setArrowScale(scale);\n    }\n\n    /**\n     * Set the start and end trim for the progress spinner arc.\n     *\n     * @param startAngle start angle\n     * @param endAngle   end angle\n     */\n    public void setStartEndTrim(float startAngle, float endAngle) {\n        mRing.setStartTrim(startAngle);\n        mRing.setEndTrim(endAngle);\n    }\n\n    /**\n     * Set the amount of rotation to apply to the progress spinner.\n     *\n     * @param rotation Rotation is from [0..1]\n     */\n    public void setProgressRotation(float rotation) {\n        mRing.setRotation(rotation);\n    }\n\n    /**\n     * Update the background color of the circle image view.\n     */\n    public void setBackgroundColor(int color) {\n        mRing.setBackgroundColor(color);\n    }\n\n    /**\n     * Set the colors used in the progress animation from color resources.\n     * The first color will also be the color of the bar that grows in response\n     * to a user swipe gesture.\n     *\n     * @param colors\n     */\n    public void setColorSchemeColors(int... colors) {\n        mRing.setColors(colors);\n        mRing.setColorIndex(0);\n    }\n\n    @Override\n    public int getIntrinsicHeight() {\n        return (int) mHeight;\n    }\n\n    @Override\n    public int getIntrinsicWidth() {\n        return (int) mWidth;\n    }\n\n    @Override\n    public void draw(Canvas c) {\n        final Rect bounds = getBounds();\n        final int saveCount = c.save();\n        c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());\n        mRing.draw(c, bounds);\n        c.restoreToCount(saveCount);\n    }\n\n    public int getAlpha() {\n        return mRing.getAlpha();\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n        mRing.setAlpha(alpha);\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter colorFilter) {\n        mRing.setColorFilter(colorFilter);\n    }\n\n    @SuppressWarnings(\"unused\")\n    private float getRotation() {\n        return mRotation;\n    }\n\n    @SuppressWarnings(\"unused\")\n    void setRotation(float rotation) {\n        mRotation = rotation;\n        invalidateSelf();\n    }\n\n    @Override\n    public int getOpacity() {\n        return PixelFormat.TRANSLUCENT;\n    }\n\n    @Override\n    public boolean isRunning() {\n        return  !this.mAnimation.hasEnded();\n    }\n\n    @Override\n    public void start() {\n        mAnimation.reset();\n        mRing.storeOriginals();\n        mRing.setShowArrow(mShowArrowOnFirstStart);\n\n        // Already showing some part of the ring\n        if (mRing.getEndTrim() != mRing.getStartTrim()) {\n            mFinishing = true;\n            mAnimation.setDuration(ANIMATION_DURATION / 2);\n            mAnimExcutor.startAnimation(mAnimation);\n        } else {\n            mRing.setColorIndex(0);\n            mRing.resetOriginals();\n            mAnimation.setDuration(ANIMATION_DURATION);\n            mAnimExcutor.startAnimation(mAnimation);\n        }\n    }\n\n    @Override\n    public void stop() {\n        mAnimExcutor.clearAnimation();\n        setRotation(0);\n        mRing.setShowArrow(false);\n        mRing.setColorIndex(0);\n        mRing.resetOriginals();\n    }\n\n    private void applyFinishTranslation(float interpolatedTime, Ring ring) {\n        // shrink back down and complete a full rotation before\n        // starting other circles\n        // Rotation goes between [0..1].\n        float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)\n                + 1f);\n        final float startTrim = ring.getStartingStartTrim()\n                + (ring.getStartingEndTrim() - ring.getStartingStartTrim()) * interpolatedTime;\n        ring.setStartTrim(startTrim);\n        final float rotation = ring.getStartingRotation()\n                + ((targetRotation - ring.getStartingRotation()) * interpolatedTime);\n        ring.setRotation(rotation);\n    }\n\n    private void setupAnimators() {\n        final Ring ring = mRing;\n        final Animation animation = new Animation() {\n            @Override\n            public void applyTransformation(float interpolatedTime, Transformation t) {\n                if (mFinishing) {\n                    applyFinishTranslation(interpolatedTime, ring);\n                } else {\n                    // The minProgressArc is calculated from 0 to create an\n                    // angle that\n                    // matches the stroke width.\n                    final float minProgressArc = (float) Math.toRadians(\n                            ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius()));\n                    final float startingEndTrim = ring.getStartingEndTrim();\n                    final float startingTrim = ring.getStartingStartTrim();\n                    final float startingRotation = ring.getStartingRotation();\n\n                    // Offset the minProgressArc to where the endTrim is\n                    // located.\n                    final float minArc = MAX_PROGRESS_ARC - minProgressArc;\n                    float endTrim = startingEndTrim + (minArc\n                            * START_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime));\n                    float startTrim = startingTrim + (MAX_PROGRESS_ARC\n                            * END_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime));\n\n                    final float sweepTrim =  endTrim-startTrim;\n                    //Avoid the ring to be a full circle\n                    if(Math.abs(sweepTrim)>=1){\n                        endTrim = startTrim+0.5f;\n                    }\n\n                    ring.setEndTrim(endTrim);\n\n                    ring.setStartTrim(startTrim);\n\n                    final float rotation = startingRotation + (0.25f * interpolatedTime);\n                    ring.setRotation(rotation);\n\n                    float groupRotation = ((720.0f / NUM_POINTS) * interpolatedTime)\n                            + (720.0f * (mRotationCount / NUM_POINTS));\n                    setRotation(groupRotation);\n                }\n            }\n        };\n        animation.setRepeatCount(Animation.INFINITE);\n        animation.setRepeatMode(Animation.RESTART);\n        animation.setInterpolator(LINEAR_INTERPOLATOR);\n        animation.setAnimationListener(new Animation.AnimationListener() {\n\n            @Override\n            public void onAnimationStart(Animation animation) {\n                mRotationCount = 0;\n            }\n\n            @Override\n            public void onAnimationEnd(Animation animation) {\n                // do nothing\n            }\n\n            @Override\n            public void onAnimationRepeat(Animation animation) {\n                ring.storeOriginals();\n                ring.goToNextColor();\n                ring.setStartTrim(ring.getEndTrim());\n                if (mFinishing) {\n                    // finished closing the last ring from the swipe gesture; go\n                    // into progress mode\n                    mFinishing = false;\n                    animation.setDuration(ANIMATION_DURATION);\n                    ring.setShowArrow(false);\n                } else {\n                    mRotationCount = (mRotationCount + 1) % (NUM_POINTS);\n                }\n            }\n        });\n        mAnimation = animation;\n    }\n\n    public void showArrowOnFirstStart(boolean showArrowOnFirstStart) {\n        this.mShowArrowOnFirstStart = showArrowOnFirstStart;\n    }\n\n\n    public @interface ProgressDrawableSize {\n    }\n\n    private static class Ring {\n        private final RectF mTempBounds = new RectF();\n        private final Paint mPaint = new Paint();\n        private final Paint mArrowPaint = new Paint();\n\n        private final Callback mCallback;\n        private final Paint mCirclePaint = new Paint();\n        private float mStartTrim = 0.0f;\n        private float mEndTrim = 0.0f;\n        private float mRotation = 0.0f;\n        private float mStrokeWidth = 5.0f;\n        private float mStrokeInset = 2.5f;\n        private int[] mColors;\n        // mColorIndex represents the offset into the available mColors that the\n        // progress circle should currently display. As the progress circle is\n        // animating, the mColorIndex moves by one to the next available color.\n        private int mColorIndex;\n        private float mStartingStartTrim;\n        private float mStartingEndTrim;\n        private float mStartingRotation;\n        private boolean mShowArrow;\n        private Path mArrow;\n        private float mArrowScale;\n        private double mRingCenterRadius;\n        private int mArrowWidth;\n        private int mArrowHeight;\n        private int mAlpha;\n        private int mBackgroundColor;\n\n        public Ring(Callback callback) {\n            mCallback = callback;\n\n            mPaint.setStrokeCap(Paint.Cap.SQUARE);\n            mPaint.setAntiAlias(true);\n            mPaint.setStyle(Style.STROKE);\n\n            mArrowPaint.setStyle(Style.FILL);\n            mArrowPaint.setAntiAlias(true);\n        }\n\n        public void setBackgroundColor(int color) {\n            mBackgroundColor = color;\n        }\n\n        /**\n         * Set the dimensions of the arrowhead.\n         *\n         * @param width  Width of the hypotenuse of the arrow head\n         * @param height Height of the arrow point\n         */\n        public void setArrowDimensions(float width, float height) {\n            mArrowWidth = (int) width;\n            mArrowHeight = (int) height;\n        }\n\n        /**\n         * Draw the progress spinner\n         */\n        public void draw(Canvas c, Rect bounds) {\n            final RectF arcBounds = mTempBounds;\n            arcBounds.set(bounds);\n            arcBounds.inset(mStrokeInset, mStrokeInset);\n\n            final float startAngle = (mStartTrim + mRotation) * 360;\n            final float endAngle = (mEndTrim + mRotation) * 360;\n            float sweepAngle = endAngle - startAngle;\n            mPaint.setColor(mColors[mColorIndex]);\n            c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);\n\n            drawTriangle(c, startAngle, sweepAngle, bounds);\n\n            if (mAlpha < 255) {\n                mCirclePaint.setColor(mBackgroundColor);\n                mCirclePaint.setAlpha(255 - mAlpha);\n                c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,\n                        mCirclePaint);\n            }\n        }\n\n        private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {\n            if (mShowArrow) {\n                if (mArrow == null) {\n                    mArrow = new Path();\n                    mArrow.setFillType(Path.FillType.EVEN_ODD);\n                } else {\n                    mArrow.reset();\n                }\n\n                // Adjust the position of the triangle so that it is inset as\n                // much as the arc, but also centered on the arc.\n                float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());\n                float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());\n\n                // Update the path each time. This works around an issue in SKIA\n                // where concatenating a rotation matrix to a scale matrix\n                // ignored a starting negative rotation. This appears to have\n                // been fixed as of API 21.\n                mArrow.moveTo(0, 0);\n                mArrow.lineTo((mArrowWidth) * mArrowScale, 0);\n                mArrow.lineTo(((mArrowWidth) * mArrowScale / 2), (mArrowHeight\n                        * mArrowScale));\n                mArrow.offset(x-((mArrowWidth) * mArrowScale / 2), y);\n                mArrow.close();\n                // draw a triangle\n                mArrowPaint.setColor(mColors[mColorIndex]);\n                //when sweepAngle < 0 adjust the position of the arrow\n                c.rotate(startAngle + (sweepAngle<0?0:sweepAngle) - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),\n                        bounds.exactCenterY());\n                c.drawPath(mArrow, mArrowPaint);\n            }\n        }\n\n        /**\n         * Set the colors the progress spinner alternates between.\n         *\n         * @param colors Array of integers describing the colors. Must be non-<code>null</code>.\n         */\n        public void setColors(int[] colors) {\n            mColors = colors;\n            // if colors are reset, make sure to reset the color index as well\n            setColorIndex(0);\n        }\n\n        /**\n         * @param index Index into the color array of the color to display in\n         *              the progress spinner.\n         */\n        public void setColorIndex(int index) {\n            mColorIndex = index;\n        }\n\n        /**\n         * Proceed to the next available ring color. This will automatically\n         * wrap back to the beginning of colors.\n         */\n        public void goToNextColor() {\n            mColorIndex = (mColorIndex + 1) % (mColors.length);\n        }\n\n        public void setColorFilter(ColorFilter filter) {\n            mPaint.setColorFilter(filter);\n            invalidateSelf();\n        }\n\n        /**\n         * @return Current alpha of the progress spinner and arrowhead.\n         */\n        public int getAlpha() {\n            return mAlpha;\n        }\n\n        /**\n         * @param alpha Set the alpha of the progress spinner and associated arrowhead.\n         */\n        public void setAlpha(int alpha) {\n            mAlpha = alpha;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public float getStrokeWidth() {\n            return mStrokeWidth;\n        }\n\n        /**\n         * @param strokeWidth Set the stroke width of the progress spinner in pixels.\n         */\n        public void setStrokeWidth(float strokeWidth) {\n            mStrokeWidth = strokeWidth;\n            mPaint.setStrokeWidth(strokeWidth);\n            invalidateSelf();\n        }\n\n        @SuppressWarnings(\"unused\")\n        public float getStartTrim() {\n            return mStartTrim;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public void setStartTrim(float startTrim) {\n            mStartTrim = startTrim;\n            invalidateSelf();\n        }\n\n        public float getStartingStartTrim() {\n            return mStartingStartTrim;\n        }\n\n        public float getStartingEndTrim() {\n            return mStartingEndTrim;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public float getEndTrim() {\n            return mEndTrim;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public void setEndTrim(float endTrim) {\n            mEndTrim = endTrim;\n            invalidateSelf();\n        }\n\n        @SuppressWarnings(\"unused\")\n        public float getRotation() {\n            return mRotation;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public void setRotation(float rotation) {\n            mRotation = rotation;\n            invalidateSelf();\n        }\n\n        public void setInsets(int width, int height) {\n            final float minEdge = (float) Math.min(width, height);\n            float insets;\n            if (mRingCenterRadius <= 0 || minEdge < 0) {\n                insets = (float) Math.ceil(mStrokeWidth / 2.0f);\n            } else {\n                insets = (float) (minEdge / 2.0f - mRingCenterRadius);\n            }\n            mStrokeInset = insets;\n        }\n\n        @SuppressWarnings(\"unused\")\n        public float getInsets() {\n            return mStrokeInset;\n        }\n\n        public double getCenterRadius() {\n            return mRingCenterRadius;\n        }\n\n        /**\n         * @param centerRadius Inner radius in px of the circle the progress\n         *                     spinner arc traces.\n         */\n        public void setCenterRadius(double centerRadius) {\n            mRingCenterRadius = centerRadius;\n        }\n\n        /**\n         * @param show Set to true to show the arrow head on the progress spinner.\n         */\n        public void setShowArrow(boolean show) {\n            if (mShowArrow != show) {\n                mShowArrow = show;\n                invalidateSelf();\n            }\n        }\n\n        /**\n         * @param scale Set the scale of the arrowhead for the spinner.\n         */\n        public void setArrowScale(float scale) {\n            if (scale != mArrowScale) {\n                mArrowScale = scale;\n                invalidateSelf();\n            }\n        }\n\n        /**\n         * @return The amount the progress spinner is currently rotated, between [0..1].\n         */\n        public float getStartingRotation() {\n            return mStartingRotation;\n        }\n\n        /**\n         * If the start / end trim are offset to begin with, store them so that\n         * animation starts from that offset.\n         */\n        public void storeOriginals() {\n            mStartingStartTrim = mStartTrim;\n            mStartingEndTrim = mEndTrim;\n            mStartingRotation = mRotation;\n        }\n\n        /**\n         * Reset the progress spinner to default rotation, start and end angles.\n         */\n        public void resetOriginals() {\n            mStartingStartTrim = 0;\n            mStartingEndTrim = 0;\n            mStartingRotation = 0;\n            setStartTrim(0);\n            setEndTrim(0);\n            setRotation(0);\n        }\n\n        private void invalidateSelf() {\n            mCallback.invalidateDrawable(null);\n        }\n    }\n\n    /**\n     * Squishes the interpolation curve into the second half of the animation.\n     */\n    private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator {\n        @Override\n        public float getInterpolation(float input) {\n            return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f));\n        }\n    }\n\n    /**\n     * Squishes the interpolation curve into the first half of the animation.\n     */\n    private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator {\n        @Override\n        public float getInterpolation(float input) {\n            return super.getInterpolation(Math.min(1, input * 2.0f));\n        }\n    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialRefreshLayout.java",
    "content": "package com.cjj;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.support.v4.view.ViewCompat;\nimport android.support.v4.view.ViewPropertyAnimatorCompat;\nimport android.support.v4.view.ViewPropertyAnimatorUpdateListener;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.DecelerateInterpolator;\nimport android.widget.AbsListView;\nimport android.widget.FrameLayout;\n\npublic class MaterialRefreshLayout extends FrameLayout {\n\n    public static final String Tag = MaterialRefreshLayout.class.getSimpleName();\n    private final static int DEFAULT_WAVE_HEIGHT = 140;\n    private final static int HIGHER_WAVE_HEIGHT = 180;\n    private final static int DEFAULT_HEAD_HEIGHT = 70;\n    private final static int hIGHER_HEAD_HEIGHT = 100;\n    private final static int DEFAULT_PROGRESS_SIZE = 50;\n    private final static int BIG_PROGRESS_SIZE = 60;\n    private final static int PROGRESS_STOKE_WIDTH = 3;\n\n    private MaterialHeaderView mMaterialHeaderView;\n    private MaterialFooterView mMaterialFooterView;\n    private SunLayout mSunLayout;\n    private boolean isOverlay;\n    private int waveType;\n    private int waveColor;\n    protected float mWaveHeight;\n    protected float mHeadHeight;\n    private View mChildView;\n    protected boolean isRefreshing;\n    private float mTouchY;\n    private float mCurrentY;\n    private DecelerateInterpolator decelerateInterpolator;\n    private float headHeight;\n    private float waveHeight;\n    private int[] colorSchemeColors;\n    private int colorsId;\n    private int progressTextColor;\n    private int progressValue, progressMax;\n    private boolean showArrow = true;\n    private int textType;\n    private MaterialRefreshListener refreshListener;\n    private boolean showProgressBg;\n    private int progressBg;\n    private boolean isShowWave;\n    private int progressSizeType;\n    private int progressSize = 0;\n    private boolean isLoadMoreing;\n    private boolean isLoadMore;\n    private boolean isSunStyle = false;\n\n    public MaterialRefreshLayout(Context context) {\n        this(context, null, 0);\n    }\n\n    public MaterialRefreshLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MaterialRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context, attrs, defStyleAttr);\n    }\n\n    private void init(Context context, AttributeSet attrs, int defstyleAttr) {\n        if (isInEditMode()) {\n            return;\n        }\n\n        if (getChildCount() > 1) {\n            throw new RuntimeException(\"can only have one child widget\");\n        }\n\n        decelerateInterpolator = new DecelerateInterpolator(10);\n\n\n        TypedArray t = context.obtainStyledAttributes(attrs, R.styleable.MaterialRefreshLayout, defstyleAttr, 0);\n        isOverlay = t.getBoolean(R.styleable.MaterialRefreshLayout_overlay, false);\n        /**attrs for materialWaveView*/\n        waveType = t.getInt(R.styleable.MaterialRefreshLayout_wave_height_type, 0);\n        if (waveType == 0) {\n            headHeight = DEFAULT_HEAD_HEIGHT;\n            waveHeight = DEFAULT_WAVE_HEIGHT;\n            MaterialWaveView.DefaulHeadHeight = DEFAULT_HEAD_HEIGHT;\n            MaterialWaveView.DefaulWaveHeight = DEFAULT_WAVE_HEIGHT;\n        } else {\n            headHeight = hIGHER_HEAD_HEIGHT;\n            waveHeight = HIGHER_WAVE_HEIGHT;\n            MaterialWaveView.DefaulHeadHeight = hIGHER_HEAD_HEIGHT;\n            MaterialWaveView.DefaulWaveHeight = HIGHER_WAVE_HEIGHT;\n        }\n        waveColor = t.getColor(R.styleable.MaterialRefreshLayout_wave_color, Color.WHITE);\n        isShowWave = t.getBoolean(R.styleable.MaterialRefreshLayout_wave_show, true);\n\n        /**attrs for circleprogressbar*/\n        colorsId = t.getResourceId(R.styleable.MaterialRefreshLayout_progress_colors, R.array.material_colors);\n        colorSchemeColors = context.getResources().getIntArray(colorsId);\n        showArrow = t.getBoolean(R.styleable.MaterialRefreshLayout_progress_show_arrow, true);\n        textType = t.getInt(R.styleable.MaterialRefreshLayout_progress_text_visibility, 1);\n        progressTextColor = t.getColor(R.styleable.MaterialRefreshLayout_progress_text_color, Color.BLACK);\n        progressValue = t.getInteger(R.styleable.MaterialRefreshLayout_progress_value, 0);\n        progressMax = t.getInteger(R.styleable.MaterialRefreshLayout_progress_max_value, 100);\n        showProgressBg = t.getBoolean(R.styleable.MaterialRefreshLayout_progress_show_circle_backgroud, true);\n        progressBg = t.getColor(R.styleable.MaterialRefreshLayout_progress_backgroud_color, CircleProgressBar.DEFAULT_CIRCLE_BG_LIGHT);\n        progressSizeType = t.getInt(R.styleable.MaterialRefreshLayout_progress_size_type, 0);\n        if (progressSizeType == 0) {\n            progressSize = DEFAULT_PROGRESS_SIZE;\n        } else {\n            progressSize = BIG_PROGRESS_SIZE;\n        }\n        isLoadMore = t.getBoolean(R.styleable.MaterialRefreshLayout_isLoadMore, false);\n        t.recycle();\n    }\n\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        Log.i(Tag, \"onAttachedToWindow\");\n\n        Context context = getContext();\n\n        mChildView = getChildAt(0);\n\n        if (mChildView == null) {\n            return;\n        }\n\n        setWaveHeight(Util.dip2px(context, waveHeight));\n        setHeaderHeight(Util.dip2px(context, headHeight));\n\n        if (isSunStyle) {\n            mSunLayout = new SunLayout(context);\n            LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dip2px(context, hIGHER_HEAD_HEIGHT));\n            layoutParams.gravity = Gravity.TOP;\n            mSunLayout.setVisibility(View.GONE);\n            setHeaderView(mSunLayout);\n        } else {\n            mMaterialHeaderView = new MaterialHeaderView(context);\n            LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dip2px(context, hIGHER_HEAD_HEIGHT));\n            layoutParams.gravity = Gravity.TOP;\n            mMaterialHeaderView.setLayoutParams(layoutParams);\n            mMaterialHeaderView.setWaveColor(isShowWave ? waveColor : Color.TRANSPARENT);\n            mMaterialHeaderView.showProgressArrow(showArrow);\n            mMaterialHeaderView.setProgressSize(progressSize);\n            mMaterialHeaderView.setProgressColors(colorSchemeColors);\n            mMaterialHeaderView.setProgressStokeWidth(PROGRESS_STOKE_WIDTH);\n            mMaterialHeaderView.setTextType(textType);\n            mMaterialHeaderView.setProgressTextColor(progressTextColor);\n            mMaterialHeaderView.setProgressValue(progressValue);\n            mMaterialHeaderView.setProgressValueMax(progressMax);\n            mMaterialHeaderView.setIsProgressBg(showProgressBg);\n            mMaterialHeaderView.setProgressBg(progressBg);\n            mMaterialHeaderView.setVisibility(View.GONE);\n            setHeaderView(mMaterialHeaderView);\n        }\n\n\n        mMaterialFooterView = new MaterialFooterView(context);\n        LayoutParams layoutParams2 = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Util.dip2px(context, hIGHER_HEAD_HEIGHT));\n        layoutParams2.gravity = Gravity.BOTTOM;\n        mMaterialFooterView.setLayoutParams(layoutParams2);\n        mMaterialFooterView.showProgressArrow(showArrow);\n        mMaterialFooterView.setProgressSize(progressSize);\n        mMaterialFooterView.setProgressColors(colorSchemeColors);\n        mMaterialFooterView.setProgressStokeWidth(PROGRESS_STOKE_WIDTH);\n        mMaterialFooterView.setTextType(textType);\n        mMaterialFooterView.setProgressValue(progressValue);\n        mMaterialFooterView.setProgressValueMax(progressMax);\n        mMaterialFooterView.setIsProgressBg(showProgressBg);\n        mMaterialFooterView.setProgressBg(progressBg);\n        mMaterialFooterView.setVisibility(View.GONE);\n        setFooderView(mMaterialFooterView);\n    }\n\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        if (isRefreshing) return true;\n        switch (ev.getAction()) {\n            case MotionEvent.ACTION_DOWN:\n                mTouchY = ev.getY();\n                mCurrentY = mTouchY;\n                break;\n            case MotionEvent.ACTION_MOVE:\n                float currentY = ev.getY();\n                float dy = currentY - mTouchY;\n                if (dy > 0 && !canChildScrollUp()) {\n                    if (mMaterialHeaderView != null) {\n                        mMaterialHeaderView.setVisibility(View.VISIBLE);\n                        mMaterialHeaderView.onBegin(this);\n                    } else if (mSunLayout != null) {\n                        mSunLayout.setVisibility(View.VISIBLE);\n                        mSunLayout.onBegin(this);\n                    }\n                    return true;\n                } else if (dy < 0 && !canChildScrollDown() && isLoadMore) {\n                    if (mMaterialFooterView != null && !isLoadMoreing) {\n                        soveLoadMoreLogic();\n                    }\n                    return super.onInterceptTouchEvent(ev);\n                }\n                break;\n        }\n        return super.onInterceptTouchEvent(ev);\n    }\n\n    private void soveLoadMoreLogic() {\n        isLoadMoreing = true;\n        mMaterialFooterView.setVisibility(View.VISIBLE);\n        mMaterialFooterView.onBegin(this);\n        mMaterialFooterView.onRefreshing(this);\n        if (refreshListener != null) {\n            refreshListener.onRefreshLoadMore(MaterialRefreshLayout.this);\n        }\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent e) {\n        if (isRefreshing) {\n            return super.onTouchEvent(e);\n        }\n\n        switch (e.getAction()) {\n            case MotionEvent.ACTION_MOVE:\n                mCurrentY = e.getY();\n                float dy = mCurrentY - mTouchY;\n                dy = Math.min(mWaveHeight * 2, dy);\n                dy = Math.max(0, dy);\n                if (mChildView != null) {\n                    float offsetY = decelerateInterpolator.getInterpolation(dy / mWaveHeight / 2) * dy / 2;\n                    float fraction = offsetY / mHeadHeight;\n                    if (mMaterialHeaderView != null) {\n                        mMaterialHeaderView.getLayoutParams().height = (int) offsetY;\n                        mMaterialHeaderView.requestLayout();\n                        mMaterialHeaderView.onPull(this, fraction);\n                    } else if (mSunLayout != null) {\n                        mSunLayout.getLayoutParams().height = (int) offsetY;\n                        mSunLayout.requestLayout();\n                        mSunLayout.onPull(this, fraction);\n                    }\n                    if (!isOverlay)\n                        ViewCompat.setTranslationY(mChildView, offsetY);\n\n                }\n                return true;\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP:\n                if (mChildView != null) {\n                    if (mMaterialHeaderView != null) {\n                        if (isOverlay) {\n                            if (mMaterialHeaderView.getLayoutParams().height > mHeadHeight) {\n\n                                updateListener();\n\n                                mMaterialHeaderView.getLayoutParams().height = (int) mHeadHeight;\n                                mMaterialHeaderView.requestLayout();\n\n                            } else {\n                                mMaterialHeaderView.getLayoutParams().height = 0;\n                                mMaterialHeaderView.requestLayout();\n                            }\n\n                        } else {\n                            if (ViewCompat.getTranslationY(mChildView) >= mHeadHeight) {\n                                createAnimatorTranslationY(mChildView, mHeadHeight, mMaterialHeaderView);\n                                updateListener();\n                            } else {\n                                createAnimatorTranslationY(mChildView, 0, mMaterialHeaderView);\n                            }\n                        }\n                    } else if (mSunLayout != null) {\n                        if (isOverlay) {\n                            if (mSunLayout.getLayoutParams().height > mHeadHeight) {\n\n                                updateListener();\n\n                                mSunLayout.getLayoutParams().height = (int) mHeadHeight;\n                                mSunLayout.requestLayout();\n\n                            } else {\n                                mSunLayout.getLayoutParams().height = 0;\n                                mSunLayout.requestLayout();\n                            }\n\n                        } else {\n                            if (ViewCompat.getTranslationY(mChildView) >= mHeadHeight) {\n                                createAnimatorTranslationY(mChildView, mHeadHeight, mSunLayout);\n                                updateListener();\n                            } else {\n                                createAnimatorTranslationY(mChildView, 0, mSunLayout);\n                            }\n                        }\n                    }\n\n\n                }\n                return true;\n        }\n\n        return super.\n\n                onTouchEvent(e);\n\n    }\n\n    public void setSunStyle(boolean isSunStyle) {\n        this.isSunStyle = isSunStyle;\n    }\n\n    public void autoRefresh() {\n        this.postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                if (!isRefreshing) {\n                    if (mMaterialHeaderView != null) {\n                        mMaterialHeaderView.setVisibility(View.VISIBLE);\n\n                        if (isOverlay) {\n                            mMaterialHeaderView.getLayoutParams().height = (int) mHeadHeight;\n                            mMaterialHeaderView.requestLayout();\n                        } else {\n                            createAnimatorTranslationY(mChildView, mHeadHeight, mMaterialHeaderView);\n                        }\n                    } else if (mSunLayout != null) {\n                        mSunLayout.setVisibility(View.VISIBLE);\n                        if (isOverlay) {\n                            mSunLayout.getLayoutParams().height = (int) mHeadHeight;\n                            mSunLayout.requestLayout();\n                        } else {\n                            createAnimatorTranslationY(mChildView, mHeadHeight, mSunLayout);\n                        }\n                    }\n\n                    updateListener();\n\n\n                }\n            }\n        }, 50);\n\n\n    }\n\n    public void autoRefreshLoadMore() {\n        this.post(new Runnable() {\n            @Override\n            public void run() {\n                if (isLoadMore) {\n                    soveLoadMoreLogic();\n                } else {\n                    throw new RuntimeException(\"you must setLoadMore ture\");\n                }\n            }\n        });\n    }\n\n    public void updateListener() {\n        isRefreshing = true;\n\n        if (mMaterialHeaderView != null) {\n            mMaterialHeaderView.onRefreshing(MaterialRefreshLayout.this);\n        } else if (mSunLayout != null) {\n            mSunLayout.onRefreshing(MaterialRefreshLayout.this);\n        }\n\n        if (refreshListener != null) {\n            refreshListener.onRefresh(MaterialRefreshLayout.this);\n        }\n\n    }\n\n    public void setLoadMore(boolean isLoadMore) {\n        this.isLoadMore = isLoadMore;\n    }\n\n    public void setProgressColors(int[] colors) {\n        this.colorSchemeColors = colors;\n    }\n\n    public void setShowArrow(boolean showArrow) {\n        this.showArrow = showArrow;\n    }\n\n    public void setShowProgressBg(boolean showProgressBg) {\n        this.showProgressBg = showProgressBg;\n    }\n\n    public void setWaveColor(int waveColor) {\n        this.waveColor = waveColor;\n    }\n\n    public void setWaveShow(boolean isShowWave) {\n        this.isShowWave = isShowWave;\n    }\n\n    public void setIsOverLay(boolean isOverLay) {\n        this.isOverlay = isOverLay;\n    }\n\n//    public void setProgressValue(int progressValue) {\n//        this.progressValue = progressValue;\n//        mMaterialHeaderView.setProgressValue(progressValue);\n//    }\n\n    public void createAnimatorTranslationY(final View v, final float h, final FrameLayout fl) {\n        ViewPropertyAnimatorCompat viewPropertyAnimatorCompat = ViewCompat.animate(v);\n        viewPropertyAnimatorCompat.setDuration(250);\n        viewPropertyAnimatorCompat.setInterpolator(new DecelerateInterpolator());\n        viewPropertyAnimatorCompat.translationY(h);\n        viewPropertyAnimatorCompat.start();\n        viewPropertyAnimatorCompat.setUpdateListener(new ViewPropertyAnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(View view) {\n                float height = ViewCompat.getTranslationY(v);\n                fl.getLayoutParams().height = (int) height;\n                fl.requestLayout();\n            }\n        });\n    }\n\n    /**\n     * @return Whether it is possible for the child view of this layout to\n     * scroll up. Override this if the child view is a custom view.\n     */\n    public boolean canChildScrollUp() {\n        if (mChildView == null) {\n            return false;\n        }\n        if (Build.VERSION.SDK_INT < 14) {\n            if (mChildView instanceof AbsListView) {\n                final AbsListView absListView = (AbsListView) mChildView;\n                return absListView.getChildCount() > 0\n                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)\n                        .getTop() < absListView.getPaddingTop());\n            } else {\n                return ViewCompat.canScrollVertically(mChildView, -1) || mChildView.getScrollY() > 0;\n            }\n        } else {\n            return ViewCompat.canScrollVertically(mChildView, -1);\n        }\n    }\n\n    public boolean canChildScrollDown() {\n        if (mChildView == null) {\n            return false;\n        }\n        if (android.os.Build.VERSION.SDK_INT < 14) {\n            if (mChildView instanceof AbsListView) {\n                final AbsListView absListView = (AbsListView) mChildView;\n                if (absListView.getChildCount() > 0) {\n                    int lastChildBottom = absListView.getChildAt(absListView.getChildCount() - 1).getBottom();\n                    return absListView.getLastVisiblePosition() == absListView.getAdapter().getCount() - 1 && lastChildBottom <= absListView.getMeasuredHeight();\n                } else {\n                    return false;\n                }\n\n            } else {\n                return ViewCompat.canScrollVertically(mChildView, 1) || mChildView.getScrollY() > 0;\n            }\n        } else {\n            return ViewCompat.canScrollVertically(mChildView, 1);\n        }\n    }\n\n    public void setWaveHigher() {\n        headHeight = hIGHER_HEAD_HEIGHT;\n        waveHeight = HIGHER_WAVE_HEIGHT;\n        MaterialWaveView.DefaulHeadHeight = hIGHER_HEAD_HEIGHT;\n        MaterialWaveView.DefaulWaveHeight = HIGHER_WAVE_HEIGHT;\n    }\n\n    public void finishRefreshing() {\n        if (mChildView != null) {\n            ViewPropertyAnimatorCompat viewPropertyAnimatorCompat = ViewCompat.animate(mChildView);\n            viewPropertyAnimatorCompat.setDuration(200);\n            viewPropertyAnimatorCompat.y(ViewCompat.getTranslationY(mChildView));\n            viewPropertyAnimatorCompat.translationY(0);\n            viewPropertyAnimatorCompat.setInterpolator(new DecelerateInterpolator());\n            viewPropertyAnimatorCompat.start();\n\n            if (mMaterialHeaderView != null) {\n                mMaterialHeaderView.onComlete(MaterialRefreshLayout.this);\n            } else if (mSunLayout != null) {\n                mSunLayout.onComlete(MaterialRefreshLayout.this);\n            }\n\n            if (refreshListener != null) {\n                refreshListener.onfinish();\n            }\n        }\n        isRefreshing = false;\n        progressValue = 0;\n    }\n\n    public void finishRefresh() {\n        this.post(new Runnable() {\n            @Override\n            public void run() {\n                finishRefreshing();\n            }\n        });\n    }\n\n    public void finishRefreshLoadMore() {\n        this.post(new Runnable() {\n            @Override\n            public void run() {\n                if (mMaterialFooterView != null && isLoadMoreing) {\n                    isLoadMoreing = false;\n                    mMaterialFooterView.onComlete(MaterialRefreshLayout.this);\n                }\n            }\n        });\n\n    }\n\n    private void setHeaderView(final View headerView) {\n        addView(headerView);\n    }\n\n    public void setHeader(final View headerView) {\n        setHeaderView(headerView);\n    }\n\n    public void setFooderView(final View fooderView) {\n        this.addView(fooderView);\n    }\n\n\n    public void setWaveHeight(float waveHeight) {\n        this.mWaveHeight = waveHeight;\n    }\n\n    public void setHeaderHeight(float headHeight) {\n        this.mHeadHeight = headHeight;\n    }\n\n    public void setMaterialRefreshListener(MaterialRefreshListener refreshListener) {\n        this.refreshListener = refreshListener;\n    }\n\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialRefreshListener.java",
    "content": "package com.cjj;\n\npublic abstract class MaterialRefreshListener {\n    public void onfinish(){};\n    public abstract void onRefresh(MaterialRefreshLayout materialRefreshLayout);\n    public void onRefreshLoadMore(MaterialRefreshLayout materialRefreshLayout){};\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/MaterialWaveView.java",
    "content": "package com.cjj;\n\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.animation.BounceInterpolator;\nimport android.view.animation.DecelerateInterpolator;\n\npublic class MaterialWaveView extends View implements MaterialHeadListener {\n    private int waveHeight;\n    private int headHeight;\n    public static int DefaulWaveHeight;\n    public static int DefaulHeadHeight;\n    private Path path;\n    private Paint paint;\n    private int color;\n\n    public MaterialWaveView(Context context) {\n        this(context, null, 0);\n    }\n\n    public MaterialWaveView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public MaterialWaveView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        setWillNotDraw(false);\n        path = new Path();\n        paint = new Paint();\n        paint.setAntiAlias(true);\n    }\n\n    public int getColor() {\n        return color;\n    }\n\n    public void setColor(int color) {\n        this.color = color;\n        invalidate();\n    }\n\n    public int getHeadHeight() {\n        return headHeight;\n    }\n\n    public void setHeadHeight(int headHeight) {\n        this.headHeight = headHeight;\n    }\n\n    public int getWaveHeight() {\n        return waveHeight;\n    }\n\n    public void setWaveHeight(int waveHeight) {\n        this.waveHeight = waveHeight;\n    }\n\n    public int getDefaulWaveHeight() {\n        return DefaulWaveHeight;\n    }\n\n    public void setDefaulWaveHeight(int defaulWaveHeight) {\n        DefaulWaveHeight = defaulWaveHeight;\n    }\n\n    public int getDefaulHeadHeight() {\n        return DefaulHeadHeight;\n    }\n\n    public void setDefaulHeadHeight(int defaulHeadHeight) {\n        DefaulHeadHeight = defaulHeadHeight;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        path.reset();\n        paint.setColor(color);\n        path.lineTo(0, headHeight);\n        path.quadTo(getMeasuredWidth() / 2, headHeight + waveHeight, getMeasuredWidth(), headHeight);\n        path.lineTo(getMeasuredWidth(), 0);\n        canvas.drawPath(path, paint);\n    }\n\n\n    @Override\n    public void onComlete(MaterialRefreshLayout br) {\n        waveHeight = 0;\n        ValueAnimator animator =ValueAnimator.ofInt(headHeight,0);\n        animator.setDuration(200);\n        animator.setInterpolator(new DecelerateInterpolator());\n        animator.start();\n        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                int value = (int) animation.getAnimatedValue();\n                headHeight = value;\n                invalidate();\n            }\n        });\n    }\n\n    @Override\n    public void onBegin(MaterialRefreshLayout br) {\n\n    }\n\n    @Override\n    public void onPull(MaterialRefreshLayout br, float fraction) {\n        setHeadHeight((int) (Util.dip2px(getContext(), DefaulHeadHeight) * Util.limitValue(1, fraction)));\n        setWaveHeight((int) (Util.dip2px(getContext(), DefaulWaveHeight) * Math.max(0, fraction - 1)));\n        invalidate();\n    }\n\n    @Override\n    public void onRelease(MaterialRefreshLayout br, float fraction) {\n\n    }\n\n    @Override\n    public void onRefreshing(MaterialRefreshLayout br) {\n        setHeadHeight((int) (Util.dip2px(getContext(), DefaulHeadHeight)));\n        ValueAnimator animator = ValueAnimator.ofInt(getWaveHeight(),0);\n        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n            @Override\n            public void onAnimationUpdate(ValueAnimator animation) {\n                Log.i(\"anim\", \"value--->\" + (int) animation.getAnimatedValue());\n                setWaveHeight((int) animation.getAnimatedValue());\n                invalidate();\n            }\n        });\n        animator.setInterpolator(new BounceInterpolator());\n        animator.setDuration(200);\n        animator.start();\n    }\n\n\n\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/SunFaceView.java",
    "content": "package com.cjj;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.util.TypedValue;\nimport android.view.View;\n\n/**\n * Created by cjj on 2016/2/20.\n */\npublic class SunFaceView extends View {\n\n    private static final String Tag = SunFaceView.class.getSimpleName();\n    private static final int DEFAULT_SUN_RADIUS = 12;//太阳的半径\n    private static final int DEFAULT_EYES_RADIUS = 2;//眼睛的半径\n\n    private int mHeight, mWidth;\n    private Paint mCirclePaint;//画圆的画笔\n    private int mSunRadius;//sun radius\n    private int mEyesRadius = DEFAULT_EYES_RADIUS;\n    private Rect debugRect;\n    private RectF mouthRect;\n    private int mSunColor;//sun color\n    private boolean isDrawFace = true;\n    private int mouthStro = 3;\n\n    public SunFaceView(Context context) {\n        this(context, null);\n    }\n\n    public SunFaceView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public SunFaceView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        Log.i(Tag, \"init\");\n\n        mSunRadius = changeDp(DEFAULT_SUN_RADIUS);\n        mEyesRadius = changeDp(DEFAULT_EYES_RADIUS);\n\n        //圆的配置\n        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        mCirclePaint.setColor(Color.RED);\n        mCirclePaint.setStyle(Paint.Style.FILL);\n\n        debugRect = new Rect();\n        mouthRect = new RectF();\n    }\n\n    /**\n     * 设置太阳半径\n     *\n     * @param sunRadius\n     */\n    public void setSunRadius(int sunRadius) {\n        mSunRadius = changeDp(sunRadius);\n        invalidate();\n    }\n\n    /**\n     * 设置眼睛大小\n     * @param eyesSize\n     */\n    public void setEyesSize(int eyesSize){\n        mEyesRadius = changeDp(eyesSize);\n        invalidate();\n    }\n\n    /**\n     * 设置嘴巴粗细\n     * @param mouthStro\n     */\n    public void setMouthStro(int mouthStro){\n        this.mouthStro = mouthStro;\n        invalidate();\n    }\n\n\n    /**\n     * 刷新用的效果\n     * @param sunRadius\n     * @param per\n     */\n    public void setPerView(int sunRadius ,float per){\n        sunRadius = changeDp(sunRadius);\n        if(per>=0.8){\n            isDrawFace = true;\n        }else {\n            isDrawFace = false;\n        }\n        per = Math.min(per,1);\n        float tempRadius =  sunRadius*per;\n        mSunRadius = (int) tempRadius;\n        mCirclePaint.setAlpha((int)per*255);\n        invalidate();\n    }\n\n    /**\n     * 设置sun color\n     *\n     * @param sunColor\n     */\n    public void setSunColor(int sunColor) {\n        mSunColor = sunColor;\n        invalidate();\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        Log.i(Tag, \"w---->\" + w + \"  -------  h----->\" + h);\n        mWidth = w;\n        mHeight = h;\n\n\n        debugRect.left = mWidth / 2 - mSunRadius;\n        debugRect.right = mWidth / 2 + mSunRadius;\n        debugRect.top = mHeight / 2 - mSunRadius;\n        debugRect.bottom = mHeight / 2 + mSunRadius;\n\n\n        mouthRect.left = mWidth / 2 - mSunRadius / 2;\n        mouthRect.right = mWidth / 2 + mSunRadius / 2;\n        mouthRect.top = mHeight / 2 - mSunRadius / 2;\n        mouthRect.bottom = mHeight / 2 + mSunRadius / 2;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        Log.i(Tag, \"onMeasure\");\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        int width;\n        int height;\n        if (widthMode == MeasureSpec.EXACTLY) {\n            width = widthSize;\n        } else {\n            width = mSunRadius * 2 + getPaddingRight() + getPaddingLeft();\n        }\n\n        if (heightMode == MeasureSpec.EXACTLY) {\n            height = heightSize;\n        } else {\n\n            height = mSunRadius * 2 + getPaddingTop() + getPaddingBottom();\n        }\n\n        setMeasuredDimension(width, height);\n    }\n\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n        drawCircle(canvas);\n        mCirclePaint.setStyle(Paint.Style.STROKE);\n//        canvas.drawRect(debugRect, mCirclePaint);\n//        canvas.drawRect(mouthRect, mCirclePaint);\n    }\n\n\n    private void drawCircle(Canvas canvas) {\n        mCirclePaint.setColor(mSunColor);\n        mCirclePaint.setStyle(Paint.Style.FILL);\n        canvas.drawCircle(mWidth / 2, mHeight / 2, mSunRadius, mCirclePaint);\n        mCirclePaint.setColor(Color.WHITE);\n        if(isDrawFace)\n        {\n            canvas.save();\n            canvas.drawCircle(mWidth / 2 - mSunRadius / 2 + mEyesRadius, mHeight / 2 - mSunRadius / 2 + mEyesRadius, mEyesRadius, mCirclePaint);\n            canvas.drawCircle(mWidth / 2 + mSunRadius / 2 - mEyesRadius, mHeight / 2 - mSunRadius / 2 + mEyesRadius, mEyesRadius, mCirclePaint);\n            mCirclePaint.setStyle(Paint.Style.STROKE);\n            mCirclePaint.setStrokeWidth(mouthStro);\n            canvas.drawArc(mouthRect, 20, 140, false, mCirclePaint);\n            canvas.restore();\n        }\n\n    }\n\n\n    public int changeDp(int value) {\n        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,\n                getResources().getDisplayMetrics());\n    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/SunLayout.java",
    "content": "package com.cjj;\n\nimport android.animation.ObjectAnimator;\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.support.v4.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.animation.LinearInterpolator;\nimport android.widget.FrameLayout;\n\n/**\n * Created by cjj on 2016/2/22.\n */\npublic class SunLayout extends FrameLayout implements MaterialHeadListener {\n\n    private final static String Tag = SunLayout.class.getSimpleName();\n    protected static final int DEFAULT_SUN_RADIUS = 12;//太阳的半径\n    private static final int DEFAULT_SUN_COLOR = Color.RED;\n    private static final int DEFAULT_SUN_EYES_SIZE = 2;\n    private static final int DEFAULT_LINE_HEIGHT = 3;\n    private static final int DEFAULT_LINE_WIDTH = 1;\n    private static final int DEFAULT_LINE_LEVEL = 30;\n    private static final int DEFAULT_MOUTH_WIDTH = 3;\n    private static final int DEFAULT_LINE_COLOR = Color.RED;\n\n    protected SunFaceView mSunView;\n    protected SunLineView mLineView;\n    private int mSunRadius;\n    private int mSunColor;\n    private int mEyesSize;\n    private int mLineLevel;\n    private int mMouthStro;\n    private int mLineColor, mLineWidth, mLineHeight;\n\n    private ObjectAnimator mAnimator;\n\n    public SunLayout(Context context) {\n        this(context, null);\n    }\n\n    public SunLayout(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public SunLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        /**\n         * 这里偷懒了，如果你想在xml配置太阳的各个属性，可以在这里设置，然后传给SunFaceView和SunLineView就可以了\n         */\n        mSunRadius = DEFAULT_SUN_RADIUS;\n        mSunColor = DEFAULT_SUN_COLOR;\n        mEyesSize = DEFAULT_SUN_EYES_SIZE;\n        mLineColor = DEFAULT_LINE_COLOR;\n        mLineHeight = DEFAULT_LINE_HEIGHT;\n        mLineWidth = DEFAULT_LINE_WIDTH;\n        mLineLevel = DEFAULT_LINE_LEVEL;\n        mMouthStro = DEFAULT_MOUTH_WIDTH;\n\n        Context context = getContext();\n        mSunView = new SunFaceView(context);\n        mSunView.setSunRadius(mSunRadius);\n        mSunView.setSunColor(mSunColor);\n        mSunView.setEyesSize(mEyesSize);\n        mSunView.setMouthStro(mMouthStro);\n        addView(mSunView);\n\n        mLineView = new SunLineView(context);\n        mLineView.setSunRadius(mSunRadius);\n        mLineView.setLineLevel(mLineLevel);\n        mLineView.setLineColor(mLineColor);\n        mLineView.setLineHeight(mLineHeight);\n        mLineView.setLineWidth(mLineWidth);\n        addView(mLineView);\n\n        startSunLineAnim(mLineView);\n    }\n\n    /**\n     * 设置太阳半径\n     *\n     * @param sunRadius\n     */\n    public void setSunRadius(int sunRadius) {\n        mSunRadius = sunRadius;\n        mSunView.setSunRadius(mSunRadius);\n        mLineView.setSunRadius(mSunRadius);\n    }\n\n    /**\n     * 设置太阳颜色\n     *\n     * @param sunColor\n     */\n    public void setSunColor(int sunColor) {\n        mSunColor = sunColor;\n        mSunView.setSunColor(mSunColor);\n    }\n\n    /**\n     * 设置太阳眼睛大小\n     *\n     * @param eyesSize\n     */\n    public void setEyesSize(int eyesSize) {\n        mEyesSize = eyesSize;\n        mSunView.setEyesSize(mEyesSize);\n    }\n\n    /**\n     * 设置太阳线的数量等级\n     *\n     * @param level\n     */\n    public void setLineLevel(int level) {\n        mLineLevel = level;\n        mLineView.setLineLevel(mLineLevel);\n    }\n\n    /**\n     * 设置太阳线的颜色\n     *\n     * @param lineColor\n     */\n    public void setLineColor(int lineColor) {\n        mLineColor = lineColor;\n        mLineView.setLineColor(mLineColor);\n    }\n\n    /**\n     * 设置太阳线的宽度\n     *\n     * @param lineWidth\n     */\n    public void setLineWidth(int lineWidth) {\n        mLineWidth = lineWidth;\n        mLineView.setLineWidth(mLineWidth);\n    }\n\n    /**\n     * 设置太阳线的长度\n     *\n     * @param lineHeight\n     */\n    public void setLineHeight(int lineHeight) {\n        mLineHeight = lineHeight;\n        mLineView.setLineHeight(mLineHeight);\n    }\n\n    /**\n     * 设置嘴巴粗细\n     *\n     * @param mouthStro\n     */\n    public void setMouthStro(int mouthStro) {\n        mMouthStro = mouthStro;\n        mSunView.setMouthStro(mMouthStro);\n    }\n\n\n    /**\n     * 开启转圈圈\n     *\n     * @param v\n     */\n    public void startSunLineAnim(View v) {\n        if (mAnimator == null) {\n            mAnimator = ObjectAnimator.ofFloat(v, \"rotation\", 0f, 720f);\n            mAnimator.setDuration(7 * 1000);\n            mAnimator.setInterpolator(new LinearInterpolator());\n            mAnimator.setRepeatCount(ValueAnimator.INFINITE);\n        }\n        if (!mAnimator.isRunning())\n            mAnimator.start();\n    }\n\n    /**\n     * 停止动画\n     */\n    public void cancelSunLineAnim() {\n        if (mAnimator != null) {\n            mAnimator.cancel();\n        }\n    }\n\n    @Override\n    public void onComlete(MaterialRefreshLayout materialRefreshLayout) {\n        cancelSunLineAnim();\n        ViewCompat.setScaleX(this, 0);\n        ViewCompat.setScaleY(this, 0);\n    }\n\n    @Override\n    public void onBegin(MaterialRefreshLayout materialRefreshLayout) {\n\n        ViewCompat.setScaleX(this, 0.001f);\n        ViewCompat.setScaleY(this, 0.001f);\n    }\n\n    @Override\n    public void onPull(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n        float a = Util.limitValue(1, fraction);\n        if (a >= 0.7) {\n            mLineView.setVisibility(View.VISIBLE);\n        } else {\n            mLineView.setVisibility(View.GONE);\n        }\n        mSunView.setPerView(mSunRadius, a);\n        ViewCompat.setScaleX(this, a);\n        ViewCompat.setScaleY(this, a);\n        ViewCompat.setAlpha(this, a);\n    }\n\n    @Override\n    public void onRelease(MaterialRefreshLayout materialRefreshLayout, float fraction) {\n\n    }\n\n    @Override\n    public void onRefreshing(MaterialRefreshLayout materialRefreshLayout) {\n        startSunLineAnim(mLineView);\n    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/SunLineView.java",
    "content": "package com.cjj;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.DrawFilter;\nimport android.graphics.Paint;\nimport android.graphics.PaintFlagsDrawFilter;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.util.TypedValue;\nimport android.view.View;\n\n/**\n * Created by cjj on 2016/2/22.\n */\npublic class SunLineView extends View {\n\n    private static final String Tag = SunLineView.class.getSimpleName();\n\n    private int mHeight, mWidth;\n    private Paint mLinePaint;//线的画笔\n    private int mLineLeft, mLineTop;// 线的左、上位置\n    private int mLineHeight;  // 短线长度\n    private int mLineWidth;//线宽度\n    private int mFixLineHeight;\n    private int mLineBottom;\n    private int mSunRadius;\n    private Rect debugRect;\n    private RectF mouthRect;\n    private DrawFilter mDrawFilter;\n    private int mLineColor;\n    private int mLineLevel;\n\n    public SunLineView(Context context) {\n        this(context, null);\n    }\n\n    public SunLineView(Context context, AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public SunLineView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        Log.i(Tag, \"init\");\n\n        mLineWidth = changeDp(1);\n        mLineHeight = changeDp(3);\n        mFixLineHeight = changeDp(6);\n        mSunRadius = changeDp(12);\n        mLineColor = Color.RED;\n        mLineLevel = 30;\n\n        //线的配置\n        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        mLinePaint.setColor(mLineColor);\n        mLinePaint.setStyle(Paint.Style.FILL_AND_STROKE);\n        // 设置画笔宽度\n        mLinePaint.setStrokeWidth(mLineWidth);\n        mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG\n                | Paint.FILTER_BITMAP_FLAG);\n        debugRect = new Rect();\n        mouthRect = new RectF();\n    }\n\n    public void setLineColor(int lineColor){\n        mLineColor = lineColor;\n        invalidate();\n    }\n\n    public void setLineWidth(int lineWidth){\n        mLineWidth = changeDp(lineWidth);\n        invalidate();\n    }\n\n    public void setLineHeight(int lineHeight){\n        mLineHeight = changeDp(lineHeight);\n        mFixLineHeight = mLineHeight*2;\n        invalidate();\n    }\n\n    /**\n     * 设置太阳半径\n     *\n     * @param sunRadius\n     */\n    public void setSunRadius(int sunRadius) {\n        mSunRadius = changeDp(sunRadius);\n        invalidate();\n    }\n\n    public void setLineLevel(int level){\n        mLineLevel = level;\n        invalidate();\n    }\n\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        Log.i(Tag, \"w---->\" + w + \"  -------  h----->\" + h);\n        mWidth = w;\n        mHeight = h;\n\n        mLineLeft = mWidth / 2 - mLineWidth / 2;\n        mLineTop = h / 2 - mSunRadius - mFixLineHeight;\n        mLineBottom = mLineTop + mLineHeight;\n\n        debugRect.left = mWidth / 2 - mSunRadius - mFixLineHeight;\n        debugRect.right = mWidth / 2 + mSunRadius + mFixLineHeight;\n        debugRect.top = mHeight / 2 - mSunRadius - mFixLineHeight;\n        debugRect.bottom = mHeight / 2 + mSunRadius + mFixLineHeight;\n\n\n        mouthRect.left = mWidth / 2 - mSunRadius / 2;\n        mouthRect.right = mWidth / 2 + mSunRadius / 2;\n        mouthRect.top = mHeight / 2 - mSunRadius / 2;\n        mouthRect.bottom = mHeight / 2 + mSunRadius / 2;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        Log.i(Tag, \"onMeasure\");\n        int widthMode = MeasureSpec.getMode(widthMeasureSpec);\n        int widthSize = MeasureSpec.getSize(widthMeasureSpec);\n        int heightMode = MeasureSpec.getMode(heightMeasureSpec);\n        int heightSize = MeasureSpec.getSize(heightMeasureSpec);\n        int width;\n        int height;\n        if (widthMode == MeasureSpec.EXACTLY) {\n            width = widthSize;\n        } else {\n            width = (mSunRadius + mFixLineHeight + mLineHeight) * 2 + getPaddingRight() + getPaddingLeft();\n        }\n\n        if (heightMode == MeasureSpec.EXACTLY) {\n            height = heightSize;\n        } else {\n\n            height = (mSunRadius + mFixLineHeight + mLineHeight) * 2 + getPaddingTop() + getPaddingBottom();\n        }\n\n        setMeasuredDimension(width, height);\n    }\n\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        canvas.setDrawFilter(mDrawFilter);\n        super.onDraw(canvas);\n        drawLines(canvas);\n    }\n\n    /**\n     * 绘制line\n     * @param canvas\n     */\n    private void drawLines(Canvas canvas) {\n        for (int i = 0; i <= 360; i++) {\n            if (i % mLineLevel == 0) {\n                canvas.save();\n                canvas.rotate(i, mWidth / 2, mHeight / 2);\n                canvas.drawLine(mLineLeft, mLineTop, mLineLeft, mLineBottom, mLinePaint);\n                canvas.restore();\n            }\n        }\n    }\n\n    public int changeDp(int value) {\n        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,\n                getResources().getDisplayMetrics());\n    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/java/com/cjj/Util.java",
    "content": "package com.cjj;\n\nimport android.content.Context;\n\npublic class Util {\n\n    public static int dip2px(Context context, float dpValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n    public static int px2dip(Context context, float pxValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (pxValue / scale + 0.5f);\n    }\n\n    public static float limitValue(float a, float b) {\n        float valve = 0;\n        final float min = Math.min(a, b);\n        final float max = Math.max(a, b);\n        valve = valve > min ? valve : min;\n        valve = valve < max ? valve : max;\n        return valve;\n    }\n}\n"
  },
  {
    "path": "MaterialRefresh_library/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- style for MaterialRefreshLayout  -->\n    <declare-styleable name=\"MaterialRefreshLayout\">\n        <attr name=\"overlay\" format=\"boolean\"/>\n        <!-- style for MaterialWaveView  -->\n        <attr name=\"wave_height_type\" format=\"enum\">\n            <enum name=\"normal\" value=\"0\"/>\n            <enum name=\"higher\" value=\"1\"/>\n        </attr>\n        <attr name=\"wave_color\" format=\"color\"/>\n        <attr name=\"wave_show\" format=\"boolean\"/>\n        <!-- style for CircleProgressBar  -->\n        <attr name=\"progress_colors\" format=\"reference\"/>\n        <attr name=\"progress_stoke_width\" format=\"dimension\"/>\n        <attr name=\"progress_backgroud_color\" format=\"color\"/>\n        <attr name=\"progress_show_arrow\" format=\"boolean\"/>\n        <attr name=\"progress_arrow_width\" format=\"dimension\"/>\n        <attr name=\"progress_arrow_height\" format=\"dimension\"/>\n        <attr name=\"progress_show_circle_backgroud\" format=\"boolean\"/>\n        <attr name=\"progress_value\" format=\"integer\"/>\n        <attr name=\"progress_max_value\" format=\"integer\"/>\n        <attr name=\"progress_text_size\" format=\"dimension\"/>\n        <attr name=\"progress_text_color\" format=\"color\"/>\n        <attr name=\"progress_text_visibility\" format=\"enum\">\n            <enum name=\"visible\" value=\"0\"/>\n            <enum name=\"invisible\" value=\"1\"/>\n        </attr>\n        <attr name=\"progress_size_type\" format=\"enum\">\n            <enum name=\"normal\" value=\"0\"></enum>\n            <enum name=\"big\" value=\"1\"></enum>\n        </attr>\n        <attr name=\"isLoadMore\" format=\"boolean\"></attr>\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "MaterialRefresh_library/src/main/res/values/attrs_circle_progressbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <declare-styleable name=\"CircleProgressBar\">\n        <attr name=\"mlpb_inner_radius\" format=\"dimension\"/>\n        <attr name=\"mlpb_background_color\" format=\"color\"/>\n        <attr name=\"mlpb_progress_color\" format=\"color\"/>\n        <attr name=\"mlpb_progress_stoke_width\" format=\"dimension\"/>\n        <attr name=\"mlpb_show_arrow\" format=\"boolean\"/>\n        <attr name=\"mlpb_enable_circle_background\" format=\"boolean\"/>\n        <attr name=\"mlpb_arrow_width\" format=\"dimension\"/>\n        <attr name=\"mlpb_arrow_height\" format=\"dimension\"/>\n\n        <attr name=\"mlpb_progress\" format=\"integer\"/>\n        <attr name=\"mlpb_max\" format=\"integer\"/>\n\n\n        <attr name=\"mlpb_progress_text_size\" format=\"dimension\"/>\n        <attr name=\"mlpb_progress_text_color\" format=\"color\"/>\n\n        <!--<attr name=\"mlpb_progress_text_offset\" format=\"dimension\"/>-->\n\n        <attr name=\"mlpb_progress_text_visibility\" format=\"enum\">\n            <enum name=\"visible\" value=\"0\"/>\n            <enum name=\"invisible\" value=\"1\"/>\n        </attr>\n    </declare-styleable>\n</resources>"
  },
  {
    "path": "MaterialRefresh_library/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"material_red\">#ffF44336</color>\n    <color name=\"material_green\">#ff4CAF50</color>\n    <color name=\"material_blue\">#ff03A9F4</color>\n    <color name=\"material_yellow\">#ffFFEB3B</color>\n</resources>"
  },
  {
    "path": "MaterialRefresh_library/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">BRLayoutLibrary</string>\n    <integer-array name=\"material_colors\">\n        <item>@color/material_red</item>\n        <item>@color/material_green</item>\n        <item>@color/material_blue</item>\n        <item>@color/material_yellow</item>\n    </integer-array>\n</resources>\n"
  },
  {
    "path": "PhotoView-master/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion '24.0.3'\n\n    defaultConfig {\n        applicationId \"uk.co.senab.photoview.sample\"\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 100\n        versionName \"1.0\"\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    compile project(':PhotoView_library')\n    compile 'com.android.support:appcompat-v7:24.2.1'\n    compile 'com.android.support:recyclerview-v7:24.2.1'\n    compile 'com.squareup.picasso:picasso:2.5.2'\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"uk.co.senab.photoview.sample\">\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n    <application\n        android:allowBackup=\"true\"\n        android:fullBackupContent=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity android:name=\".LauncherActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n\n        <activity android:name=\".SimpleSampleActivity\"/>\n\n        <activity android:name=\".ViewPagerActivity\"/>\n\n        <activity android:name=\".RotationSampleActivity\"/>\n\n        <activity android:name=\".PicassoSampleActivity\"/>\n\n        <activity android:name=\".ActivityTransitionActivity\"/>\n\n        <activity android:name=\".ActivityTransitionToActivity\"/>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/ActivityTransitionActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.sample;\n\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.ActivityOptionsCompat;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.GridLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.widget.Toast;\n\npublic class ActivityTransitionActivity extends AppCompatActivity {\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_transition);\n\n        RecyclerView list = (RecyclerView) findViewById(R.id.list);\n        list.setLayoutManager(new GridLayoutManager(this, 2));\n        ImageAdapter imageAdapter = new ImageAdapter(new ImageAdapter.Listener() {\n            @Override\n            public void onImageClicked(View view) {\n                transition(view);\n            }\n        });\n        list.setAdapter(imageAdapter);\n    }\n\n    private void transition(View view) {\n        if (Build.VERSION.SDK_INT < 21) {\n            Toast.makeText(ActivityTransitionActivity.this, \"21+ only, keep out\", Toast.LENGTH_SHORT).show();\n        } else {\n            Intent intent = new Intent(ActivityTransitionActivity.this, ActivityTransitionToActivity.class);\n            ActivityOptionsCompat options = ActivityOptionsCompat.\n                    makeSceneTransitionAnimation(ActivityTransitionActivity.this, view, getString(R.string.transition_test));\n            startActivity(intent, options.toBundle());\n        }\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/ActivityTransitionToActivity.java",
    "content": "package uk.co.senab.photoview.sample;\n\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v7.app.AppCompatActivity;\n\n/**\n * Activity that gets transitioned to\n */\npublic class ActivityTransitionToActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_transition_to);\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/HackyDrawerLayout.java",
    "content": "package uk.co.senab.photoview.sample;\n\nimport android.content.Context;\nimport android.support.v4.widget.DrawerLayout;\nimport android.view.MotionEvent;\n\n/**\n * Hacky fix for Issue #4 and\n * http://code.google.com/p/android/issues/detail?id=18990\n * <p/>\n * ScaleGestureDetector seems to mess up the touch events, which means that\n * ViewGroups which make use of onInterceptTouchEvent throw a lot of\n * IllegalArgumentException: pointerIndex out of range.\n * <p/>\n * There's not much I can do in my code for now, but we can mask the result by\n * just catching the problem and ignoring it.\n * Created by John on 10/1/15.\n */\npublic class HackyDrawerLayout extends DrawerLayout {\n\n    public HackyDrawerLayout(Context context) {\n        super(context);\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n        try {\n            return super.onInterceptTouchEvent(ev);\n        } catch (IllegalArgumentException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/HackyViewPager.java",
    "content": "package uk.co.senab.photoview.sample;\n\nimport android.content.Context;\nimport android.support.v4.view.ViewPager;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\n/**\n * Hacky fix for Issue #4 and\n * http://code.google.com/p/android/issues/detail?id=18990\n * <p/>\n * ScaleGestureDetector seems to mess up the touch events, which means that\n * ViewGroups which make use of onInterceptTouchEvent throw a lot of\n * IllegalArgumentException: pointerIndex out of range.\n * <p/>\n * There's not much I can do in my code for now, but we can mask the result by\n * just catching the problem and ignoring it.\n *\n * @author Chris Banes\n */\npublic class HackyViewPager extends ViewPager {\n\t\n    public HackyViewPager(Context context) {\n        super(context);\n    }\n\n    public HackyViewPager(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public boolean onInterceptTouchEvent(MotionEvent ev) {\n\t\ttry {\n\t\t\treturn super.onInterceptTouchEvent(ev);\n\t\t} catch (IllegalArgumentException e) {\n\t\t\te.printStackTrace();\n\t\t\treturn false;\n\t\t}\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/ImageAdapter.java",
    "content": "package uk.co.senab.photoview.sample;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.view.ViewGroup;\n\n/**\n * Image adapter\n */\npublic class ImageAdapter extends RecyclerView.Adapter<ImageViewHolder> {\n\n    Listener mListener;\n\n    public ImageAdapter(Listener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        ImageViewHolder holder = ImageViewHolder.inflate(parent);\n        holder.itemView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                mListener.onImageClicked(view);\n            }\n        });\n        return holder;\n    }\n\n    @Override\n    public void onBindViewHolder(ImageViewHolder holder, int position) {\n\n    }\n\n    @Override\n    public int getItemCount() {\n        return 20;\n    }\n\n    public interface Listener {\n        void onImageClicked(View view);\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/ImageViewHolder.java",
    "content": "package uk.co.senab.photoview.sample;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\n/**\n * Image in recyclerview\n */\npublic class ImageViewHolder extends RecyclerView.ViewHolder {\n\n    public static ImageViewHolder inflate(ViewGroup parent) {\n        View view = LayoutInflater.from(parent.getContext())\n                .inflate(R.layout.item_image, parent, false);\n        return new ImageViewHolder(view);\n    }\n\n    public TextView mTextTitle;\n\n    public ImageViewHolder(View view) {\n        super(view);\n        mTextTitle = (TextView) view.findViewById(R.id.title);\n    }\n\n    private void bind(String title) {\n        mTextTitle.setText(title);\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/LauncherActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.sample;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\npublic class LauncherActivity extends AppCompatActivity {\n\n    public static final String[] options = {\"Simple Sample\", \"ViewPager Sample\", \"Rotation Sample\", \"Picasso Sample\", \"Activity Transition Sample\"};\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_launcher);\n        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);\n        recyclerView.setLayoutManager(new LinearLayoutManager(this));\n        recyclerView.setAdapter(new ItemAdapter());\n    }\n\n\n    private static class ItemAdapter extends RecyclerView.Adapter<ItemViewHolder> {\n        @Override\n        public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n            return ItemViewHolder.newInstance(parent);\n        }\n\n        @Override\n        public void onBindViewHolder(final ItemViewHolder holder, int position) {\n            holder.bind(options[position]);\n            holder.itemView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    Class c;\n\n                    switch (holder.getAdapterPosition()) {\n                        default:\n                        case 0:\n                            c = SimpleSampleActivity.class;\n                            break;\n                        case 1:\n                            c = ViewPagerActivity.class;\n                            break;\n                        case 2:\n                            c = RotationSampleActivity.class;\n                            break;\n                        case 3:\n                            c = PicassoSampleActivity.class;\n                            break;\n                        case 4:\n                            c = ActivityTransitionActivity.class;\n                            break;\n                    }\n\n                    Context context = holder.itemView.getContext();\n                    context.startActivity(new Intent(context, c));\n                }\n            });\n        }\n\n        @Override\n        public int getItemCount() {\n            return options.length;\n        }\n    }\n\n    private static class ItemViewHolder extends RecyclerView.ViewHolder {\n\n        public static ItemViewHolder newInstance(ViewGroup parent) {\n            View view = LayoutInflater.from(parent.getContext())\n                    .inflate(R.layout.item_list_item, parent, false);\n            return new ItemViewHolder(view);\n        }\n\n        public TextView mTextTitle;\n\n        public ItemViewHolder(View view) {\n            super(view);\n            mTextTitle = (TextView) view.findViewById(R.id.title);\n        }\n\n        private void bind(String title) {\n            mTextTitle.setText(title);\n        }\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/PicassoSampleActivity.java",
    "content": "package uk.co.senab.photoview.sample;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\n\nimport com.squareup.picasso.Callback;\nimport com.squareup.picasso.Picasso;\n\nimport uk.co.senab.photoview.PhotoView;\nimport uk.co.senab.photoview.PhotoViewAttacher;\n\npublic class PicassoSampleActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_simple);\n\n        PhotoView photoView = (PhotoView) findViewById(R.id.iv_photo);\n\n        final PhotoViewAttacher attacher = new PhotoViewAttacher(photoView);\n\n        Picasso.with(this)\n                .load(\"http://www.atguigu.com/images/logo.gif\")\n                .into(photoView, new Callback() {\n                    @Override\n                    public void onSuccess() {\n                        attacher.update();\n                    }\n\n                    @Override\n                    public void onError() {\n                    }\n                });\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/RotationSampleActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.sample;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.Menu;\nimport android.view.MenuItem;\n\nimport uk.co.senab.photoview.PhotoView;\n\npublic class RotationSampleActivity extends AppCompatActivity {\n\n    private PhotoView photo;\n    private final Handler handler = new Handler();\n    private boolean rotating = false;\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        photo = new PhotoView(this);\n        photo.setImageResource(R.drawable.wallpaper);\n        setContentView(photo);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        handler.removeCallbacksAndMessages(null);\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        menu.add(Menu.NONE, 0, Menu.NONE, \"Rotate 10° Right\");\n        menu.add(Menu.NONE, 1, Menu.NONE, \"Rotate 10° Left\");\n        menu.add(Menu.NONE, 2, Menu.NONE, \"Toggle automatic rotation\");\n        menu.add(Menu.NONE, 3, Menu.NONE, \"Reset to 0\");\n        menu.add(Menu.NONE, 4, Menu.NONE, \"Reset to 90\");\n        menu.add(Menu.NONE, 5, Menu.NONE, \"Reset to 180\");\n        menu.add(Menu.NONE, 6, Menu.NONE, \"Reset to 270\");\n        return super.onCreateOptionsMenu(menu);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case 0:\n                photo.setRotationBy(10);\n                return true;\n            case 1:\n                photo.setRotationBy(-10);\n                return true;\n            case 2:\n                toggleRotation();\n                return true;\n            case 3:\n                photo.setRotationTo(0);\n                return true;\n            case 4:\n                photo.setRotationTo(90);\n                return true;\n            case 5:\n                photo.setRotationTo(180);\n                return true;\n            case 6:\n                photo.setRotationTo(270);\n                return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n\n    private void toggleRotation() {\n        if (rotating) {\n            handler.removeCallbacksAndMessages(null);\n        } else {\n            rotateLoop();\n        }\n        rotating = !rotating;\n    }\n\n    private void rotateLoop() {\n        handler.postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                photo.setRotationBy(1);\n                rotateLoop();\n            }\n        }, 15);\n    }\n\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/SimpleSampleActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.sample;\n\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.Matrix;\nimport android.graphics.RectF;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.support.v4.content.ContextCompat;\nimport android.util.Log;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.ImageView.ScaleType;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.util.Random;\n\nimport uk.co.senab.photoview.PhotoViewAttacher;\nimport uk.co.senab.photoview.PhotoViewAttacher.OnMatrixChangedListener;\nimport uk.co.senab.photoview.PhotoViewAttacher.OnPhotoTapListener;\n\npublic class SimpleSampleActivity extends AppCompatActivity {\n\n    static final String PHOTO_TAP_TOAST_STRING = \"Photo Tap! X: %.2f %% Y:%.2f %% ID: %d\";\n    static final String SCALE_TOAST_STRING = \"Scaled to: %.2ff\";\n    static final String FLING_LOG_STRING = \"Fling velocityX: %.2f, velocityY: %.2f\";\n\n    private TextView mCurrMatrixTv;\n\n    private PhotoViewAttacher mAttacher;\n\n    private Toast mCurrentToast;\n\n    private Matrix mCurrentDisplayMatrix = null;\n\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        ImageView mImageView = (ImageView) findViewById(R.id.iv_photo);\n        mCurrMatrixTv = (TextView) findViewById(R.id.tv_current_matrix);\n\n        Drawable bitmap = ContextCompat.getDrawable(this, R.drawable.wallpaper);\n        mImageView.setImageDrawable(bitmap);\n\n        // The MAGIC happens here!\n        mAttacher = new PhotoViewAttacher(mImageView);\n\n        // Lets attach some listeners, not required though!\n        mAttacher.setOnMatrixChangeListener(new MatrixChangeListener());\n        mAttacher.setOnPhotoTapListener(new PhotoTapListener());\n        mAttacher.setOnSingleFlingListener(new SingleFlingListener());\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        getMenuInflater().inflate(R.menu.main_menu, menu);\n        return super.onCreateOptionsMenu(menu);\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n\n        // Need to call clean-up\n        mAttacher.cleanup();\n    }\n\n    @Override\n    public boolean onPrepareOptionsMenu(Menu menu) {\n        MenuItem zoomToggle = menu.findItem(R.id.menu_zoom_toggle);\n        assert null != zoomToggle;\n        zoomToggle.setTitle(mAttacher.canZoom() ? R.string.menu_zoom_disable : R.string.menu_zoom_enable);\n\n        return super.onPrepareOptionsMenu(menu);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        switch (item.getItemId()) {\n            case R.id.menu_zoom_toggle:\n                mAttacher.setZoomable(!mAttacher.canZoom());\n                return true;\n\n            case R.id.menu_scale_fit_center:\n                mAttacher.setScaleType(ScaleType.FIT_CENTER);\n                return true;\n\n            case R.id.menu_scale_fit_start:\n                mAttacher.setScaleType(ScaleType.FIT_START);\n                return true;\n\n            case R.id.menu_scale_fit_end:\n                mAttacher.setScaleType(ScaleType.FIT_END);\n                return true;\n\n            case R.id.menu_scale_fit_xy:\n                mAttacher.setScaleType(ScaleType.FIT_XY);\n                return true;\n\n            case R.id.menu_scale_scale_center:\n                mAttacher.setScaleType(ScaleType.CENTER);\n                return true;\n\n            case R.id.menu_scale_scale_center_crop:\n                mAttacher.setScaleType(ScaleType.CENTER_CROP);\n                return true;\n\n            case R.id.menu_scale_scale_center_inside:\n                mAttacher.setScaleType(ScaleType.CENTER_INSIDE);\n                return true;\n\n            case R.id.menu_scale_random_animate:\n            case R.id.menu_scale_random:\n                Random r = new Random();\n\n                float minScale = mAttacher.getMinimumScale();\n                float maxScale = mAttacher.getMaximumScale();\n                float randomScale = minScale + (r.nextFloat() * (maxScale - minScale));\n                mAttacher.setScale(randomScale, item.getItemId() == R.id.menu_scale_random_animate);\n\n                showToast(String.format(SCALE_TOAST_STRING, randomScale));\n\n                return true;\n            case R.id.menu_matrix_restore:\n                if (mCurrentDisplayMatrix == null)\n                    showToast(\"You need to capture display matrix first\");\n                else\n                    mAttacher.setDisplayMatrix(mCurrentDisplayMatrix);\n                return true;\n            case R.id.menu_matrix_capture:\n                mCurrentDisplayMatrix = new Matrix();\n                mAttacher.getDisplayMatrix(mCurrentDisplayMatrix);\n                return true;\n            case R.id.extract_visible_bitmap:\n                try {\n                    Bitmap bmp = mAttacher.getVisibleRectangleBitmap();\n                    File tmpFile = File.createTempFile(\"photoview\", \".png\",\n                            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));\n                    FileOutputStream out = new FileOutputStream(tmpFile);\n                    bmp.compress(Bitmap.CompressFormat.PNG, 90, out);\n                    out.close();\n                    Intent share = new Intent(Intent.ACTION_SEND);\n                    share.setType(\"image/png\");\n                    share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tmpFile));\n                    startActivity(share);\n                    Toast.makeText(this, String.format(\"Extracted into: %s\", tmpFile.getAbsolutePath()), Toast.LENGTH_SHORT).show();\n                } catch (Throwable t) {\n                    t.printStackTrace();\n                    Toast.makeText(this, \"Error occured while extracting bitmap\", Toast.LENGTH_SHORT).show();\n                }\n                return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n\n    private class PhotoTapListener implements OnPhotoTapListener {\n\n        @Override\n        public void onPhotoTap(View view, float x, float y) {\n            float xPercentage = x * 100f;\n            float yPercentage = y * 100f;\n\n            showToast(String.format(PHOTO_TAP_TOAST_STRING, xPercentage, yPercentage, view == null ? 0 : view.getId()));\n        }\n\n        @Override\n        public void onOutsidePhotoTap() {\n            showToast(\"You have a tap event on the place where out of the photo.\");\n        }\n    }\n\n    private void showToast(CharSequence text) {\n        if (null != mCurrentToast) {\n            mCurrentToast.cancel();\n        }\n\n        mCurrentToast = Toast.makeText(SimpleSampleActivity.this, text, Toast.LENGTH_SHORT);\n        mCurrentToast.show();\n    }\n\n    private class MatrixChangeListener implements OnMatrixChangedListener {\n\n        @Override\n        public void onMatrixChanged(RectF rect) {\n            mCurrMatrixTv.setText(rect.toString());\n        }\n    }\n\n    private class SingleFlingListener implements PhotoViewAttacher.OnSingleFlingListener {\n\n        @Override\n        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {\n            if (BuildConfig.DEBUG) {\n                Log.d(\"PhotoView\", String.format(FLING_LOG_STRING, velocityX, velocityY));\n            }\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/java/uk/co/senab/photoview/sample/ViewPagerActivity.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.sample;\n\nimport android.os.Bundle;\nimport android.support.v4.view.PagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewGroup.LayoutParams;\n\nimport uk.co.senab.photoview.PhotoView;\n\npublic class ViewPagerActivity extends AppCompatActivity {\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_view_pager);\n\t\tViewPager mViewPager = (HackyViewPager) findViewById(R.id.view_pager);\n\t\tsetContentView(mViewPager);\n\n\t\tmViewPager.setAdapter(new SamplePagerAdapter());\n\t}\n\n\tstatic class SamplePagerAdapter extends PagerAdapter {\n\n\t\tprivate static final int[] sDrawables = { R.drawable.wallpaper, R.drawable.wallpaper, R.drawable.wallpaper,\n\t\t\t\tR.drawable.wallpaper, R.drawable.wallpaper, R.drawable.wallpaper };\n\n\t\t@Override\n\t\tpublic int getCount() {\n\t\t\treturn sDrawables.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic View instantiateItem(ViewGroup container, int position) {\n\t\t\tPhotoView photoView = new PhotoView(container.getContext());\n\t\t\tphotoView.setImageResource(sDrawables[position]);\n\n\t\t\t// Now just add PhotoView to ViewPager and return it\n\t\t\tcontainer.addView(photoView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);\n\n\t\t\treturn photoView;\n\t\t}\n\n\t\t@Override\n\t\tpublic void destroyItem(ViewGroup container, int position, Object object) {\n\t\t\tcontainer.removeView((View) object);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean isViewFromObject(View view, Object object) {\n\t\t\treturn view == object;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/activity_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.v7.widget.RecyclerView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n</android.support.v7.widget.RecyclerView>"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/activity_main.xml",
    "content": "<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" >\n\n        <uk.co.senab.photoview.PhotoView\n            android:id=\"@+id/iv_photo\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"fill_parent\" />\n\n        <TextView\n            android:id=\"@+id/tv_current_matrix\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"bottom|center_horizontal\"\n            android:background=\"#60000000\"\n            android:gravity=\"center\"\n            android:textColor=\"@android:color/white\" />\n    </FrameLayout>\n\n</merge>"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/activity_simple.xml",
    "content": "<uk.co.senab.photoview.PhotoView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/iv_photo\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/activity_transition.xml",
    "content": "<android.support.v7.widget.RecyclerView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/list\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/activity_transition_to.xml",
    "content": "<uk.co.senab.photoview.PhotoView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/iv_photo\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center\"\n    android:transitionName=\"@string/transition_test\"\n    android:src=\"@drawable/wallpaper\"/>"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/activity_view_pager.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<uk.co.senab.photoview.sample.HackyViewPager\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/view_pager\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/item_image.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ImageView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:scaleType=\"centerCrop\"\n    android:src=\"@drawable/wallpaper\"/>"
  },
  {
    "path": "PhotoView-master/src/main/res/layout/item_list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:minHeight=\"?attr/listPreferredItemHeight\"\n    android:background=\"?attr/selectableItemBackground\">\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"@android:color/white\"/>\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:textSize=\"20sp\"\n        android:layout_margin=\"16dp\"\n        tools:text=\"Hello there\"/>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"@android:color/white\"/>\n\n</FrameLayout>"
  },
  {
    "path": "PhotoView-master/src/main/res/menu/main_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      xmlns:tools=\"http://schemas.android.com/tools\"\n      tools:ignore=\"UnusedAttribute\">\n\n    <item\n        android:id=\"@+id/menu_zoom_toggle\"\n        android:title=\"@string/menu_zoom_disable\"/>\n    <item\n        android:id=\"@+id/menu_scale_fit_center\"\n        android:title=\"@string/menu_scale_fit_center\"/>\n    <item\n        android:id=\"@+id/menu_scale_fit_start\"\n        android:title=\"@string/menu_scale_fit_start\"/>\n    <item\n        android:id=\"@+id/menu_scale_fit_end\"\n        android:title=\"@string/menu_scale_fit_end\"/>\n    <item\n        android:id=\"@+id/menu_scale_fit_xy\"\n        android:title=\"@string/menu_scale_fit_xy\"/>\n    <item\n        android:id=\"@+id/menu_scale_scale_center\"\n        android:title=\"@string/menu_scale_center\"/>\n    <item\n        android:id=\"@+id/menu_scale_scale_center_crop\"\n        android:title=\"@string/menu_scale_center_crop\"/>\n    <item\n        android:id=\"@+id/menu_scale_scale_center_inside\"\n        android:title=\"@string/menu_scale_center_inside\"/>\n    <item\n        android:id=\"@+id/menu_scale_random_animate\"\n        android:title=\"@string/menu_zoom_random_animate\"/>\n    <item\n        android:id=\"@+id/menu_scale_random\"\n        android:title=\"@string/menu_zoom_random\"/>\n    <item\n        android:id=\"@+id/menu_matrix_restore\"\n        android:title=\"@string/menu_matrix_restore\"/>\n    <item\n        android:id=\"@+id/menu_matrix_capture\"\n        android:title=\"@string/menu_matrix_capture\"/>\n    <item\n        android:id=\"@+id/extract_visible_bitmap\"\n        android:title=\"@string/extract_visible_bitmap\"/>\n\n</menu>"
  },
  {
    "path": "PhotoView-master/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"green\">#AACE30</color>\n    <color name=\"blue\">#142D3E</color>\n    <color name=\"blue_dark\">#001425</color>\n</resources>"
  },
  {
    "path": "PhotoView-master/src/main/res/values/strings.xml",
    "content": "<resources>\n\n    <string name=\"app_name\">PhotoView Sample</string>\n    <string name=\"menu_zoom_enable\">Enable Zoom</string>\n    <string name=\"menu_zoom_disable\">Disable Zoom</string>\n    <string name=\"menu_scale_fit_center\">Change to FIT_CENTER</string>\n    <string name=\"menu_scale_fit_start\">Change to FIT_START</string>\n    <string name=\"menu_scale_fit_end\">Change to FIT_END</string>\n    <string name=\"menu_scale_fit_xy\">Change to FIT_XY</string>\n    <string name=\"menu_scale_center\">Change to CENTER</string>\n    <string name=\"menu_scale_center_inside\">Change to CENTER_INSIDE</string>\n    <string name=\"menu_scale_center_crop\">Change to CENTER_CROP</string>\n    <string name=\"menu_zoom_random_animate\">Animate scale to random value</string>\n    <string name=\"menu_zoom_random\">Set scale to random value</string>\n    <string name=\"menu_matrix_restore\">Restore Display Matrix</string>\n    <string name=\"menu_matrix_capture\">Capture Display Matrix</string>\n    <string name=\"extract_visible_bitmap\">Extract visible bitmap</string>\n\n</resources>\n"
  },
  {
    "path": "PhotoView-master/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/blue</item>\n        <item name=\"colorPrimaryDark\">@color/blue_dark</item>\n        <item name=\"colorAccent\">@color/green</item>\n\n        <!-- enable window content transitions -->\n        <item name=\"android:windowActivityTransitions\" tools:targetApi=\"lollipop\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "PhotoView-master/src/main/res/values/transitions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"transition_test\">test</string>\n</resources>"
  },
  {
    "path": "PhotoView_library/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "PhotoView_library/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion '24.0.3'\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 1\n        versionName \"1.0\"\n    }\n}\n\ndependencies {\n    compile \"com.android.support:support-core-utils:24.2.1\"\n}\n\n//apply from: 'https://raw.githubusercontent.com/Commit451/gradle-android-javadocs/1.0.0/gradle-android-javadocs.gradle'\n"
  },
  {
    "path": "PhotoView_library/gradle.properties",
    "content": "POM_NAME=PhotoView Library\nPOM_ARTIFACT_ID=library\nPOM_PACKAGING=jar\n"
  },
  {
    "path": "PhotoView_library/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"uk.co.senab.photoview\" >\n\n</manifest>\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/Compat.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport android.os.Build.VERSION;\nimport android.os.Build.VERSION_CODES;\nimport android.view.MotionEvent;\nimport android.view.View;\n\npublic class Compat {\n\n    private static final int SIXTY_FPS_INTERVAL = 1000 / 60;\n\n    public static void postOnAnimation(View view, Runnable runnable) {\n        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {\n            postOnAnimationJellyBean(view, runnable);\n        } else {\n            view.postDelayed(runnable, SIXTY_FPS_INTERVAL);\n        }\n    }\n\n    @TargetApi(16)\n    private static void postOnAnimationJellyBean(View view, Runnable runnable) {\n        view.postOnAnimation(runnable);\n    }\n\n    public static int getPointerIndex(int action) {\n        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)\n            return getPointerIndexHoneyComb(action);\n        else\n            return getPointerIndexEclair(action);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @TargetApi(Build.VERSION_CODES.ECLAIR)\n    private static int getPointerIndexEclair(int action) {\n        return (action & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    private static int getPointerIndexHoneyComb(int action) {\n        return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;\n    }\n\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/DefaultOnDoubleTapListener.java",
    "content": "package uk.co.senab.photoview;\n\nimport android.graphics.RectF;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.widget.ImageView;\n\n/**\n * Provided default implementation of GestureDetector.OnDoubleTapListener, to be overridden with custom behavior, if needed\n * <p>&nbsp;</p>\n * To be used via {@link uk.co.senab.photoview.PhotoViewAttacher#setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener)}\n */\npublic class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {\n\n    private PhotoViewAttacher photoViewAttacher;\n\n    /**\n     * Default constructor\n     *\n     * @param photoViewAttacher PhotoViewAttacher to bind to\n     */\n    public DefaultOnDoubleTapListener(PhotoViewAttacher photoViewAttacher) {\n        setPhotoViewAttacher(photoViewAttacher);\n    }\n\n    /**\n     * Allows to change PhotoViewAttacher within range of single instance\n     *\n     * @param newPhotoViewAttacher PhotoViewAttacher to bind to\n     */\n    public void setPhotoViewAttacher(PhotoViewAttacher newPhotoViewAttacher) {\n        this.photoViewAttacher = newPhotoViewAttacher;\n    }\n\n    @Override\n    public boolean onSingleTapConfirmed(MotionEvent e) {\n        if (this.photoViewAttacher == null)\n            return false;\n\n        ImageView imageView = photoViewAttacher.getImageView();\n\n        if (null != photoViewAttacher.getOnPhotoTapListener()) {\n            final RectF displayRect = photoViewAttacher.getDisplayRect();\n\n            if (null != displayRect) {\n                final float x = e.getX(), y = e.getY();\n\n                // Check to see if the user tapped on the photo\n                if (displayRect.contains(x, y)) {\n\n                    float xResult = (x - displayRect.left)\n                            / displayRect.width();\n                    float yResult = (y - displayRect.top)\n                            / displayRect.height();\n\n                    photoViewAttacher.getOnPhotoTapListener().onPhotoTap(imageView, xResult, yResult);\n                    return true;\n                }else{\n                    photoViewAttacher.getOnPhotoTapListener().onOutsidePhotoTap();\n                }\n            }\n        }\n        if (null != photoViewAttacher.getOnViewTapListener()) {\n            photoViewAttacher.getOnViewTapListener().onViewTap(imageView, e.getX(), e.getY());\n        }\n\n        return false;\n    }\n\n    @Override\n    public boolean onDoubleTap(MotionEvent ev) {\n        if (photoViewAttacher == null)\n            return false;\n\n        try {\n            float scale = photoViewAttacher.getScale();\n            float x = ev.getX();\n            float y = ev.getY();\n\n            if (scale < photoViewAttacher.getMediumScale()) {\n                photoViewAttacher.setScale(photoViewAttacher.getMediumScale(), x, y, true);\n            } else if (scale >= photoViewAttacher.getMediumScale() && scale < photoViewAttacher.getMaximumScale()) {\n                photoViewAttacher.setScale(photoViewAttacher.getMaximumScale(), x, y, true);\n            } else {\n                photoViewAttacher.setScale(photoViewAttacher.getMinimumScale(), x, y, true);\n            }\n        } catch (ArrayIndexOutOfBoundsException e) {\n            // Can sometimes happen when getX() and getY() is called\n        }\n\n        return true;\n    }\n\n    @Override\n    public boolean onDoubleTapEvent(MotionEvent e) {\n        // Wait for the confirmed onDoubleTap() instead\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/IPhotoView.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Matrix;\nimport android.graphics.RectF;\nimport android.view.GestureDetector;\nimport android.view.View;\nimport android.widget.ImageView;\n\n\npublic interface IPhotoView {\n\n    float DEFAULT_MAX_SCALE = 3.0f;\n    float DEFAULT_MID_SCALE = 1.75f;\n    float DEFAULT_MIN_SCALE = 1.0f;\n    int DEFAULT_ZOOM_DURATION = 200;\n\n    /**\n     * Returns true if the PhotoView is set to allow zooming of Photos.\n     *\n     * @return true if the PhotoView allows zooming.\n     */\n    boolean canZoom();\n\n    /**\n     * Gets the Display Rectangle of the currently displayed Drawable. The Rectangle is relative to\n     * this View and includes all scaling and translations.\n     *\n     * @return - RectF of Displayed Drawable\n     */\n    RectF getDisplayRect();\n\n    /**\n     * Sets the Display Matrix of the currently displayed Drawable. The Rectangle is considered\n     * relative to this View and includes all scaling and translations.\n     *\n     * @param finalMatrix target matrix to set PhotoView to\n     * @return - true if rectangle was applied successfully\n     */\n    boolean setDisplayMatrix(Matrix finalMatrix);\n\n    /**\n     * Copies the Display Matrix of the currently displayed Drawable. The Rectangle is considered\n     * relative to this View and includes all scaling and translations.\n     *\n     * @param matrix target matrix to copy to\n     */\n    void getDisplayMatrix(Matrix matrix);\n\n    /**\n     * @return The current minimum scale level. What this value represents depends on the current\n     * {@link android.widget.ImageView.ScaleType}.\n     */\n    float getMinimumScale();\n\n    /**\n     * @return The current medium scale level. What this value represents depends on the current\n     * {@link android.widget.ImageView.ScaleType}.\n     */\n    float getMediumScale();\n\n    /**\n     * @return The current maximum scale level. What this value represents depends on the current\n     * {@link android.widget.ImageView.ScaleType}.\n     */\n    float getMaximumScale();\n\n    /**\n     * Returns the current scale value\n     *\n     * @return float - current scale value\n     */\n    float getScale();\n\n    /**\n     * Return the current scale type in use by the ImageView.\n     *\n     * @return current ImageView.ScaleType\n     */\n    ImageView.ScaleType getScaleType();\n\n    /**\n     * Whether to allow the ImageView's parent to intercept the touch event when the photo is scroll\n     * to it's horizontal edge.\n     *\n     * @param allow whether to allow intercepting by parent element or not\n     */\n    void setAllowParentInterceptOnEdge(boolean allow);\n\n    /**\n     * Sets the minimum scale level. What this value represents depends on the current {@link\n     * android.widget.ImageView.ScaleType}.\n     *\n     * @param minimumScale minimum allowed scale\n     */\n    void setMinimumScale(float minimumScale);\n\n    /**\n     * Sets the medium scale level. What this value represents depends on the current {@link android.widget.ImageView.ScaleType}.\n     *\n     * @param mediumScale medium scale preset\n     */\n    void setMediumScale(float mediumScale);\n\n    /**\n     * Sets the maximum scale level. What this value represents depends on the current {@link\n     * android.widget.ImageView.ScaleType}.\n     *\n     * @param maximumScale maximum allowed scale preset\n     */\n    void setMaximumScale(float maximumScale);\n\n    /**\n     * Allows to set all three scale levels at once, so you don't run into problem with setting\n     * medium/minimum scale before the maximum one\n     *\n     * @param minimumScale minimum allowed scale\n     * @param mediumScale  medium allowed scale\n     * @param maximumScale maximum allowed scale preset\n     */\n    void setScaleLevels(float minimumScale, float mediumScale, float maximumScale);\n\n    /**\n     * Register a callback to be invoked when the Photo displayed by this view is long-pressed.\n     *\n     * @param listener - Listener to be registered.\n     */\n    void setOnLongClickListener(View.OnLongClickListener listener);\n\n    /**\n     * Register a callback to be invoked when the Matrix has changed for this View. An example would\n     * be the user panning or scaling the Photo.\n     *\n     * @param listener - Listener to be registered.\n     */\n    void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener listener);\n\n    /**\n     * Register a callback to be invoked when the Photo displayed by this View is tapped with a\n     * single tap.\n     *\n     * @param listener - Listener to be registered.\n     */\n    void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener listener);\n\n    /**\n     * Register a callback to be invoked when the View is tapped with a single tap.\n     *\n     * @param listener - Listener to be registered.\n     */\n    void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener listener);\n\n    /**\n     * Enables rotation via PhotoView internal functions.\n     *\n     * @param rotationDegree - Degree to rotate PhotoView to, should be in range 0 to 360\n     */\n    void setRotationTo(float rotationDegree);\n\n    /**\n     * Enables rotation via PhotoView internal functions.\n     *\n     * @param rotationDegree - Degree to rotate PhotoView by, should be in range 0 to 360\n     */\n    void setRotationBy(float rotationDegree);\n\n    /**\n     * Changes the current scale to the specified value.\n     *\n     * @param scale - Value to scale to\n     */\n    void setScale(float scale);\n\n    /**\n     * Changes the current scale to the specified value.\n     *\n     * @param scale   - Value to scale to\n     * @param animate - Whether to animate the scale\n     */\n    void setScale(float scale, boolean animate);\n\n    /**\n     * Changes the current scale to the specified value, around the given focal point.\n     *\n     * @param scale   - Value to scale to\n     * @param focalX  - X Focus Point\n     * @param focalY  - Y Focus Point\n     * @param animate - Whether to animate the scale\n     */\n    void setScale(float scale, float focalX, float focalY, boolean animate);\n\n    /**\n     * Controls how the image should be resized or moved to match the size of the ImageView. Any\n     * scaling or panning will happen within the confines of this {@link\n     * android.widget.ImageView.ScaleType}.\n     *\n     * @param scaleType - The desired scaling mode.\n     */\n    void setScaleType(ImageView.ScaleType scaleType);\n\n    /**\n     * Allows you to enable/disable the zoom functionality on the ImageView. When disable the\n     * ImageView reverts to using the FIT_CENTER matrix.\n     *\n     * @param zoomable - Whether the zoom functionality is enabled.\n     */\n    void setZoomable(boolean zoomable);\n\n    /**\n     * Extracts currently visible area to Bitmap object, if there is no image loaded yet or the\n     * ImageView is already destroyed, returns {@code null}\n     *\n     * @return currently visible area as bitmap or null\n     */\n    Bitmap getVisibleRectangleBitmap();\n\n    /**\n     * Allows to change zoom transition speed, default value is 200 (PhotoViewAttacher.DEFAULT_ZOOM_DURATION).\n     * Will default to 200 if provided negative value\n     *\n     * @param milliseconds duration of zoom interpolation\n     */\n    void setZoomTransitionDuration(int milliseconds);\n\n    /**\n     * Will return instance of IPhotoView (eg. PhotoViewAttacher), can be used to provide better\n     * integration\n     *\n     * @return IPhotoView implementation instance if available, null if not\n     */\n    IPhotoView getIPhotoViewImplementation();\n\n    /**\n     * Sets custom double tap listener, to intercept default given functions. To reset behavior to\n     * default, you can just pass in \"null\" or public field of PhotoViewAttacher.defaultOnDoubleTapListener\n     *\n     * @param newOnDoubleTapListener custom OnDoubleTapListener to be set on ImageView\n     */\n    void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener);\n\n    /**\n     * Will report back about scale changes\n     *\n     * @param onScaleChangeListener OnScaleChangeListener instance\n     */\n    void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener);\n\n    /**\n     * Will report back about fling(single touch)\n     *\n     * @param onSingleFlingListener OnSingleFlingListener instance\n     */\n    void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener);\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/PhotoView.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Matrix;\nimport android.graphics.RectF;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.util.AttributeSet;\nimport android.view.GestureDetector;\nimport android.widget.ImageView;\n\nimport uk.co.senab.photoview.PhotoViewAttacher.OnMatrixChangedListener;\nimport uk.co.senab.photoview.PhotoViewAttacher.OnPhotoTapListener;\nimport uk.co.senab.photoview.PhotoViewAttacher.OnViewTapListener;\n\npublic class PhotoView extends ImageView implements IPhotoView {\n\n    private PhotoViewAttacher mAttacher;\n\n    private ScaleType mPendingScaleType;\n\n    public PhotoView(Context context) {\n        this(context, null);\n    }\n\n    public PhotoView(Context context, AttributeSet attr) {\n        this(context, attr, 0);\n    }\n\n    public PhotoView(Context context, AttributeSet attr, int defStyle) {\n        super(context, attr, defStyle);\n        super.setScaleType(ScaleType.MATRIX);\n        init();\n    }\n\n    protected void init() {\n        if (null == mAttacher || null == mAttacher.getImageView()) {\n            mAttacher = new PhotoViewAttacher(this);\n        }\n\n        if (null != mPendingScaleType) {\n            setScaleType(mPendingScaleType);\n            mPendingScaleType = null;\n        }\n    }\n\n    @Override\n    public void setRotationTo(float rotationDegree) {\n        mAttacher.setRotationTo(rotationDegree);\n    }\n\n    @Override\n    public void setRotationBy(float rotationDegree) {\n        mAttacher.setRotationBy(rotationDegree);\n    }\n\n    @Override\n    public boolean canZoom() {\n        return mAttacher.canZoom();\n    }\n\n    @Override\n    public RectF getDisplayRect() {\n        return mAttacher.getDisplayRect();\n    }\n\n    @Override\n    public void getDisplayMatrix(Matrix matrix) {\n        mAttacher.getDisplayMatrix(matrix);\n    }\n\n    @Override\n    public boolean setDisplayMatrix(Matrix finalRectangle) {\n        return mAttacher.setDisplayMatrix(finalRectangle);\n    }\n\n    @Override\n    public float getMinimumScale() {\n        return mAttacher.getMinimumScale();\n    }\n\n    @Override\n    public float getMediumScale() {\n        return mAttacher.getMediumScale();\n    }\n\n    @Override\n    public float getMaximumScale() {\n        return mAttacher.getMaximumScale();\n    }\n\n    @Override\n    public float getScale() {\n        return mAttacher.getScale();\n    }\n\n    @Override\n    public ScaleType getScaleType() {\n        return mAttacher.getScaleType();\n    }\n\n    @Override\n    public Matrix getImageMatrix() {\n        return mAttacher.getImageMatrix();\n    }\n\n    @Override\n    public void setAllowParentInterceptOnEdge(boolean allow) {\n        mAttacher.setAllowParentInterceptOnEdge(allow);\n    }\n\n    @Override\n    public void setMinimumScale(float minimumScale) {\n        mAttacher.setMinimumScale(minimumScale);\n    }\n\n    @Override\n    public void setMediumScale(float mediumScale) {\n        mAttacher.setMediumScale(mediumScale);\n    }\n\n    @Override\n    public void setMaximumScale(float maximumScale) {\n        mAttacher.setMaximumScale(maximumScale);\n    }\n\n    @Override\n    public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {\n        mAttacher.setScaleLevels(minimumScale, mediumScale, maximumScale);\n    }\n\n    @Override\n    // setImageBitmap calls through to this method\n    public void setImageDrawable(Drawable drawable) {\n        super.setImageDrawable(drawable);\n        if (null != mAttacher) {\n            mAttacher.update();\n        }\n    }\n\n    @Override\n    public void setImageResource(int resId) {\n        super.setImageResource(resId);\n        if (null != mAttacher) {\n            mAttacher.update();\n        }\n    }\n\n    @Override\n    public void setImageURI(Uri uri) {\n        super.setImageURI(uri);\n        if (null != mAttacher) {\n            mAttacher.update();\n        }\n    }\n\n    @Override\n    protected boolean setFrame(int l, int t, int r, int b) {\n        boolean changed = super.setFrame(l, t, r, b);\n        if (null != mAttacher) {\n            mAttacher.update();\n        }\n        return changed;\n    }\n\n    @Override\n    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {\n        mAttacher.setOnMatrixChangeListener(listener);\n    }\n\n    @Override\n    public void setOnLongClickListener(OnLongClickListener l) {\n        mAttacher.setOnLongClickListener(l);\n    }\n\n    @Override\n    public void setOnPhotoTapListener(OnPhotoTapListener listener) {\n        mAttacher.setOnPhotoTapListener(listener);\n    }\n\n    @Override\n    public void setOnViewTapListener(OnViewTapListener listener) {\n        mAttacher.setOnViewTapListener(listener);\n    }\n\n    @Override\n    public void setScale(float scale) {\n        mAttacher.setScale(scale);\n    }\n\n    @Override\n    public void setScale(float scale, boolean animate) {\n        mAttacher.setScale(scale, animate);\n    }\n\n    @Override\n    public void setScale(float scale, float focalX, float focalY, boolean animate) {\n        mAttacher.setScale(scale, focalX, focalY, animate);\n    }\n\n    @Override\n    public void setScaleType(ScaleType scaleType) {\n        if (null != mAttacher) {\n            mAttacher.setScaleType(scaleType);\n        } else {\n            mPendingScaleType = scaleType;\n        }\n    }\n\n    @Override\n    public void setZoomable(boolean zoomable) {\n        mAttacher.setZoomable(zoomable);\n    }\n\n    @Override\n    public Bitmap getVisibleRectangleBitmap() {\n        return mAttacher.getVisibleRectangleBitmap();\n    }\n\n    @Override\n    public void setZoomTransitionDuration(int milliseconds) {\n        mAttacher.setZoomTransitionDuration(milliseconds);\n    }\n\n    @Override\n    public IPhotoView getIPhotoViewImplementation() {\n        return mAttacher;\n    }\n\n    @Override\n    public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {\n        mAttacher.setOnDoubleTapListener(newOnDoubleTapListener);\n    }\n\n    @Override\n    public void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener) {\n        mAttacher.setOnScaleChangeListener(onScaleChangeListener);\n    }\n\n    @Override\n    public void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener) {\n        mAttacher.setOnSingleFlingListener(onSingleFlingListener);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        mAttacher.cleanup();\n        mAttacher = null;\n        super.onDetachedFromWindow();\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        init();\n        super.onAttachedToWindow();\n    }\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/PhotoViewAttacher.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Matrix;\nimport android.graphics.Matrix.ScaleToFit;\nimport android.graphics.RectF;\nimport android.graphics.drawable.Drawable;\nimport android.support.annotation.Nullable;\nimport android.support.v4.view.MotionEventCompat;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.View.OnLongClickListener;\nimport android.view.ViewParent;\nimport android.view.ViewTreeObserver;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.Interpolator;\nimport android.widget.ImageView;\nimport android.widget.ImageView.ScaleType;\n\nimport java.lang.ref.WeakReference;\n\nimport uk.co.senab.photoview.gestures.OnGestureListener;\nimport uk.co.senab.photoview.gestures.VersionedGestureDetector;\nimport uk.co.senab.photoview.log.LogManager;\nimport uk.co.senab.photoview.scrollerproxy.ScrollerProxy;\n\nimport static android.view.MotionEvent.ACTION_CANCEL;\nimport static android.view.MotionEvent.ACTION_DOWN;\nimport static android.view.MotionEvent.ACTION_UP;\n\npublic class PhotoViewAttacher implements IPhotoView, View.OnTouchListener,\n        OnGestureListener,\n        ViewTreeObserver.OnGlobalLayoutListener {\n\n    private static final String LOG_TAG = \"PhotoViewAttacher\";\n\n    // let debug flag be dynamic, but still Proguard can be used to remove from\n    // release builds\n    private static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG);\n\n    private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();\n    int ZOOM_DURATION = DEFAULT_ZOOM_DURATION;\n\n    static final int EDGE_NONE = -1;\n    static final int EDGE_LEFT = 0;\n    static final int EDGE_RIGHT = 1;\n    static final int EDGE_BOTH = 2;\n\n    static int SINGLE_TOUCH = 1;\n\n    private float mMinScale = DEFAULT_MIN_SCALE;\n    private float mMidScale = DEFAULT_MID_SCALE;\n    private float mMaxScale = DEFAULT_MAX_SCALE;\n\n    private boolean mAllowParentInterceptOnEdge = true;\n    private boolean mBlockParentIntercept = false;\n\n    private static void checkZoomLevels(float minZoom, float midZoom,\n                                        float maxZoom) {\n        if (minZoom >= midZoom) {\n            throw new IllegalArgumentException(\n                    \"Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value\");\n        } else if (midZoom >= maxZoom) {\n            throw new IllegalArgumentException(\n                    \"Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value\");\n        }\n    }\n\n    /**\n     * @return true if the ImageView exists, and its Drawable exists\n     */\n    private static boolean hasDrawable(ImageView imageView) {\n        return null != imageView && null != imageView.getDrawable();\n    }\n\n    /**\n     * @return true if the ScaleType is supported.\n     */\n    private static boolean isSupportedScaleType(final ScaleType scaleType) {\n        if (null == scaleType) {\n            return false;\n        }\n\n        switch (scaleType) {\n            case MATRIX:\n                throw new IllegalArgumentException(scaleType.name()\n                        + \" is not supported in PhotoView\");\n\n            default:\n                return true;\n        }\n    }\n\n    /**\n     * Sets the ImageView's ScaleType to Matrix.\n     */\n    private static void setImageViewScaleTypeMatrix(ImageView imageView) {\n        /**\n         * PhotoView sets its own ScaleType to Matrix, then diverts all calls\n         * setScaleType to this.setScaleType automatically.\n         */\n        if (null != imageView && !(imageView instanceof IPhotoView)) {\n            if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {\n                imageView.setScaleType(ScaleType.MATRIX);\n            }\n        }\n    }\n\n    private WeakReference<ImageView> mImageView;\n\n    // Gesture Detectors\n    private GestureDetector mGestureDetector;\n    private uk.co.senab.photoview.gestures.GestureDetector mScaleDragDetector;\n\n    // These are set so we don't keep allocating them on the heap\n    private final Matrix mBaseMatrix = new Matrix();\n    private final Matrix mDrawMatrix = new Matrix();\n    private final Matrix mSuppMatrix = new Matrix();\n    private final RectF mDisplayRect = new RectF();\n    private final float[] mMatrixValues = new float[9];\n\n    // Listeners\n    private OnMatrixChangedListener mMatrixChangeListener;\n    private OnPhotoTapListener mPhotoTapListener;\n    private OnViewTapListener mViewTapListener;\n    private OnLongClickListener mLongClickListener;\n    private OnScaleChangeListener mScaleChangeListener;\n    private OnSingleFlingListener mSingleFlingListener;\n\n    private int mIvTop, mIvRight, mIvBottom, mIvLeft;\n    private FlingRunnable mCurrentFlingRunnable;\n    private int mScrollEdge = EDGE_BOTH;\n    private float mBaseRotation;\n\n    private boolean mZoomEnabled;\n    private ScaleType mScaleType = ScaleType.FIT_CENTER;\n\n    public PhotoViewAttacher(ImageView imageView) {\n        this(imageView, true);\n    }\n\n    public PhotoViewAttacher(ImageView imageView, boolean zoomable) {\n        mImageView = new WeakReference<>(imageView);\n\n        imageView.setDrawingCacheEnabled(true);\n        imageView.setOnTouchListener(this);\n\n        ViewTreeObserver observer = imageView.getViewTreeObserver();\n        if (null != observer)\n            observer.addOnGlobalLayoutListener(this);\n\n        // Make sure we using MATRIX Scale Type\n        setImageViewScaleTypeMatrix(imageView);\n\n        if (imageView.isInEditMode()) {\n            return;\n        }\n        // Create Gesture Detectors...\n        mScaleDragDetector = VersionedGestureDetector.newInstance(\n                imageView.getContext(), this);\n\n        mGestureDetector = new GestureDetector(imageView.getContext(),\n                new GestureDetector.SimpleOnGestureListener() {\n\n                    // forward long click listener\n                    @Override\n                    public void onLongPress(MotionEvent e) {\n                        if (null != mLongClickListener) {\n                            mLongClickListener.onLongClick(getImageView());\n                        }\n                    }\n\n                    @Override\n                    public boolean onFling(MotionEvent e1, MotionEvent e2,\n                                           float velocityX, float velocityY) {\n                        if (mSingleFlingListener != null) {\n                            if (getScale() > DEFAULT_MIN_SCALE) {\n                                return false;\n                            }\n\n                            if (MotionEventCompat.getPointerCount(e1) > SINGLE_TOUCH\n                                    || MotionEventCompat.getPointerCount(e2) > SINGLE_TOUCH) {\n                                return false;\n                            }\n\n                            return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY);\n                        }\n                        return false;\n                    }\n                });\n\n        mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));\n        mBaseRotation = 0.0f;\n\n        // Finally, update the UI so that we're zoomable\n        setZoomable(zoomable);\n    }\n\n    @Override\n    public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {\n        if (newOnDoubleTapListener != null) {\n            this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);\n        } else {\n            this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));\n        }\n    }\n\n    @Override\n    public void setOnScaleChangeListener(OnScaleChangeListener onScaleChangeListener) {\n        this.mScaleChangeListener = onScaleChangeListener;\n    }\n\n    @Override\n    public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {\n        this.mSingleFlingListener = onSingleFlingListener;\n    }\n\n    @Override\n    public boolean canZoom() {\n        return mZoomEnabled;\n    }\n\n    /**\n     * Clean-up the resources attached to this object. This needs to be called when the ImageView is\n     * no longer used. A good example is from {@link android.view.View#onDetachedFromWindow()} or\n     * from {@link android.app.Activity#onDestroy()}. This is automatically called if you are using\n     * {@link uk.co.senab.photoview.PhotoView}.\n     */\n    @SuppressWarnings(\"deprecation\")\n    public void cleanup() {\n        if (null == mImageView) {\n            return; // cleanup already done\n        }\n\n        final ImageView imageView = mImageView.get();\n\n        if (null != imageView) {\n            // Remove this as a global layout listener\n            ViewTreeObserver observer = imageView.getViewTreeObserver();\n            if (null != observer && observer.isAlive()) {\n                observer.removeGlobalOnLayoutListener(this);\n            }\n\n            // Remove the ImageView's reference to this\n            imageView.setOnTouchListener(null);\n\n            // make sure a pending fling runnable won't be run\n            cancelFling();\n        }\n\n        if (null != mGestureDetector) {\n            mGestureDetector.setOnDoubleTapListener(null);\n        }\n\n        // Clear listeners too\n        mMatrixChangeListener = null;\n        mPhotoTapListener = null;\n        mViewTapListener = null;\n\n        // Finally, clear ImageView\n        mImageView = null;\n    }\n\n    @Override\n    public RectF getDisplayRect() {\n        checkMatrixBounds();\n        return getDisplayRect(getDrawMatrix());\n    }\n\n    @Override\n    public boolean setDisplayMatrix(Matrix finalMatrix) {\n        if (finalMatrix == null) {\n            throw new IllegalArgumentException(\"Matrix cannot be null\");\n        }\n\n        ImageView imageView = getImageView();\n        if (null == imageView) {\n            return false;\n        }\n\n        if (null == imageView.getDrawable()) {\n            return false;\n        }\n\n        mSuppMatrix.set(finalMatrix);\n        setImageViewMatrix(getDrawMatrix());\n        checkMatrixBounds();\n\n        return true;\n    }\n\n    public void setBaseRotation(final float degrees) {\n        mBaseRotation = degrees % 360;\n        update();\n        setRotationBy(mBaseRotation);\n        checkAndDisplayMatrix();\n    }\n\n    @Override\n    public void setRotationTo(float degrees) {\n        mSuppMatrix.setRotate(degrees % 360);\n        checkAndDisplayMatrix();\n    }\n\n    @Override\n    public void setRotationBy(float degrees) {\n        mSuppMatrix.postRotate(degrees % 360);\n        checkAndDisplayMatrix();\n    }\n\n    public ImageView getImageView() {\n        ImageView imageView = null;\n\n        if (null != mImageView) {\n            imageView = mImageView.get();\n        }\n\n        // If we don't have an ImageView, call cleanup()\n        if (null == imageView) {\n            cleanup();\n            LogManager.getLogger().i(LOG_TAG,\n                    \"ImageView no longer exists. You should not use this PhotoViewAttacher any more.\");\n        }\n\n        return imageView;\n    }\n\n    @Override\n    public float getMinimumScale() {\n        return mMinScale;\n    }\n\n    @Override\n    public float getMediumScale() {\n        return mMidScale;\n    }\n\n    @Override\n    public float getMaximumScale() {\n        return mMaxScale;\n    }\n\n    @Override\n    public float getScale() {\n        return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));\n    }\n\n    @Override\n    public ScaleType getScaleType() {\n        return mScaleType;\n    }\n\n    @Override\n    public void onDrag(float dx, float dy) {\n        if (mScaleDragDetector.isScaling()) {\n            return; // Do not drag if we are already scaling\n        }\n\n        if (DEBUG) {\n            LogManager.getLogger().d(LOG_TAG,\n                    String.format(\"onDrag: dx: %.2f. dy: %.2f\", dx, dy));\n        }\n\n        ImageView imageView = getImageView();\n        mSuppMatrix.postTranslate(dx, dy);\n        checkAndDisplayMatrix();\n\n        /**\n         * Here we decide whether to let the ImageView's parent to start taking\n         * over the touch event.\n         *\n         * First we check whether this function is enabled. We never want the\n         * parent to take over if we're scaling. We then check the edge we're\n         * on, and the direction of the scroll (i.e. if we're pulling against\n         * the edge, aka 'overscrolling', let the parent take over).\n         */\n        ViewParent parent = imageView.getParent();\n        if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) {\n            if (mScrollEdge == EDGE_BOTH\n                    || (mScrollEdge == EDGE_LEFT && dx >= 1f)\n                    || (mScrollEdge == EDGE_RIGHT && dx <= -1f)) {\n                if (null != parent) {\n                    parent.requestDisallowInterceptTouchEvent(false);\n                }\n            }\n        } else {\n            if (null != parent) {\n                parent.requestDisallowInterceptTouchEvent(true);\n            }\n        }\n    }\n\n    @Override\n    public void onFling(float startX, float startY, float velocityX,\n                        float velocityY) {\n        if (DEBUG) {\n            LogManager.getLogger().d(\n                    LOG_TAG,\n                    \"onFling. sX: \" + startX + \" sY: \" + startY + \" Vx: \"\n                            + velocityX + \" Vy: \" + velocityY);\n        }\n        ImageView imageView = getImageView();\n        mCurrentFlingRunnable = new FlingRunnable(imageView.getContext());\n        mCurrentFlingRunnable.fling(getImageViewWidth(imageView),\n                getImageViewHeight(imageView), (int) velocityX, (int) velocityY);\n        imageView.post(mCurrentFlingRunnable);\n    }\n\n    @Override\n    public void onGlobalLayout() {\n        ImageView imageView = getImageView();\n\n        if (null != imageView) {\n            if (mZoomEnabled) {\n                final int top = imageView.getTop();\n                final int right = imageView.getRight();\n                final int bottom = imageView.getBottom();\n                final int left = imageView.getLeft();\n\n                /**\n                 * We need to check whether the ImageView's bounds have changed.\n                 * This would be easier if we targeted API 11+ as we could just use\n                 * View.OnLayoutChangeListener. Instead we have to replicate the\n                 * work, keeping track of the ImageView's bounds and then checking\n                 * if the values change.\n                 */\n                if (top != mIvTop || bottom != mIvBottom || left != mIvLeft\n                        || right != mIvRight) {\n                    // Update our base matrix, as the bounds have changed\n                    updateBaseMatrix(imageView.getDrawable());\n\n                    // Update values as something has changed\n                    mIvTop = top;\n                    mIvRight = right;\n                    mIvBottom = bottom;\n                    mIvLeft = left;\n                }\n            } else {\n                updateBaseMatrix(imageView.getDrawable());\n            }\n        }\n    }\n\n    @Override\n    public void onScale(float scaleFactor, float focusX, float focusY) {\n        if (DEBUG) {\n            LogManager.getLogger().d(\n                    LOG_TAG,\n                    String.format(\"onScale: scale: %.2f. fX: %.2f. fY: %.2f\",\n                            scaleFactor, focusX, focusY));\n        }\n\n        if ((getScale() < mMaxScale || scaleFactor < 1f) && (getScale() > mMinScale || scaleFactor > 1f)) {\n            if (null != mScaleChangeListener) {\n                mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY);\n            }\n            mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);\n            checkAndDisplayMatrix();\n        }\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public boolean onTouch(View v, MotionEvent ev) {\n        boolean handled = false;\n\n        if (mZoomEnabled && hasDrawable((ImageView) v)) {\n            ViewParent parent = v.getParent();\n            switch (ev.getAction()) {\n                case ACTION_DOWN:\n                    // First, disable the Parent from intercepting the touch\n                    // event\n                    if (null != parent) {\n                        parent.requestDisallowInterceptTouchEvent(true);\n                    } else {\n                        LogManager.getLogger().i(LOG_TAG, \"onTouch getParent() returned null\");\n                    }\n\n                    // If we're flinging, and the user presses down, cancel\n                    // fling\n                    cancelFling();\n                    break;\n\n                case ACTION_CANCEL:\n                case ACTION_UP:\n                    // If the user has zoomed less than min scale, zoom back\n                    // to min scale\n                    if (getScale() < mMinScale) {\n                        RectF rect = getDisplayRect();\n                        if (null != rect) {\n                            v.post(new AnimatedZoomRunnable(getScale(), mMinScale,\n                                    rect.centerX(), rect.centerY()));\n                            handled = true;\n                        }\n                    }\n                    break;\n            }\n\n            // Try the Scale/Drag detector\n            if (null != mScaleDragDetector) {\n                boolean wasScaling = mScaleDragDetector.isScaling();\n                boolean wasDragging = mScaleDragDetector.isDragging();\n\n                handled = mScaleDragDetector.onTouchEvent(ev);\n\n                boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling();\n                boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging();\n\n                mBlockParentIntercept = didntScale && didntDrag;\n            }\n\n            // Check to see if the user double tapped\n            if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {\n                handled = true;\n            }\n\n        }\n\n        return handled;\n    }\n\n    @Override\n    public void setAllowParentInterceptOnEdge(boolean allow) {\n        mAllowParentInterceptOnEdge = allow;\n    }\n\n    @Override\n    public void setMinimumScale(float minimumScale) {\n        checkZoomLevels(minimumScale, mMidScale, mMaxScale);\n        mMinScale = minimumScale;\n    }\n\n    @Override\n    public void setMediumScale(float mediumScale) {\n        checkZoomLevels(mMinScale, mediumScale, mMaxScale);\n        mMidScale = mediumScale;\n    }\n\n    @Override\n    public void setMaximumScale(float maximumScale) {\n        checkZoomLevels(mMinScale, mMidScale, maximumScale);\n        mMaxScale = maximumScale;\n    }\n\n    @Override\n    public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {\n        checkZoomLevels(minimumScale, mediumScale, maximumScale);\n        mMinScale = minimumScale;\n        mMidScale = mediumScale;\n        mMaxScale = maximumScale;\n    }\n\n    @Override\n    public void setOnLongClickListener(OnLongClickListener listener) {\n        mLongClickListener = listener;\n    }\n\n    @Override\n    public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {\n        mMatrixChangeListener = listener;\n    }\n\n    @Override\n    public void setOnPhotoTapListener(OnPhotoTapListener listener) {\n        mPhotoTapListener = listener;\n    }\n\n    @Nullable\n    OnPhotoTapListener getOnPhotoTapListener() {\n        return mPhotoTapListener;\n    }\n\n    @Override\n    public void setOnViewTapListener(OnViewTapListener listener) {\n        mViewTapListener = listener;\n    }\n\n    @Nullable\n    OnViewTapListener getOnViewTapListener() {\n        return mViewTapListener;\n    }\n\n    @Override\n    public void setScale(float scale) {\n        setScale(scale, false);\n    }\n\n    @Override\n    public void setScale(float scale, boolean animate) {\n        ImageView imageView = getImageView();\n\n        if (null != imageView) {\n            setScale(scale,\n                    (imageView.getRight()) / 2,\n                    (imageView.getBottom()) / 2,\n                    animate);\n        }\n    }\n\n    @Override\n    public void setScale(float scale, float focalX, float focalY,\n                         boolean animate) {\n        ImageView imageView = getImageView();\n\n        if (null != imageView) {\n            // Check to see if the scale is within bounds\n            if (scale < mMinScale || scale > mMaxScale) {\n                LogManager\n                        .getLogger()\n                        .i(LOG_TAG,\n                                \"Scale must be within the range of minScale and maxScale\");\n                return;\n            }\n\n            if (animate) {\n                imageView.post(new AnimatedZoomRunnable(getScale(), scale,\n                        focalX, focalY));\n            } else {\n                mSuppMatrix.setScale(scale, scale, focalX, focalY);\n                checkAndDisplayMatrix();\n            }\n        }\n    }\n\n    /**\n     * Set the zoom interpolator\n     * @param interpolator the zoom interpolator\n     */\n    public void setZoomInterpolator(Interpolator interpolator) {\n        mInterpolator = interpolator;\n    }\n\n    @Override\n    public void setScaleType(ScaleType scaleType) {\n        if (isSupportedScaleType(scaleType) && scaleType != mScaleType) {\n            mScaleType = scaleType;\n\n            // Finally update\n            update();\n        }\n    }\n\n    @Override\n    public void setZoomable(boolean zoomable) {\n        mZoomEnabled = zoomable;\n        update();\n    }\n\n    public void update() {\n        ImageView imageView = getImageView();\n\n        if (null != imageView) {\n            if (mZoomEnabled) {\n                // Make sure we using MATRIX Scale Type\n                setImageViewScaleTypeMatrix(imageView);\n\n                // Update the base matrix using the current drawable\n                updateBaseMatrix(imageView.getDrawable());\n            } else {\n                // Reset the Matrix...\n                resetMatrix();\n            }\n        }\n    }\n\n    /**\n     * Get the display matrix\n     * @param matrix target matrix to copy to\n     */\n    @Override\n    public void getDisplayMatrix(Matrix matrix) {\n        matrix.set(getDrawMatrix());\n    }\n\n    /**\n     * Get the current support matrix\n     */\n    public void getSuppMatrix(Matrix matrix) {\n        matrix.set(mSuppMatrix);\n    }\n\n    private Matrix getDrawMatrix() {\n        mDrawMatrix.set(mBaseMatrix);\n        mDrawMatrix.postConcat(mSuppMatrix);\n        return mDrawMatrix;\n    }\n\n    private void cancelFling() {\n        if (null != mCurrentFlingRunnable) {\n            mCurrentFlingRunnable.cancelFling();\n            mCurrentFlingRunnable = null;\n        }\n    }\n\n    public Matrix getImageMatrix() {\n        return mDrawMatrix;\n    }\n\n    /**\n     * Helper method that simply checks the Matrix, and then displays the result\n     */\n    private void checkAndDisplayMatrix() {\n        if (checkMatrixBounds()) {\n            setImageViewMatrix(getDrawMatrix());\n        }\n    }\n\n    private void checkImageViewScaleType() {\n        ImageView imageView = getImageView();\n\n        /**\n         * PhotoView's getScaleType() will just divert to this.getScaleType() so\n         * only call if we're not attached to a PhotoView.\n         */\n        if (null != imageView && !(imageView instanceof IPhotoView)) {\n            if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {\n                throw new IllegalStateException(\n                        \"The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher. You should call setScaleType on the PhotoViewAttacher instead of on the ImageView\"  );\n            }\n        }\n    }\n\n    private boolean checkMatrixBounds() {\n        final ImageView imageView = getImageView();\n        if (null == imageView) {\n            return false;\n        }\n\n        final RectF rect = getDisplayRect(getDrawMatrix());\n        if (null == rect) {\n            return false;\n        }\n\n        final float height = rect.height(), width = rect.width();\n        float deltaX = 0, deltaY = 0;\n\n        final int viewHeight = getImageViewHeight(imageView);\n        if (height <= viewHeight) {\n            switch (mScaleType) {\n                case FIT_START:\n                    deltaY = -rect.top;\n                    break;\n                case FIT_END:\n                    deltaY = viewHeight - height - rect.top;\n                    break;\n                default:\n                    deltaY = (viewHeight - height) / 2 - rect.top;\n                    break;\n            }\n        } else if (rect.top > 0) {\n            deltaY = -rect.top;\n        } else if (rect.bottom < viewHeight) {\n            deltaY = viewHeight - rect.bottom;\n        }\n\n        final int viewWidth = getImageViewWidth(imageView);\n        if (width <= viewWidth) {\n            switch (mScaleType) {\n                case FIT_START:\n                    deltaX = -rect.left;\n                    break;\n                case FIT_END:\n                    deltaX = viewWidth - width - rect.left;\n                    break;\n                default:\n                    deltaX = (viewWidth - width) / 2 - rect.left;\n                    break;\n            }\n            mScrollEdge = EDGE_BOTH;\n        } else if (rect.left > 0) {\n            mScrollEdge = EDGE_LEFT;\n            deltaX = -rect.left;\n        } else if (rect.right < viewWidth) {\n            deltaX = viewWidth - rect.right;\n            mScrollEdge = EDGE_RIGHT;\n        } else {\n            mScrollEdge = EDGE_NONE;\n        }\n\n        // Finally actually translate the matrix\n        mSuppMatrix.postTranslate(deltaX, deltaY);\n        return true;\n    }\n\n    /**\n     * Helper method that maps the supplied Matrix to the current Drawable\n     *\n     * @param matrix - Matrix to map Drawable against\n     * @return RectF - Displayed Rectangle\n     */\n    private RectF getDisplayRect(Matrix matrix) {\n        ImageView imageView = getImageView();\n\n        if (null != imageView) {\n            Drawable d = imageView.getDrawable();\n            if (null != d) {\n                mDisplayRect.set(0, 0, d.getIntrinsicWidth(),\n                        d.getIntrinsicHeight());\n                matrix.mapRect(mDisplayRect);\n                return mDisplayRect;\n            }\n        }\n        return null;\n    }\n\n    public Bitmap getVisibleRectangleBitmap() {\n        ImageView imageView = getImageView();\n        return imageView == null ? null : imageView.getDrawingCache();\n    }\n\n    @Override\n    public void setZoomTransitionDuration(int milliseconds) {\n        if (milliseconds < 0)\n            milliseconds = DEFAULT_ZOOM_DURATION;\n        this.ZOOM_DURATION = milliseconds;\n    }\n\n    @Override\n    public IPhotoView getIPhotoViewImplementation() {\n        return this;\n    }\n\n    /**\n     * Helper method that 'unpacks' a Matrix and returns the required value\n     *\n     * @param matrix     - Matrix to unpack\n     * @param whichValue - Which value from Matrix.M* to return\n     * @return float - returned value\n     */\n    private float getValue(Matrix matrix, int whichValue) {\n        matrix.getValues(mMatrixValues);\n        return mMatrixValues[whichValue];\n    }\n\n    /**\n     * Resets the Matrix back to FIT_CENTER, and then displays it.s\n     */\n    private void resetMatrix() {\n        mSuppMatrix.reset();\n        setRotationBy(mBaseRotation);\n        setImageViewMatrix(getDrawMatrix());\n        checkMatrixBounds();\n    }\n\n    private void setImageViewMatrix(Matrix matrix) {\n        ImageView imageView = getImageView();\n        if (null != imageView) {\n\n            checkImageViewScaleType();\n            imageView.setImageMatrix(matrix);\n\n            // Call MatrixChangedListener if needed\n            if (null != mMatrixChangeListener) {\n                RectF displayRect = getDisplayRect(matrix);\n                if (null != displayRect) {\n                    mMatrixChangeListener.onMatrixChanged(displayRect);\n                }\n            }\n        }\n    }\n\n    /**\n     * Calculate Matrix for FIT_CENTER\n     *\n     * @param d - Drawable being displayed\n     */\n    private void updateBaseMatrix(Drawable d) {\n        ImageView imageView = getImageView();\n        if (null == imageView || null == d) {\n            return;\n        }\n\n        final float viewWidth = getImageViewWidth(imageView);\n        final float viewHeight = getImageViewHeight(imageView);\n        final int drawableWidth = d.getIntrinsicWidth();\n        final int drawableHeight = d.getIntrinsicHeight();\n\n        mBaseMatrix.reset();\n\n        final float widthScale = viewWidth / drawableWidth;\n        final float heightScale = viewHeight / drawableHeight;\n\n        if (mScaleType == ScaleType.CENTER) {\n            mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F,\n                    (viewHeight - drawableHeight) / 2F);\n\n        } else if (mScaleType == ScaleType.CENTER_CROP) {\n            float scale = Math.max(widthScale, heightScale);\n            mBaseMatrix.postScale(scale, scale);\n            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,\n                    (viewHeight - drawableHeight * scale) / 2F);\n\n        } else if (mScaleType == ScaleType.CENTER_INSIDE) {\n            float scale = Math.min(1.0f, Math.min(widthScale, heightScale));\n            mBaseMatrix.postScale(scale, scale);\n            mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,\n                    (viewHeight - drawableHeight * scale) / 2F);\n\n        } else {\n            RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);\n            RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);\n\n            if ((int) mBaseRotation % 180 != 0) {\n                mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);\n            }\n\n            switch (mScaleType) {\n                case FIT_CENTER:\n                    mBaseMatrix\n                            .setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);\n                    break;\n\n                case FIT_START:\n                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);\n                    break;\n\n                case FIT_END:\n                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);\n                    break;\n\n                case FIT_XY:\n                    mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);\n                    break;\n\n                default:\n                    break;\n            }\n        }\n\n        resetMatrix();\n    }\n\n    private int getImageViewWidth(ImageView imageView) {\n        if (null == imageView)\n            return 0;\n        return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();\n    }\n\n    private int getImageViewHeight(ImageView imageView) {\n        if (null == imageView)\n            return 0;\n        return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();\n    }\n\n    /**\n     * Interface definition for a callback to be invoked when the internal Matrix has changed for\n     * this View.\n     *\n     * @author Chris Banes\n     */\n    public interface OnMatrixChangedListener {\n        /**\n         * Callback for when the Matrix displaying the Drawable has changed. This could be because\n         * the View's bounds have changed, or the user has zoomed.\n         *\n         * @param rect - Rectangle displaying the Drawable's new bounds.\n         */\n        void onMatrixChanged(RectF rect);\n    }\n\n    /**\n     * Interface definition for callback to be invoked when attached ImageView scale changes\n     *\n     * @author Marek Sebera\n     */\n    public interface OnScaleChangeListener {\n        /**\n         * Callback for when the scale changes\n         *\n         * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in)\n         * @param focusX      focal point X position\n         * @param focusY      focal point Y position\n         */\n        void onScaleChange(float scaleFactor, float focusX, float focusY);\n    }\n\n    /**\n     * Interface definition for a callback to be invoked when the Photo is tapped with a single\n     * tap.\n     *\n     * @author Chris Banes\n     */\n    public interface OnPhotoTapListener {\n\n        /**\n         * A callback to receive where the user taps on a photo. You will only receive a callback if\n         * the user taps on the actual photo, tapping on 'whitespace' will be ignored.\n         *\n         * @param view - View the user tapped.\n         * @param x    - where the user tapped from the of the Drawable, as percentage of the\n         *             Drawable width.\n         * @param y    - where the user tapped from the top of the Drawable, as percentage of the\n         *             Drawable height.\n         */\n        void onPhotoTap(View view, float x, float y);\n\n        /**\n         * A simple callback where out of photo happened;\n         * */\n        void onOutsidePhotoTap();\n    }\n\n    /**\n     * Interface definition for a callback to be invoked when the ImageView is tapped with a single\n     * tap.\n     *\n     * @author Chris Banes\n     */\n    public interface OnViewTapListener {\n\n        /**\n         * A callback to receive where the user taps on a ImageView. You will receive a callback if\n         * the user taps anywhere on the view, tapping on 'whitespace' will not be ignored.\n         *\n         * @param view - View the user tapped.\n         * @param x    - where the user tapped from the left of the View.\n         * @param y    - where the user tapped from the top of the View.\n         */\n        void onViewTap(View view, float x, float y);\n    }\n\n    /**\n     * Interface definition for a callback to be invoked when the ImageView is fling with a single\n     * touch\n     *\n     * @author tonyjs\n     */\n    public interface OnSingleFlingListener {\n\n        /**\n         * A callback to receive where the user flings on a ImageView. You will receive a callback if\n         * the user flings anywhere on the view.\n         *\n         * @param e1        - MotionEvent the user first touch.\n         * @param e2        - MotionEvent the user last touch.\n         * @param velocityX - distance of user's horizontal fling.\n         * @param velocityY - distance of user's vertical fling.\n         */\n        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);\n    }\n\n    private class AnimatedZoomRunnable implements Runnable {\n\n        private final float mFocalX, mFocalY;\n        private final long mStartTime;\n        private final float mZoomStart, mZoomEnd;\n\n        public AnimatedZoomRunnable(final float currentZoom, final float targetZoom,\n                                    final float focalX, final float focalY) {\n            mFocalX = focalX;\n            mFocalY = focalY;\n            mStartTime = System.currentTimeMillis();\n            mZoomStart = currentZoom;\n            mZoomEnd = targetZoom;\n        }\n\n        @Override\n        public void run() {\n            ImageView imageView = getImageView();\n            if (imageView == null) {\n                return;\n            }\n\n            float t = interpolate();\n            float scale = mZoomStart + t * (mZoomEnd - mZoomStart);\n            float deltaScale = scale / getScale();\n\n            onScale(deltaScale, mFocalX, mFocalY);\n\n            // We haven't hit our target scale yet, so post ourselves again\n            if (t < 1f) {\n                Compat.postOnAnimation(imageView, this);\n            }\n        }\n\n        private float interpolate() {\n            float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION;\n            t = Math.min(1f, t);\n            t = mInterpolator.getInterpolation(t);\n            return t;\n        }\n    }\n\n    private class FlingRunnable implements Runnable {\n\n        private final ScrollerProxy mScroller;\n        private int mCurrentX, mCurrentY;\n\n        public FlingRunnable(Context context) {\n            mScroller = ScrollerProxy.getScroller(context);\n        }\n\n        public void cancelFling() {\n            if (DEBUG) {\n                LogManager.getLogger().d(LOG_TAG, \"Cancel Fling\");\n            }\n            mScroller.forceFinished(true);\n        }\n\n        public void fling(int viewWidth, int viewHeight, int velocityX,\n                          int velocityY) {\n            final RectF rect = getDisplayRect();\n            if (null == rect) {\n                return;\n            }\n\n            final int startX = Math.round(-rect.left);\n            final int minX, maxX, minY, maxY;\n\n            if (viewWidth < rect.width()) {\n                minX = 0;\n                maxX = Math.round(rect.width() - viewWidth);\n            } else {\n                minX = maxX = startX;\n            }\n\n            final int startY = Math.round(-rect.top);\n            if (viewHeight < rect.height()) {\n                minY = 0;\n                maxY = Math.round(rect.height() - viewHeight);\n            } else {\n                minY = maxY = startY;\n            }\n\n            mCurrentX = startX;\n            mCurrentY = startY;\n\n            if (DEBUG) {\n                LogManager.getLogger().d(\n                        LOG_TAG,\n                        \"fling. StartX:\" + startX + \" StartY:\" + startY\n                                + \" MaxX:\" + maxX + \" MaxY:\" + maxY);\n            }\n\n            // If we actually can move, fling the scroller\n            if (startX != maxX || startY != maxY) {\n                mScroller.fling(startX, startY, velocityX, velocityY, minX,\n                        maxX, minY, maxY, 0, 0);\n            }\n        }\n\n        @Override\n        public void run() {\n            if (mScroller.isFinished()) {\n                return; // remaining post that should not be handled\n            }\n\n            ImageView imageView = getImageView();\n            if (null != imageView && mScroller.computeScrollOffset()) {\n\n                final int newX = mScroller.getCurrX();\n                final int newY = mScroller.getCurrY();\n\n                if (DEBUG) {\n                    LogManager.getLogger().d(\n                            LOG_TAG,\n                            \"fling run(). CurrentX:\" + mCurrentX + \" CurrentY:\"\n                                    + mCurrentY + \" NewX:\" + newX + \" NewY:\"\n                                    + newY);\n                }\n\n                mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);\n                setImageViewMatrix(getDrawMatrix());\n\n                mCurrentX = newX;\n                mCurrentY = newY;\n\n                // Post On animation\n                Compat.postOnAnimation(imageView, this);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/gestures/CupcakeGestureDetector.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.gestures;\n\nimport android.content.Context;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.ViewConfiguration;\n\nimport uk.co.senab.photoview.log.LogManager;\n\npublic class CupcakeGestureDetector implements GestureDetector {\n\n    protected OnGestureListener mListener;\n    private static final String LOG_TAG = \"CupcakeGestureDetector\";\n    float mLastTouchX;\n    float mLastTouchY;\n    final float mTouchSlop;\n    final float mMinimumVelocity;\n\n    @Override\n    public void setOnGestureListener(OnGestureListener listener) {\n        this.mListener = listener;\n    }\n\n    public CupcakeGestureDetector(Context context) {\n        final ViewConfiguration configuration = ViewConfiguration\n                .get(context);\n        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();\n        mTouchSlop = configuration.getScaledTouchSlop();\n    }\n\n    private VelocityTracker mVelocityTracker;\n    private boolean mIsDragging;\n\n    float getActiveX(MotionEvent ev) {\n        return ev.getX();\n    }\n\n    float getActiveY(MotionEvent ev) {\n        return ev.getY();\n    }\n\n    @Override\n    public boolean isScaling() {\n        return false;\n    }\n\n    @Override\n    public boolean isDragging() {\n        return mIsDragging;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        switch (ev.getAction()) {\n            case MotionEvent.ACTION_DOWN: {\n                mVelocityTracker = VelocityTracker.obtain();\n                if (null != mVelocityTracker) {\n                    mVelocityTracker.addMovement(ev);\n                } else {\n                    LogManager.getLogger().i(LOG_TAG, \"Velocity tracker is null\");\n                }\n\n                mLastTouchX = getActiveX(ev);\n                mLastTouchY = getActiveY(ev);\n                mIsDragging = false;\n                break;\n            }\n\n            case MotionEvent.ACTION_MOVE: {\n                final float x = getActiveX(ev);\n                final float y = getActiveY(ev);\n                final float dx = x - mLastTouchX, dy = y - mLastTouchY;\n\n                if (!mIsDragging) {\n                    // Use Pythagoras to see if drag length is larger than\n                    // touch slop\n                    mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;\n                }\n\n                if (mIsDragging) {\n                    mListener.onDrag(dx, dy);\n                    mLastTouchX = x;\n                    mLastTouchY = y;\n\n                    if (null != mVelocityTracker) {\n                        mVelocityTracker.addMovement(ev);\n                    }\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_CANCEL: {\n                // Recycle Velocity Tracker\n                if (null != mVelocityTracker) {\n                    mVelocityTracker.recycle();\n                    mVelocityTracker = null;\n                }\n                break;\n            }\n\n            case MotionEvent.ACTION_UP: {\n                if (mIsDragging) {\n                    if (null != mVelocityTracker) {\n                        mLastTouchX = getActiveX(ev);\n                        mLastTouchY = getActiveY(ev);\n\n                        // Compute velocity within the last 1000ms\n                        mVelocityTracker.addMovement(ev);\n                        mVelocityTracker.computeCurrentVelocity(1000);\n\n                        final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker\n                                .getYVelocity();\n\n                        // If the velocity is greater than minVelocity, call\n                        // listener\n                        if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {\n                            mListener.onFling(mLastTouchX, mLastTouchY, -vX,\n                                    -vY);\n                        }\n                    }\n                }\n\n                // Recycle Velocity Tracker\n                if (null != mVelocityTracker) {\n                    mVelocityTracker.recycle();\n                    mVelocityTracker = null;\n                }\n                break;\n            }\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/gestures/EclairGestureDetector.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n * <p/>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p/>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p/>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.gestures;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.view.MotionEvent;\n\nimport uk.co.senab.photoview.Compat;\n\n@TargetApi(5)\npublic class EclairGestureDetector extends CupcakeGestureDetector {\n\n    private static final int INVALID_POINTER_ID = -1;\n    private int mActivePointerId = INVALID_POINTER_ID;\n    private int mActivePointerIndex = 0;\n\n    public EclairGestureDetector(Context context) {\n        super(context);\n    }\n\n    @Override\n    float getActiveX(MotionEvent ev) {\n        try {\n            return ev.getX(mActivePointerIndex);\n        } catch (Exception e) {\n            return ev.getX();\n        }\n    }\n\n    @Override\n    float getActiveY(MotionEvent ev) {\n        try {\n            return ev.getY(mActivePointerIndex);\n        } catch (Exception e) {\n            return ev.getY();\n        }\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        final int action = ev.getAction();\n        switch (action & MotionEvent.ACTION_MASK) {\n            case MotionEvent.ACTION_DOWN:\n                mActivePointerId = ev.getPointerId(0);\n                break;\n            case MotionEvent.ACTION_CANCEL:\n            case MotionEvent.ACTION_UP:\n                mActivePointerId = INVALID_POINTER_ID;\n                break;\n            case MotionEvent.ACTION_POINTER_UP:\n                // Ignore deprecation, ACTION_POINTER_ID_MASK and\n                // ACTION_POINTER_ID_SHIFT has same value and are deprecated\n                // You can have either deprecation or lint target api warning\n                final int pointerIndex = Compat.getPointerIndex(ev.getAction());\n                final int pointerId = ev.getPointerId(pointerIndex);\n                if (pointerId == mActivePointerId) {\n                    // This was our active pointer going up. Choose a new\n                    // active pointer and adjust accordingly.\n                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;\n                    mActivePointerId = ev.getPointerId(newPointerIndex);\n                    mLastTouchX = ev.getX(newPointerIndex);\n                    mLastTouchY = ev.getY(newPointerIndex);\n                }\n                break;\n        }\n\n        mActivePointerIndex = ev\n                .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId\n                        : 0);\n        try {\n            return super.onTouchEvent(ev);\n        } catch (IllegalArgumentException e) {\n            // Fix for support lib bug, happening when onDestroy is\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/gestures/FroyoGestureDetector.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n * <p/>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p/>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p/>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.gestures;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.view.MotionEvent;\nimport android.view.ScaleGestureDetector;\n\n@TargetApi(8)\npublic class FroyoGestureDetector extends EclairGestureDetector {\n\n    protected final ScaleGestureDetector mDetector;\n\n    public FroyoGestureDetector(Context context) {\n        super(context);\n        ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {\n\n            @Override\n            public boolean onScale(ScaleGestureDetector detector) {\n                float scaleFactor = detector.getScaleFactor();\n\n                if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))\n                    return false;\n\n                mListener.onScale(scaleFactor,\n                        detector.getFocusX(), detector.getFocusY());\n                return true;\n            }\n\n            @Override\n            public boolean onScaleBegin(ScaleGestureDetector detector) {\n                return true;\n            }\n\n            @Override\n            public void onScaleEnd(ScaleGestureDetector detector) {\n                // NO-OP\n            }\n        };\n        mDetector = new ScaleGestureDetector(context, mScaleListener);\n    }\n\n    @Override\n    public boolean isScaling() {\n        return mDetector.isInProgress();\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent ev) {\n        try {\n            mDetector.onTouchEvent(ev);\n            return super.onTouchEvent(ev);\n        } catch (IllegalArgumentException e) {\n            // Fix for support lib bug, happening when onDestroy is\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/gestures/GestureDetector.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.gestures;\n\nimport android.view.MotionEvent;\n\npublic interface GestureDetector {\n\n    boolean onTouchEvent(MotionEvent ev);\n\n    boolean isScaling();\n\n    boolean isDragging();\n\n    void setOnGestureListener(OnGestureListener listener);\n\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/gestures/OnGestureListener.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.gestures;\n\npublic interface OnGestureListener {\n\n    void onDrag(float dx, float dy);\n\n    void onFling(float startX, float startY, float velocityX,\n                 float velocityY);\n\n    void onScale(float scaleFactor, float focusX, float focusY);\n\n}"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/gestures/VersionedGestureDetector.java",
    "content": "package uk.co.senab.photoview.gestures;\n\n/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\n\nimport android.content.Context;\nimport android.os.Build;\n\npublic final class VersionedGestureDetector {\n\n    public static GestureDetector newInstance(Context context,\n                                              OnGestureListener listener) {\n        final int sdkVersion = Build.VERSION.SDK_INT;\n        GestureDetector detector;\n\n        if (sdkVersion < Build.VERSION_CODES.ECLAIR) {\n            detector = new CupcakeGestureDetector(context);\n        } else if (sdkVersion < Build.VERSION_CODES.FROYO) {\n            detector = new EclairGestureDetector(context);\n        } else {\n            detector = new FroyoGestureDetector(context);\n        }\n\n        detector.setOnGestureListener(listener);\n\n        return detector;\n    }\n\n}"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/log/LogManager.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.log;\n\nimport android.util.Log;\n\n/**\n * class that holds the {@link Logger} for this library, defaults to {@link LoggerDefault} to send logs to android {@link Log}\n */\npublic final class LogManager {\n\n    private static Logger logger = new LoggerDefault();\n\n    public static void setLogger(Logger newLogger) {\n        logger = newLogger;\n    }\n\n    public static Logger getLogger() {\n        return logger;\n    }\n\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/log/Logger.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.log;\n\n/**\n * interface for a logger class to replace the static calls to {@link android.util.Log}\n */\npublic interface Logger {\n\n    int v(String tag, String msg);\n\n    int v(String tag, String msg, Throwable tr);\n\n    int d(String tag, String msg);\n\n    int d(String tag, String msg, Throwable tr);\n\n    int i(String tag, String msg);\n\n    int i(String tag, String msg, Throwable tr);\n\n    int w(String tag, String msg);\n\n    int w(String tag, String msg, Throwable tr);\n\n    int e(String tag, String msg);\n\n    int e(String tag, String msg, Throwable tr);\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/log/LoggerDefault.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.log;\n\nimport android.util.Log;\n\n/**\n * Helper class to redirect {@link LogManager#logger} to {@link Log}\n */\npublic class LoggerDefault implements Logger {\n\n    @Override\n    public int v(String tag, String msg) {\n        return Log.v(tag, msg);\n    }\n\n    @Override\n    public int v(String tag, String msg, Throwable tr) {\n        return Log.v(tag, msg, tr);\n    }\n\n    @Override\n    public int d(String tag, String msg) {\n        return Log.d(tag, msg);\n    }\n\n    @Override\n    public int d(String tag, String msg, Throwable tr) {\n        return Log.d(tag, msg, tr);\n    }\n\n    @Override\n    public int i(String tag, String msg) {\n        return Log.i(tag, msg);\n    }\n\n    @Override\n    public int i(String tag, String msg, Throwable tr) {\n        return Log.i(tag, msg, tr);\n    }\n\n    @Override\n    public int w(String tag, String msg) {\n        return Log.w(tag, msg);\n    }\n\n    @Override\n    public int w(String tag, String msg, Throwable tr) {\n        return Log.w(tag, msg, tr);\n    }\n\n    @Override\n    public int e(String tag, String msg) {\n        return Log.e(tag, msg);\n    }\n\n    @Override\n    public int e(String tag, String msg, Throwable tr) {\n        return Log.e(tag, msg, tr);\n    }\n\n\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/scrollerproxy/GingerScroller.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.scrollerproxy;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.widget.OverScroller;\n\n@TargetApi(9)\npublic class GingerScroller extends ScrollerProxy {\n\n    protected final OverScroller mScroller;\n\n    public GingerScroller(Context context) {\n        mScroller = new OverScroller(context);\n    }\n\n    @Override\n    public boolean computeScrollOffset() {\n        return mScroller.computeScrollOffset();\n    }\n\n    @Override\n    public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,\n                      int overX, int overY) {\n        mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY);\n    }\n\n    @Override\n    public void forceFinished(boolean finished) {\n        mScroller.forceFinished(finished);\n    }\n\n    @Override\n    public boolean isFinished() {\n        return mScroller.isFinished();\n    }\n\n    @Override\n    public int getCurrX() {\n        return mScroller.getCurrX();\n    }\n\n    @Override\n    public int getCurrY() {\n        return mScroller.getCurrY();\n    }\n}"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/scrollerproxy/IcsScroller.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.scrollerproxy;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\n\n@TargetApi(14)\npublic class IcsScroller extends GingerScroller {\n\n    public IcsScroller(Context context) {\n        super(context);\n    }\n\n    @Override\n    public boolean computeScrollOffset() {\n        return mScroller.computeScrollOffset();\n    }\n\n}\n"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/scrollerproxy/PreGingerScroller.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.scrollerproxy;\n\nimport android.content.Context;\nimport android.widget.Scroller;\n\npublic class PreGingerScroller extends ScrollerProxy {\n\n    private final Scroller mScroller;\n\n    public PreGingerScroller(Context context) {\n        mScroller = new Scroller(context);\n    }\n\n    @Override\n    public boolean computeScrollOffset() {\n        return mScroller.computeScrollOffset();\n    }\n\n    @Override\n    public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,\n                      int overX, int overY) {\n        mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);\n    }\n\n    @Override\n    public void forceFinished(boolean finished) {\n        mScroller.forceFinished(finished);\n    }\n\n    public boolean isFinished() {\n        return mScroller.isFinished();\n    }\n\n    @Override\n    public int getCurrX() {\n        return mScroller.getCurrX();\n    }\n\n    @Override\n    public int getCurrY() {\n        return mScroller.getCurrY();\n    }\n}"
  },
  {
    "path": "PhotoView_library/src/main/java/uk/co/senab/photoview/scrollerproxy/ScrollerProxy.java",
    "content": "/*******************************************************************************\n * Copyright 2011, 2012 Chris Banes.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *******************************************************************************/\npackage uk.co.senab.photoview.scrollerproxy;\n\nimport android.content.Context;\nimport android.os.Build.VERSION;\nimport android.os.Build.VERSION_CODES;\n\npublic abstract class ScrollerProxy {\n\n    public static ScrollerProxy getScroller(Context context) {\n        if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {\n            return new PreGingerScroller(context);\n        } else if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {\n            return new GingerScroller(context);\n        } else {\n            return new IcsScroller(context);\n        }\n    }\n\n    public abstract boolean computeScrollOffset();\n\n    public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY,\n                               int maxY, int overX, int overY);\n\n    public abstract void forceFinished(boolean finished);\n\n    public abstract boolean isFinished();\n\n    public abstract int getCurrX();\n\n    public abstract int getCurrY();\n\n\n}\n"
  },
  {
    "path": "README.md",
    "content": "# MobilePlayer1020\n手机影音项目是真实的上线项目，本视频在原项目基础上，进行了新技术的更新和优化。该项目包括本地音乐播放、网络音乐播放、本地视频播放、网络直播四大模块，几乎涵盖了市面上视频播放类APP的全部技术。\n"
  },
  {
    "path": "RecyclerViewDemo/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "RecyclerViewDemo/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"24.0.3\"\n\n    defaultConfig {\n        applicationId \"com.atguigu.recyclerviewdemo\"\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    compile 'com.android.support:appcompat-v7:24.2.1'\n    testCompile 'junit:junit:4.12'\n\n    compile 'com.android.support:recyclerview-v7:24.2.1'\n}\n"
  },
  {
    "path": "RecyclerViewDemo/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:\\Android_studio_sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "RecyclerViewDemo/src/androidTest/java/com/atguigu/recyclerviewdemo/ExampleInstrumentedTest.java",
    "content": "package com.atguigu.recyclerviewdemo;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.atguigu.recyclerviewdemo\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.atguigu.recyclerviewdemo\">\n\n    <application android:allowBackup=\"true\" android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\" android:supportsRtl=\"true\" android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "RecyclerViewDemo/src/main/java/com/atguigu/recyclerviewdemo/DividerItemDecoration.java",
    "content": "package com.atguigu.recyclerviewdemo;\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/18 15:42\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：自定义分割线\n */\npublic class DividerItemDecoration extends RecyclerView.ItemDecoration {\n\n    private static final int[] ATTRS = new int[]{\n            android.R.attr.listDivider\n    };\n\n    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;\n\n    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;\n\n    private Drawable mDivider;\n\n    private int mOrientation;\n\n    public DividerItemDecoration(Context context, int orientation) {\n        final TypedArray a = context.obtainStyledAttributes(ATTRS);\n        mDivider = a.getDrawable(0);\n        a.recycle();\n        setOrientation(orientation);\n    }\n\n    public void setOrientation(int orientation) {\n        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {\n            throw new IllegalArgumentException(\"invalid orientation\");\n        }\n        mOrientation = orientation;\n    }\n\n    @Override\n    public void onDraw(Canvas c, RecyclerView parent) {\n\n        if (mOrientation == VERTICAL_LIST) {\n            drawVertical(c, parent);\n        } else {\n            drawHorizontal(c, parent);\n        }\n\n    }\n\n\n    public void drawVertical(Canvas c, RecyclerView parent) {\n        final int left = parent.getPaddingLeft();\n        final int right = parent.getWidth() - parent.getPaddingRight();\n\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = parent.getChildAt(i);\n            android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());\n            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child\n                    .getLayoutParams();\n            final int top = child.getBottom() + params.bottomMargin;\n            final int bottom = top + mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(c);\n        }\n    }\n\n    public void drawHorizontal(Canvas c, RecyclerView parent) {\n        final int top = parent.getPaddingTop();\n        final int bottom = parent.getHeight() - parent.getPaddingBottom();\n\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            final View child = parent.getChildAt(i);\n            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child\n                    .getLayoutParams();\n            final int left = child.getRight() + params.rightMargin;\n            final int right = left + mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(c);\n        }\n    }\n\n    @Override\n    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {\n        if (mOrientation == VERTICAL_LIST) {\n            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());\n        } else {\n            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);\n        }\n    }\n}\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/java/com/atguigu/recyclerviewdemo/MainActivity.java",
    "content": "package com.atguigu.recyclerviewdemo;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.DefaultItemAnimator;\nimport android.support.v7.widget.GridLayoutManager;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.StaggeredGridLayoutManager;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.Toast;\n\nimport java.util.ArrayList;\n\npublic class MainActivity extends AppCompatActivity implements View.OnClickListener {\n\n    private Button btnAdd;\n    private Button btnDelete;\n    private Button btnList;\n    private Button btnGrid;\n    private Button btnFlow;\n    private RecyclerView recyclerview;\n    private ArrayList<String> datas;\n    private RecyclerDemoAdapter adapter;\n\n    /**\n     * Find the Views in the layout<br />\n     * <br />\n     * Auto-created on 2017-01-18 14:46:31 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    private void findViews() {\n        setContentView(R.layout.activity_main);\n        btnAdd = (Button) findViewById(R.id.btn_add);\n        btnDelete = (Button) findViewById(R.id.btn_delete);\n        btnList = (Button) findViewById(R.id.btn_List);\n        btnGrid = (Button) findViewById(R.id.btn_Grid);\n        btnFlow = (Button) findViewById(R.id.btn_flow);\n        recyclerview = (RecyclerView) findViewById(R.id.recyclerview);\n\n        btnAdd.setOnClickListener(this);\n        btnDelete.setOnClickListener(this);\n        btnList.setOnClickListener(this);\n        btnGrid.setOnClickListener(this);\n        btnFlow.setOnClickListener(this);\n    }\n\n    /**\n     * Handle button click events<br />\n     * <br />\n     * Auto-created on 2017-01-18 14:46:31 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    @Override\n    public void onClick(View v) {\n        if (v == btnAdd) {\n            // Handle clicks for btnAdd\n            adapter.addData(0,\"new Data\");\n            recyclerview.scrollToPosition(0);\n        } else if (v == btnDelete) {\n            // Handle clicks for btnDelete\n            adapter.removewData(2);\n            recyclerview.scrollToPosition(2);\n        } else if (v == btnList) {\n            // Handle clicks for btnList\n            recyclerview.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));\n        } else if (v == btnGrid) {\n            // Handle clicks for btnGrid\n            recyclerview.setLayoutManager(new GridLayoutManager(this,3,GridLayoutManager.VERTICAL,false));\n        } else if (v == btnFlow) {\n            // Handle clicks for btnFlow\n            recyclerview.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));\n        }\n    }\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        findViews();\n        setAdapter();\n    }\n\n    private void setAdapter() {\n        //准备数据\n        datas = new ArrayList<>();\n        for (int i = 0; i < 200; i++) {\n            datas.add(\"Data_\"+i) ;\n\n        }\n\n        adapter = new RecyclerDemoAdapter(this,datas);\n        //设置RecyclerView的适配器\n        recyclerview.setAdapter(adapter);\n\n        //布局管理器\n        //List 第一个参数：上下文；第二个参数：方向；第三个参数：是否倒序\n        recyclerview.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));\n        //Grid\n\n//        recyclerview.setLayoutManager(new GridLayoutManager(this,3,GridLayoutManager.VERTICAL,false));\n        //瀑布流\n//        recyclerview.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));\n\n\n        //设置分割线\n        recyclerview.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));\n\n        //设置item的动画\n        recyclerview.setItemAnimator(new DefaultItemAnimator());\n\n        //设置item的点击事件\n        adapter.setOnItemClickListener(new RecyclerDemoAdapter.OnItemClickListener() {\n            @Override\n            public void onItemClick(int postion) {\n                Toast.makeText(MainActivity.this, \"\"+datas.get(postion), Toast.LENGTH_SHORT).show();\n            }\n        });\n\n    }\n}\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/java/com/atguigu/recyclerviewdemo/RecyclerDemoAdapter.java",
    "content": "package com.atguigu.recyclerviewdemo;\n\nimport android.content.Context;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/18 14:54\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：RecyclerView的适配器\n */\npublic class RecyclerDemoAdapter extends RecyclerView.Adapter<RecyclerDemoAdapter.MyViewHolder> {\n\n    private final Context mContext;\n    private final ArrayList<String> datas;\n\n    public RecyclerDemoAdapter(Context context, ArrayList<String> datas) {\n        this.mContext = context;\n        this.datas = datas;\n    }\n\n    /**\n     * 得到数据的总数\n     * @return\n     */\n    @Override\n    public int getItemCount() {\n        return datas.size();\n    }\n\n\n\n    /**\n     * 创建ViewHolder\n     * @param parent\n     * @param viewType\n     * @return\n     */\n    @Override\n    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        return new MyViewHolder(View.inflate(mContext,R.layout.item,null));\n    }\n\n    /**\n     * 把数据绑定到ViewHolder上\n     * @param holder\n     * @param position\n     */\n    @Override\n    public void onBindViewHolder(MyViewHolder holder, int position) {\n        //1.根据位置得到数据\n        String data = datas.get(position);\n        //2.绑定数据\n        holder.tv_name.setText(data);\n\n\n    }\n\n\n\n\n    /**\n     * 添加数据\n     * @param data\n     */\n    public void addData(int position,String data) {\n        datas.add(position,data);\n        //刷新数据\n        notifyItemInserted(position);\n    }\n\n    /*\n    删除数据\n     */\n    public void removewData(int positon) {\n        datas.remove(positon);\n        notifyItemRemoved(positon);\n    }\n\n\n    /**\n     * ViewHolder\n     */\n    class MyViewHolder extends RecyclerView.ViewHolder{\n        private ImageView iv_icon;\n        private TextView tv_name;\n\n        public MyViewHolder(View itemView) {\n            super(itemView);\n            iv_icon = (ImageView) itemView.findViewById(R.id.iv_icon);\n            tv_name = (TextView) itemView.findViewById(R.id.tv_name);\n            //设置item的点击事件\n            itemView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    //getLayoutPosition 把你点击的View的对应的位置\n//                    Toast.makeText(mContext, \"data==\"+datas.get(getLayoutPosition()), Toast.LENGTH_SHORT).show();\n                    if(listener != null){\n                        listener.onItemClick(getLayoutPosition());\n                    }\n                }\n            });\n        }\n    }\n\n    /**\n     * item点击的监听器\n     */\n    public interface OnItemClickListener{\n        /**\n         * 单点击item的时候回调\n         * @param postion\n         */\n        public void  onItemClick(int postion);\n    }\n\n    private OnItemClickListener listener;\n\n    /*\n    设置item的点击监听\n     */\n    public void setOnItemClickListener(OnItemClickListener listener) {\n        this.listener = listener;\n    }\n}\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/drawable/divider_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\" >\n\n    <gradient\n        android:centerColor=\"#ff00ff00\"\n        android:endColor=\"#ff0000ff\"\n        android:startColor=\"#ffff0000\"\n        android:type=\"linear\" />\n    <size android:height=\"1dp\"/>\n\n</shape>"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\"com.atguigu.recyclerviewdemo.MainActivity\">\n\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\">\n\n        <Button\n            android:id=\"@+id/btn_add\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"Add\"\n            android:textAllCaps=\"false\" />\n\n        <Button\n            android:id=\"@+id/btn_delete\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"Delete\"\n            android:textAllCaps=\"false\" />\n\n        <Button\n            android:id=\"@+id/btn_List\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"List\"\n            android:textAllCaps=\"false\" />\n\n        <Button\n            android:id=\"@+id/btn_Grid\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"Grid\"\n            android:textAllCaps=\"false\" />\n\n        <Button\n            android:id=\"@+id/btn_flow\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"Flow\"\n            android:textAllCaps=\"false\" />\n    </LinearLayout>\n\n\n\n    <android.support.v7.widget.RecyclerView\n        android:id=\"@+id/recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"/>\n\n</LinearLayout>\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/layout/item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#ffffff\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <ImageView\n            android:id=\"@+id/iv_icon\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:padding=\"3dp\"\n            android:src=\"@drawable/video_default\"/>\n\n        <TextView\n            android:id=\"@+id/tv_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"5dp\"\n            android:layout_marginRight=\"5dp\"\n            android:padding=\"3dp\"\n            android:text=\"内容\"\n            android:textColor=\"#000000\"\n            android:textSize=\"15sp\"/>\n\n    </LinearLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">RecyclerViewDemo</string>\n</resources>\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n\n    </style>\n\n    <!--<item name=\"android:listDivider\">@drawable/divider_bg</item>-->\n\n</resources>\n"
  },
  {
    "path": "RecyclerViewDemo/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "RecyclerViewDemo/src/test/java/com/atguigu/recyclerviewdemo/ExampleUnitTest.java",
    "content": "package com.atguigu.recyclerviewdemo;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "SpeechDemo2/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "SpeechDemo2/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"24.0.3\"\n\n    defaultConfig {\n        applicationId \"com.atguigu.speechdemo2\"\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    compile 'com.android.support:appcompat-v7:24.2.1'\n    testCompile 'junit:junit:4.12'\n    compile files('libs/Msc.jar')\n    compile files('libs/Sunflower.jar')\n}\n"
  },
  {
    "path": "SpeechDemo2/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:\\Android_studio_sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "SpeechDemo2/src/androidTest/java/com/atguigu/speechdemo2/ExampleInstrumentedTest.java",
    "content": "package com.atguigu.speechdemo2;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.atguigu.speechdemo2\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "SpeechDemo2/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.atguigu.speechdemo2\">\n\n    <!--连接网络权限，用于执行云端语音能力 -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <!--获取手机录音机使用权限，听写、识别、语义理解需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>\n    <!--读取网络信息状态 -->\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n    <!--获取当前wifi状态 -->\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>\n    <!--允许程序改变网络连接状态 -->\n    <uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>\n    <!--读取手机信息权限 -->\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>\n    <!--读取联系人权限，上传联系人需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.READ_CONTACTS\"/>\n    <!--外存储写权限， 构建语法需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <!--外存储读权限，构建语法需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n    <!--配置权限，用来记录应用配置信息 -->\n    <uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>\n    <!--手机定位信息，用来为语义等功能提供定位， 提供更精准的服务-->\n    <!--定位信息是敏感信息， 可通过Setting.setLocationEnable(false)关闭定位请求 -->\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>\n\n    <application android:allowBackup=\"true\" android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\" android:supportsRtl=\"true\" android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "SpeechDemo2/src/main/java/com/atguigu/speechdemo2/JsonParser.java",
    "content": "package com.atguigu.speechdemo2;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\nimport org.json.JSONTokener;\n\n/**\n * Json结果解析类\n */\npublic class JsonParser {\n\n\tpublic static String parseIatResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\t// 转写结果词，默认使用第一个结果\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tJSONObject obj = items.getJSONObject(0);\n\t\t\t\tret.append(obj.getString(\"w\"));\n//\t\t\t\t如果需要多候选结果，解析数组其他字段\n//\t\t\t\tfor(int j = 0; j < items.length(); j++)\n//\t\t\t\t{\n//\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n//\t\t\t\t\tret.append(obj.getString(\"w\"));\n//\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t} \n\t\treturn ret.toString();\n\t}\n\t\n\tpublic static String parseGrammarResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tfor(int j = 0; j < items.length(); j++)\n\t\t\t\t{\n\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n\t\t\t\t\tif(obj.getString(\"w\").contains(\"nomatch\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tret.append(\"没有匹配结果.\");\n\t\t\t\t\t\treturn ret.toString();\n\t\t\t\t\t}\n\t\t\t\t\tret.append(\"【结果】\" + obj.getString(\"w\"));\n\t\t\t\t\tret.append(\"【置信度】\" + obj.getInt(\"sc\"));\n\t\t\t\t\tret.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tret.append(\"没有匹配结果.\");\n\t\t} \n\t\treturn ret.toString();\n\t}\n\t\n\tpublic static String parseLocalGrammarResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tfor(int j = 0; j < items.length(); j++)\n\t\t\t\t{\n\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n\t\t\t\t\tif(obj.getString(\"w\").contains(\"nomatch\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tret.append(\"没有匹配结果.\");\n\t\t\t\t\t\treturn ret.toString();\n\t\t\t\t\t}\n\t\t\t\t\tret.append(\"【结果】\" + obj.getString(\"w\"));\n\t\t\t\t\tret.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tret.append(\"【置信度】\" + joResult.optInt(\"sc\"));\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tret.append(\"没有匹配结果.\");\n\t\t} \n\t\treturn ret.toString();\n\t}\n}\n"
  },
  {
    "path": "SpeechDemo2/src/main/java/com/atguigu/speechdemo2/MainActivity.java",
    "content": "package com.atguigu.speechdemo2;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\nimport com.iflytek.cloud.InitListener;\nimport com.iflytek.cloud.RecognizerResult;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechSynthesizer;\nimport com.iflytek.cloud.SpeechUtility;\nimport com.iflytek.cloud.SynthesizerListener;\nimport com.iflytek.cloud.ui.RecognizerDialog;\nimport com.iflytek.cloud.ui.RecognizerDialogListener;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\n\npublic class MainActivity extends AppCompatActivity {\n    // 用HashMap存储听写结果\n    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();\n\n\n    private EditText editText;\n    private Button btn_voice;\n    private Button btn_speechtext;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        SpeechUtility.createUtility(this, SpeechConstant.APPID + \"=5838f0d9\");\n        editText = (EditText) findViewById(R.id.et_input);\n        btn_voice = (Button) findViewById(R.id.btn_voice);\n        btn_speechtext = (Button) findViewById(R.id.btn_speechtext);\n        btn_voice.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                showDialogVoice();\n            }\n        });\n\n        btn_speechtext.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                textToVoice();\n\n            }\n        });\n    }\n\n    private void textToVoice() {\n        //1.创建 SpeechSynthesizer 对象, 第二个参数： 本地合成时传 InitListener\n        SpeechSynthesizer mTts= SpeechSynthesizer.createSynthesizer(this, null);\n//2.合成参数设置，详见《 MSC Reference Manual》 SpeechSynthesizer 类\n//设置发音人（更多在线发音人，用户可参见 附录13.2\n        mTts.setParameter(SpeechConstant.VOICE_NAME, \"xiaoyan\"); //设置发音人\n        mTts.setParameter(SpeechConstant.SPEED, \"50\");//设置语速\n        mTts.setParameter(SpeechConstant.VOLUME, \"80\");//设置音量，范围 0~100\n        mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); //设置云端\n//设置合成音频保存位置（可自定义保存位置），保存在“./sdcard/iflytek.pcm”\n//保存在 SD 卡需要在 AndroidManifest.xml 添加写 SD 卡权限\n//仅支持保存为 pcm 和 wav 格式， 如果不需要保存合成音频，注释该行代码\n        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, \"./sdcard/iflytek.pcm\");\n//3.开始合成\n        mTts.startSpeaking(editText.getText().toString(), mSynListener);\n\n    }\n\n    //合成监听器\n    private SynthesizerListener mSynListener = new SynthesizerListener(){\n        //会话结束回调接口，没有错误时， error为null\n        public void onCompleted(SpeechError error) {}\n//缓冲进度回调\n//percent为缓冲进度0~100， beginPos为缓冲音频在文本中开始位置， endPos表示缓冲音频在\n//        文本中结束位置， info为附加信息。\n        public void onBufferProgress(int percent, int beginPos, int endPos, String info) {}\n        //开始播放\n        public void onSpeakBegin() {}\n        //暂停播放\n        public void onSpeakPaused() {}\n//播放进度回调\n//percent为播放进度0~100,beginPos为播放音频在文本中开始位置， endPos表示播放音频在文\n//        本中结束位置.\n        public void onSpeakProgress(int percent, int beginPos, int endPos) {}\n        //恢复播放回调接口\n        public void onSpeakResumed() {}\n        //会话事件回调接口\n        public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {}\n    };\n\n    private void showDialogVoice() {\n        //1.创建RecognizerDialog对象\n        RecognizerDialog mDialog = new RecognizerDialog(this, new MyInitListener());\n//2.设置accent、 language等参数\n        mDialog.setParameter(SpeechConstant.LANGUAGE, \"zh_cn\");\n        mDialog.setParameter(SpeechConstant.ACCENT, \"mandarin\");\n//若要将UI控件用于语义理解，必须添加以下参数设置，设置之后onResult回调返回将是语义理解\n//结果\n// mDialog.setParameter(\"asr_sch\", \"1\");\n// mDialog.setParameter(\"nlp_version\", \"2.0\");\n//3.设置回调接口\n        mDialog.setListener(new MyRecognizerDialogListener());\n//4.显示dialog，接收语音输入\n        mDialog.show();\n    }\n\n    class MyRecognizerDialogListener implements RecognizerDialogListener {\n\n        @Override\n        public void onResult(RecognizerResult recognizerResult, boolean b) {\n            String result = recognizerResult.getResultString();\n            System.out.println(result);\n            String text = JsonParser.parseIatResult(recognizerResult.getResultString());\n\n            String sn = null;\n            // 读取json结果中的sn字段\n            try {\n                JSONObject resultJson = new JSONObject(recognizerResult.getResultString());\n                sn = resultJson.optString(\"sn\");\n            } catch (JSONException e) {\n                e.printStackTrace();\n            }\n\n            mIatResults.put(sn, text);\n\n            StringBuffer resultBuffer = new StringBuffer();\n            for (String key : mIatResults.keySet()) {\n                resultBuffer.append(mIatResults.get(key));\n            }\n\n            editText.setText(resultBuffer.toString());\n            editText.setSelection(editText.length());\n\n        }\n\n        @Override\n        public void onError(SpeechError speechError) {\n\n            Toast.makeText(MainActivity.this, \"出错了哦\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    class MyInitListener implements InitListener {\n\n        @Override\n        public void onInit(int i) {\n\n\n        }\n    }\n}\n"
  },
  {
    "path": "SpeechDemo2/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_main\"\n    android:layout_width=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"com.atguigu.speechdemo2.MainActivity\">\n\n    <EditText\n        android:id=\"@+id/et_input\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:hint=\"Hello World!\" />\n\n    <Button\n        android:id=\"@+id/btn_voice\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"语音转文字\" />\n\n    <Button\n        android:id=\"@+id/btn_speechtext\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"文字转语音\" />\n</LinearLayout>\n"
  },
  {
    "path": "SpeechDemo2/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "SpeechDemo2/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "SpeechDemo2/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">SpeechDemo2</string>\n</resources>\n"
  },
  {
    "path": "SpeechDemo2/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "SpeechDemo2/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "SpeechDemo2/src/test/java/com/atguigu/speechdemo2/ExampleUnitTest.java",
    "content": "package com.atguigu.speechdemo2;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "StartAllVideoPlayer/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "StartAllVideoPlayer/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"24.0.3\"\n\n    defaultConfig {\n        applicationId \"com.atguigu.startallvideoplayer\"\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    compile 'com.android.support:appcompat-v7:24.2.1'\n    testCompile 'junit:junit:4.12'\n}\n"
  },
  {
    "path": "StartAllVideoPlayer/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:\\Android_studio_sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "StartAllVideoPlayer/src/androidTest/java/com/atguigu/startallvideoplayer/ExampleInstrumentedTest.java",
    "content": "package com.atguigu.startallvideoplayer;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.atguigu.startallvideoplayer\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "StartAllVideoPlayer/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.atguigu.startallvideoplayer\">\n\n    <application android:allowBackup=\"true\" android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\" android:supportsRtl=\"true\" android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "StartAllVideoPlayer/src/main/java/com/atguigu/startallvideoplayer/MainActivity.java",
    "content": "package com.atguigu.startallvideoplayer;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\n\npublic class MainActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n    }\n\n    public void startAllVideo(View view) {\n        Intent intent = new Intent();\n//            //第一参数：播放路径\n//            //第二参数：路径对应的类型\n        intent.setDataAndType(Uri.parse(\"http://10.0.2.2:8080/rmvb.rmvbd\"), \"video/*\");\n//        intent.setDataAndType(Uri.parse(\"http://10.0.2.2:8080/yellow.mp4\"), \"video/*\");\n//        intent.setDataAndType(Uri.parse(\"http://192.168.1.30:8080/yellow.mp4\"), \"video/*\");\n//        intent.setDataAndType(Uri.parse(\"http://192.168.191.1:8080/yellow.mp4\"), \"video/*\");\n//        intent.setDataAndType(Uri.parse(\"http://vfx.mtime.cn/Video/2016/12/29/mp4/161229134943070513_480.mp4\"), \"video/*\");\n        startActivity(intent);\n    }\n}\n"
  },
  {
    "path": "StartAllVideoPlayer/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.atguigu.startallvideoplayer.MainActivity\">\n\n    <Button\n        android:onClick=\"startAllVideo\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"调用系统播放器\" />\n</RelativeLayout>\n"
  },
  {
    "path": "StartAllVideoPlayer/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "StartAllVideoPlayer/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "StartAllVideoPlayer/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">StartAllVideoPlayer</string>\n</resources>\n"
  },
  {
    "path": "StartAllVideoPlayer/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "StartAllVideoPlayer/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "StartAllVideoPlayer/src/test/java/com/atguigu/startallvideoplayer/ExampleUnitTest.java",
    "content": "package com.atguigu.startallvideoplayer;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "SurfaceViewDemo/.gitignore",
    "content": ".gradle\n.idea\nbuild\n*.iml\n/local.properties\n/bin"
  },
  {
    "path": "SurfaceViewDemo/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.3\"\n\n    defaultConfig {\n        applicationId \"com.example.videotest\"\n        minSdkVersion 14\n        targetSdkVersion 23\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n        }\n    }\n}\n\ndependencies {\n    compile 'com.android.support:support-v4:23.3.0'\n}\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.example.videotest\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"14\"\n        android:targetSdkVersion=\"21\" />\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n\t\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@android:style/Theme.Holo.NoActionBar.Fullscreen\" >\n        <activity\n            android:name=\"com.example.videotest.MainActivity\"\n            android:label=\"@string/app_name\"\n            android:screenOrientation=\"sensor\" >\n        </activity>\n        <activity\n            android:name=\"com.example.videotest.SurfaceViewTestActivity\"\n\t\t\tandroid:screenOrientation=\"sensorLandscape\"\n            android:configChanges=\"keyboardHidden|orientation|screenSize\" >\n           \n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "SurfaceViewDemo/src/main/java/com/example/videotest/Constants.java",
    "content": "package com.example.videotest;\n\nimport android.graphics.Canvas;\n\n/**\n * @author shenxiaolei\n *\n */\npublic class Constants {\n\n    /**\n     * 记录播放位置\n     */\n    public static int playPosition=-1;\n    \n    private static  Canvas canvas;\n\n    public static Canvas getCanvas() {\n        return canvas;\n    }\n\n    public static void setCanvas(Canvas canvas) {\n        Constants.canvas = canvas;\n    }\n    \n    \n}\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/java/com/example/videotest/MainActivity.java",
    "content": "package com.example.videotest;\n\nimport android.app.Activity;\nimport android.media.MediaPlayer;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.MediaController;\nimport android.widget.ProgressBar;\nimport android.widget.RelativeLayout;\nimport android.widget.VideoView;\n\n\npublic class MainActivity extends Activity implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, MediaPlayer.OnPreparedListener, View.OnTouchListener {\n\n    /**\n     * View播放\n     */\n    private VideoView videoView;\n\n    /**\n     * 加载预览进度条\n     */\n    private ProgressBar progressBar;\n\n    /**\n     * 设置view播放控制条\n     */\n    private MediaController mediaController;\n\n    /**\n     * 标记当视频暂停时播放位置\n     */\n    private int intPositionWhenPause=-1;\n\n    /**\n     * 设置窗口模式下的videoview的宽度\n     */\n    private int videoWidth;\n\n    /**\n     * 设置窗口模式下videoview的高度\n     */\n    private int videoHeight;\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        initVideoView();\n\n//        Uri uri=Uri.parse(\"http://123.150.52.227/0725695b00000000-1415769042-1960016430/data5/vkplx.video.qq.com/flv/74/164/a0015193bxf.p203.1.mp4\");\n//        Intent intent=new Intent(Intent.ACTION_VIEW);\n//        intent.setDataAndType(uri,\"video/*\");\n//        startActivity(intent);\n    }\n    /**\n     *初始化videoview播放\n     */\n    public void initVideoView(){\n        //初始化进度条\n        progressBar= (ProgressBar) findViewById(R.id.progressBar);\n        //初始化VideoView\n        videoView= (VideoView) findViewById(R.id.videoView);\n        //初始化videoview控制条\n        mediaController=new MediaController(this);\n        //设置videoview的控制条\n        videoView.setMediaController(mediaController);\n        //设置显示控制条\n        mediaController.show(0);\n        //设置播放完成以后监听\n        videoView.setOnCompletionListener(this);\n        //设置发生错误监听，如果不设置videoview会向用户提示发生错误\n        videoView.setOnErrorListener(this);\n        //设置在视频文件在加载完毕以后的回调函数\n        videoView.setOnPreparedListener(this);\n        //设置videoView的点击监听\n        videoView.setOnTouchListener(this);\n        //设置网络视频路径\n//        http://123.150.52.227/0725695b00000000-1415769042-1960016430/data5/vkplx.video.qq.com/flv/74/164/a0015193bxf.p203.1.mp4\n//        http://120.41.1.158/vkplx.video.qq.com/flv/74/164/a0015193bxf.p203.1.mp4\n//        http://vkplx.video.qq.com/flv/74/164/a0015193bxf.p203.1.mp4\n//        http://221.204.220.17//data5/cdn_transfer/36/4C/364f19b5662e054057fc9851703a03f3b0f4ea4c.flv\n//        http://106.38.249.144/youku/67739CD2F943E7AB77EFF6511/03000810005429C7E703C905CF07DD613756C8-634B-EB1E-9628-F2F38A38C261.mp4\n        Uri uri=Uri.parse(\"http://10.0.2.2:8080/oppo.mp4\");\n        videoView.setVideoURI(uri);\n        //设置为全屏模式播放\n        setVideoViewLayoutParams(1);\n    }\n    @Override\n    protected void onStart() {\n        super.onStart();\n        //启动视频播放\n        videoView.start();\n        //设置获取焦点\n        videoView.setFocusable(true);\n\n    }\n    /**\n     * 设置videiview的全屏和窗口模式\n     * @param paramsType 标识 1为全屏模式 2为窗口模式\n     */\n    public void setVideoViewLayoutParams(int paramsType){\n\n        if(1==paramsType) {\n            RelativeLayout.LayoutParams LayoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);\n            LayoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);\n            LayoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);\n            LayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);\n            LayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);\n            videoView.setLayoutParams(LayoutParams);\n        }else{\n            //动态获取宽高\n            DisplayMetrics DisplayMetrics=new DisplayMetrics();\n            this.getWindowManager().getDefaultDisplay().getMetrics(DisplayMetrics);\n            videoHeight=DisplayMetrics.heightPixels-50;\n            videoWidth=DisplayMetrics.widthPixels-50;\n            RelativeLayout.LayoutParams LayoutParams = new RelativeLayout.LayoutParams(videoWidth,videoHeight);\n            LayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);\n            videoView.setLayoutParams(LayoutParams);\n        }\n\n    }\n    /**\n     * 视频播放完成以后调用的回调函数\n     */\n    @Override\n    public void onCompletion(MediaPlayer mp) {\n\n    }\n    /**\n     * 视频播放发生错误时调用的回调函数\n     */\n    @Override\n    public boolean onError(MediaPlayer mp, int what, int extra) {\n        switch (what){\n            case MediaPlayer.MEDIA_ERROR_UNKNOWN:\n                Log.e(\"text\",\"发生未知错误\");\n\n                break;\n            case MediaPlayer.MEDIA_ERROR_SERVER_DIED:\n                Log.e(\"text\",\"媒体服务器死机\");\n                break;\n            default:\n                Log.e(\"text\",\"onError+\"+what);\n                break;\n        }\n        switch (extra){\n            case MediaPlayer.MEDIA_ERROR_IO:\n                //io读写错误\n                Log.e(\"text\",\"文件或网络相关的IO操作错误\");\n                break;\n            case MediaPlayer.MEDIA_ERROR_MALFORMED:\n                //文件格式不支持\n                Log.e(\"text\",\"比特流编码标准或文件不符合相关规范\");\n                break;\n            case MediaPlayer.MEDIA_ERROR_TIMED_OUT:\n                //一些操作需要太长时间来完成,通常超过3 - 5秒。\n                Log.e(\"text\",\"操作超时\");\n                break;\n            case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:\n                //比特流编码标准或文件符合相关规范,但媒体框架不支持该功能\n                Log.e(\"text\",\"比特流编码标准或文件符合相关规范,但媒体框架不支持该功能\");\n                break;\n            default:\n                Log.e(\"text\",\"onError+\"+extra);\n                break;\n        }\n        //如果未指定回调函数， 或回调函数返回假，VideoView 会通知用户发生了错误。\n        return false;\n    }\n\n    /**\n     * 视频文件加载文成后调用的回调函数\n     * @param mp\n     */\n    @Override\n    public void onPrepared(MediaPlayer mp) {\n     //如果文件加载成功,隐藏加载进度条\n        progressBar.setVisibility(View.GONE);\n\n    }\n\n    /**\n     * 对videoView的触摸监听\n     * @param v\n     * @param event\n     * @return\n     */\n    @Override\n    public boolean onTouch(View v, MotionEvent event) {\n        return false;\n    }\n\n\n\n    /**\n     * 页面暂停效果处理\n     */\n    @Override\n    protected  void onPause() {\n        super.onPause();\n        //如果当前页面暂定则保存当前播放位置，并暂停\n        intPositionWhenPause=videoView.getCurrentPosition();\n        //停止回放视频文件\n        videoView.stopPlayback();\n    }\n\n    /**\n     * 页面从暂停中恢复\n     */\n    @Override\n    protected void onResume() {\n        super.onResume();\n       //跳转到暂停时保存的位置\n        if(intPositionWhenPause>=0){\n            videoView.seekTo(intPositionWhenPause);\n            //初始播放位置\n            intPositionWhenPause=-1;\n        }\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        if(null!=videoView){\n            videoView=null;\n        }\n    }\n}\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/java/com/example/videotest/MediaPlayUtil.java",
    "content": "\npackage com.example.videotest;\n\nimport android.media.MediaPlayer;\n\npublic class MediaPlayUtil {\n\n    /**\n     * 播放器\n     */\n    private static MediaPlayer mediaPlayer;\n    \n    /**\n     * 静态的播放路径\n     */\n    private static String pathString;\n    \n    \n    public static void getInitanse(String path){\n        if(null==mediaPlayer){\n           synchronized(MediaPlayUtil.class){\n               if(null==mediaPlayer){\n                   \n               }\n           }\n        }else{\n            \n        }\n    }\n}\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/java/com/example/videotest/SurfaceViewTestActivity.java",
    "content": "\npackage com.example.videotest;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.res.Configuration;\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.CompressFormat;\nimport android.graphics.BitmapFactory;\nimport android.media.AudioManager;\nimport android.media.MediaMetadataRetriever;\nimport android.media.MediaPlayer;\nimport android.media.MediaPlayer.OnBufferingUpdateListener;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceView;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.ViewGroup.LayoutParams;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.ProgressBar;\nimport android.widget.RelativeLayout;\nimport android.widget.SeekBar;\nimport android.widget.SeekBar.OnSeekBarChangeListener;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\n\npublic class SurfaceViewTestActivity extends Activity implements MediaPlayer.OnCompletionListener,\n        MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, OnBufferingUpdateListener,\n        OnClickListener {\n\n    /**\n     *\n     */\n    private SurfaceView surfaceView;\n\n    /**\n     * surfaceView播放控制\n     */\n    private SurfaceHolder surfaceHolder;\n\n    /**\n     * 播放控制条\n     */\n    private SeekBar seekBar;\n\n    /**\n     * 暂停播放按钮\n     */\n    private Button playButton;\n\n    /**\n     * 重新播放按钮\n     */\n    private Button replayButton;\n\n    /**\n     * 截图按钮\n     */\n    private Button screenShotButton;\n\n    /**\n     * 改变视频大小button\n     */\n    private Button videoSizeButton;\n\n    /**\n     * 加载进度显示条\n     */\n    private ProgressBar progressBar;\n\n    /**\n     * 播放视频\n     */\n    private MediaPlayer mediaPlayer;\n\n    /**\n     * 记录当前播放的位置\n     */\n    private int playPosition = -1;\n\n    /**\n     * seekBar是否自动拖动\n     */\n    private boolean seekBarAutoFlag = false;\n\n    /**\n     * 视频时间显示\n     */\n    private TextView vedioTiemTextView;\n\n    /**\n     * 播放总时间\n     */\n    private String videoTimeString;\n\n    private long videoTimeLong;\n\n    /**\n     * 播放路径\n     */\n    private String pathString = \"http://10.0.2.2:8080/oppo.mp4\";\n\n    /**\n     * 屏幕的宽度和高度\n     */\n    private int screenWidth, screenHeight;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_surface_view_test);\n        // 获取屏幕的宽度和高度\n        DisplayMetrics displayMetrics = new DisplayMetrics();\n        this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);\n        screenWidth = displayMetrics.widthPixels;\n        screenHeight = displayMetrics.heightPixels;\n        initViews();\n    }\n\n    public void initViews() {\n        String path = null;\n\n        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {\n            // 存在获取外部文件路径\n            path = Environment.getExternalStorageDirectory().getPath();\n        } else {\n            // 不存在获取内部存储\n            path = Environment.getDataDirectory().getPath();\n        }\n//        pathString = path + \"/shen/x0200hkt1cg.p202.1.mp4\";\n        // 初始化控件\n        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);\n        progressBar = (ProgressBar) findViewById(R.id.progressBar);\n        seekBar = (SeekBar) findViewById(R.id.seekbar);\n        playButton = (Button) findViewById(R.id.button_play);\n        replayButton = (Button) findViewById(R.id.button_replay);\n        vedioTiemTextView = (TextView) findViewById(R.id.textView_showTime);\n        screenShotButton = (Button) findViewById(R.id.button_screenShot);\n        videoSizeButton = (Button) findViewById(R.id.button_videoSize);\n        // 设置surfaceHolder\n        surfaceHolder = surfaceView.getHolder();\n        // 设置Holder类型,该类型表示surfaceView自己不管理缓存区,虽然提示过时，但最好还是要设置\n        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);\n        // 设置surface回调\n        surfaceHolder.addCallback(new SurfaceCallback());\n\n    }\n\n    // SurfaceView的callBack\n    private class SurfaceCallback implements SurfaceHolder.Callback {\n        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {\n\n        }\n\n        public void surfaceCreated(SurfaceHolder holder) {\n            // surfaceView被创建\n            // 设置播放资源\n            playVideo();\n        }\n\n        public void surfaceDestroyed(SurfaceHolder holder) {\n            // surfaceView销毁,同时销毁mediaPlayer\n            if (null != mediaPlayer) {\n                mediaPlayer.release();\n                mediaPlayer = null;\n            }\n\n        }\n\n    }\n\n    /**\n     * 播放视频\n     */\n    public void playVideo() {\n        // 初始化MediaPlayer\n        mediaPlayer = new MediaPlayer();\n        // 重置mediaPaly,建议在初始滑mediaplay立即调用。\n        mediaPlayer.reset();\n        // 设置声音效果\n        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);\n        // 设置播放完成监听\n        mediaPlayer.setOnCompletionListener(this);\n        // 设置媒体加载完成以后回调函数。\n        mediaPlayer.setOnPreparedListener(this);\n        // 错误监听回调函数\n        mediaPlayer.setOnErrorListener(this);\n        // 设置缓存变化监听\n        mediaPlayer.setOnBufferingUpdateListener(this);\n        try {\n            // mediaPlayer.reset();\n            mediaPlayer.setDataSource(pathString);\n            // mediaPlayer.setDataSource(this, uri);\n            // mediaPlayer.setDataSource(SurfaceViewTestActivity.this, uri);\n            // 设置异步加载视频，包括两种方式 prepare()同步，prepareAsync()异步\n            mediaPlayer.prepareAsync();\n        } catch (IOException e) {\n            e.printStackTrace();\n            Toast.makeText(this, \"加载视频错误！\", Toast.LENGTH_LONG).show();\n        }\n    }\n\n    /**\n     * 视频加载完毕监听\n     * \n     * @param mp\n     */\n    @Override\n    public void onPrepared(MediaPlayer mp) {\n        // 当视频加载完毕以后，隐藏加载进度条\n        progressBar.setVisibility(View.GONE);\n        // 判断是否有保存的播放位置,防止屏幕旋转时，界面被重新构建，播放位置丢失。\n        if (Constants.playPosition >= 0) {\n            mediaPlayer.seekTo(Constants.playPosition);\n            Constants.playPosition = -1;\n            // surfaceHolder.unlockCanvasAndPost(Constants.getCanvas());\n        }\n        seekBarAutoFlag = true;\n        // 设置控制条,放在加载完成以后设置，防止获取getDuration()错误\n        seekBar.setMax(mediaPlayer.getDuration());\n        // 设置播放时间\n        videoTimeLong = mediaPlayer.getDuration();\n        videoTimeString = getShowTime(videoTimeLong);\n        vedioTiemTextView.setText(\"00:00:00/\" + videoTimeString);\n        // 设置拖动监听事件\n        seekBar.setOnSeekBarChangeListener(new SeekBarChangeListener());\n        // 设置按钮监听事件\n        // 重新播放\n        replayButton.setOnClickListener(SurfaceViewTestActivity.this);\n        // 暂停和播放\n        playButton.setOnClickListener(SurfaceViewTestActivity.this);\n        // 截图按钮\n        screenShotButton.setOnClickListener(SurfaceViewTestActivity.this);\n        // 视频大小\n        videoSizeButton.setOnClickListener(SurfaceViewTestActivity.this);\n        // 播放视频\n        mediaPlayer.start();\n        // 设置显示到屏幕\n        mediaPlayer.setDisplay(surfaceHolder);\n        // 开启线程 刷新进度条\n        new Thread(runnable).start();\n        // 设置surfaceView保持在屏幕上\n        mediaPlayer.setScreenOnWhilePlaying(true);\n        surfaceHolder.setKeepScreenOn(true);\n\n    }\n\n    /**\n     * 滑动条变化线程\n     */\n    private Runnable runnable = new Runnable() {\n\n        public void run() {\n            // TODO Auto-generated method stub\n            // 增加对异常的捕获，防止在判断mediaPlayer.isPlaying的时候，报IllegalStateException异常\n            try {\n                while (seekBarAutoFlag) {\n                    /*\n                     * mediaPlayer不为空且处于正在播放状态时，使进度条滚动。\n                     * 通过指定类名的方式判断mediaPlayer防止状态发生不一致\n                     */\n\n                    if (null != SurfaceViewTestActivity.this.mediaPlayer\n                            && SurfaceViewTestActivity.this.mediaPlayer.isPlaying()) {\n                        seekBar.setProgress(mediaPlayer.getCurrentPosition());\n                    }\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n\n            }\n        }\n    };\n\n    /**\n     * seekBar拖动监听类\n     * \n     * @author shenxiaolei\n     */\n    @SuppressWarnings(\"unused\")\n    private class SeekBarChangeListener implements OnSeekBarChangeListener {\n\n        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n            // TODO Auto-generated method stub\n            if (progress >= 0) {\n                // 如果是用户手动拖动控件，则设置视频跳转。\n                if (fromUser) {\n                    mediaPlayer.seekTo(progress);\n                }\n                // 设置当前播放时间\n                vedioTiemTextView.setText(getShowTime(progress) + \"/\" + videoTimeString);\n            }\n        }\n\n        public void onStartTrackingTouch(SeekBar seekBar) {\n            // TODO Auto-generated method stub\n\n        }\n\n        public void onStopTrackingTouch(SeekBar seekBar) {\n            // TODO Auto-generated method stub\n\n        }\n\n    }\n\n    /**\n     * 按钮点击事件监听\n     */\n    public void onClick(View v) {\n        // TODO Auto-generated method stub\n        // 重新播放\n        if (v == replayButton) {\n            // mediaPlayer不空，则直接跳转\n            if (null != mediaPlayer) {\n                // MediaPlayer和进度条都跳转到开始位置\n                mediaPlayer.seekTo(0);\n                seekBar.setProgress(0);\n                // 如果不处于播放状态，则开始播放\n                if (!mediaPlayer.isPlaying()) {\n                    mediaPlayer.start();\n                }\n            } else {\n                // 为空则重新设置mediaPlayer\n                playVideo();\n            }\n\n        }\n        // 播放、暂停按钮\n        if (v == playButton) {\n            if (null != mediaPlayer) {\n                // 正在播放\n                if (mediaPlayer.isPlaying()) {\n                    Constants.playPosition = mediaPlayer.getCurrentPosition();\n                    // seekBarAutoFlag = false;\n                    mediaPlayer.pause();\n                    playButton.setText(\"播放\");\n                } else {\n                    if (Constants.playPosition >= 0) {\n                        // seekBarAutoFlag = true;\n                        mediaPlayer.seekTo(Constants.playPosition);\n                        mediaPlayer.start();\n                        playButton.setText(\"暂停\");\n                        Constants.playPosition = -1;\n                    }\n                }\n\n            }\n        }\n        // 视频截图\n        if (v == screenShotButton) {\n            if (null != mediaPlayer) {\n                // 视频正在播放，\n                if (mediaPlayer.isPlaying()) {\n                    // 获取播放位置\n                    Constants.playPosition = mediaPlayer.getCurrentPosition();\n                    // 暂停播放\n                    mediaPlayer.pause();\n                    //\n                    playButton.setText(\"播放\");\n                }\n                // 视频截图\n                savaScreenShot(Constants.playPosition);\n            } else {\n                Toast.makeText(SurfaceViewTestActivity.this, \"视频暂未播放！\", Toast.LENGTH_SHORT).show();\n            }\n        }\n        if (v == videoSizeButton) {\n            // 调用改变大小的方法\n            changeVideoSize();\n        }\n    }\n\n    /**\n     * 播放完毕监听\n     * \n     * @param mp\n     */\n    @Override\n    public void onCompletion(MediaPlayer mp) {\n        // 设置seeKbar跳转到最后位置\n        seekBar.setProgress(Integer.parseInt(String.valueOf(videoTimeLong)));\n        // 设置播放标记为false\n        seekBarAutoFlag = false;\n    }\n\n    /**\n     * 视频缓存大小监听,当视频播放以后 在started状态会调用\n     */\n    public void onBufferingUpdate(MediaPlayer mp, int percent) {\n        // TODO Auto-generated method stub\n        // percent 表示缓存加载进度，0为没开始，100表示加载完成，在加载完成以后也会一直调用该方法\n        Log.e(\"text\", \"onBufferingUpdate-->\" + percent);\n        // 可以根据大小的变化来\n    }\n\n    /**\n     * 错误监听\n     * \n     * @param mp\n     * @param what\n     * @param extra\n     * @return\n     */\n    @Override\n    public boolean onError(MediaPlayer mp, int what, int extra) {\n        switch (what) {\n            case MediaPlayer.MEDIA_ERROR_UNKNOWN:\n                Toast.makeText(this, \"MEDIA_ERROR_UNKNOWN\", Toast.LENGTH_SHORT).show();\n                break;\n            case MediaPlayer.MEDIA_ERROR_SERVER_DIED:\n                Toast.makeText(this, \"MEDIA_ERROR_SERVER_DIED\", Toast.LENGTH_SHORT).show();\n                break;\n            default:\n                break;\n        }\n\n        switch (extra) {\n            case MediaPlayer.MEDIA_ERROR_IO:\n                Toast.makeText(this, \"MEDIA_ERROR_IO\", Toast.LENGTH_SHORT).show();\n                break;\n            case MediaPlayer.MEDIA_ERROR_MALFORMED:\n                Toast.makeText(this, \"MEDIA_ERROR_MALFORMED\", Toast.LENGTH_SHORT).show();\n                break;\n            case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:\n                Toast.makeText(this, \"MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK\",\n                        Toast.LENGTH_SHORT).show();\n                break;\n            case MediaPlayer.MEDIA_ERROR_TIMED_OUT:\n                Toast.makeText(this, \"MEDIA_ERROR_TIMED_OUT\", Toast.LENGTH_SHORT).show();\n                break;\n            case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:\n                Toast.makeText(this, \"MEDIA_ERROR_UNSUPPORTED\", Toast.LENGTH_SHORT).show();\n                break;\n        }\n        return false;\n    }\n\n    /**\n     * 从暂停中恢复\n     */\n    protected void onResume() {\n        // TODO Auto-generated method stub\n        super.onResume();\n        // 判断播放位置\n        if (Constants.playPosition >= 0) {\n\n            if (null != mediaPlayer) {\n                seekBarAutoFlag = true;\n                mediaPlayer.seekTo(Constants.playPosition);\n                mediaPlayer.start();\n            } else {\n                playVideo();\n            }\n\n        }\n    }\n\n    /**\n     * 页面处于暂停状态\n     */\n    @Override\n    protected void onPause() {\n        super.onPause();\n        try {\n            if (null != mediaPlayer && mediaPlayer.isPlaying()) {\n                Constants.playPosition = mediaPlayer.getCurrentPosition();\n                mediaPlayer.pause();\n                seekBarAutoFlag = false;\n            }\n        } catch (Exception e) {\n            // TODO: handle exception\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * 发生屏幕旋转时调用\n     */\n    protected void onSaveInstanceState(Bundle outState) {\n        // TODO Auto-generated method stub\n        super.onSaveInstanceState(outState);\n        if (null != mediaPlayer) {\n            // 保存播放位置\n            Constants.playPosition = mediaPlayer.getCurrentPosition();\n        }\n    }\n\n    /**\n     * 屏幕旋转完成时调用\n     */\n    protected void onRestoreInstanceState(Bundle savedInstanceState) {\n        // TODO Auto-generated method stub\n        super.onRestoreInstanceState(savedInstanceState);\n\n    }\npublic void onConfigurationChanged(Configuration newConfig) {\n    // TODO Auto-generated method stub\n    super.onConfigurationChanged(newConfig);\n}\n    /**\n     * 屏幕销毁时调用\n     */\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        // 由于MediaPlay非常占用资源，所以建议屏幕当前activity销毁时，则直接销毁\n        try {\n            if (null != SurfaceViewTestActivity.this.mediaPlayer) {\n                // 提前标志为false,防止在视频停止时，线程仍在运行。\n                seekBarAutoFlag = false;\n                // 如果正在播放，则停止。\n                if (mediaPlayer.isPlaying()) {\n                    mediaPlayer.stop();\n                }\n                Constants.playPosition = -1;\n                // 释放mediaPlayer\n                SurfaceViewTestActivity.this.mediaPlayer.release();\n                SurfaceViewTestActivity.this.mediaPlayer = null;\n            }\n        } catch (Exception e) {\n            // TODO: handle exception\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * 转换播放时间\n     * \n     * @param milliseconds 传入毫秒值\n     * @return 返回 hh:mm:ss或mm:ss格式的数据\n     */\n    @SuppressLint(\"SimpleDateFormat\")\n    public String getShowTime(long milliseconds) {\n        // 获取日历函数\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTimeInMillis(milliseconds);\n        SimpleDateFormat dateFormat = null;\n        // 判断是否大于60分钟，如果大于就显示小时。设置日期格式\n        if (milliseconds / 60000 > 60) {\n            dateFormat = new SimpleDateFormat(\"hh:mm:ss\");\n        } else {\n            dateFormat = new SimpleDateFormat(\"mm:ss\");\n        }\n        return dateFormat.format(calendar.getTime());\n    }\n\n    /**\n     * 保存视频截图.该方法只能支持本地视频文件\n     * \n     * @param time 视频当前位置\n     */\n    public void savaScreenShot(long time) {\n        // 标记是否保存成功\n        boolean isSave = false;\n        // 获取文件路径\n        String path = null;\n        // 文件名称\n        String fileName = null;\n        if (time >= 0) {\n            try {\n                MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();\n                mediaMetadataRetriever.setDataSource(pathString);\n                // 获取视频的播放总时长单位为毫秒\n                String timeString = mediaMetadataRetriever\n                        .extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);\n                // 转换格式为微秒\n                long timelong = Long.parseLong(timeString) * 1000;\n                // 计算当前视频截取的位置\n                long index = (time * timelong) / mediaPlayer.getDuration();\n                // 获取当前视频指定位置的截图,时间参数的单位是微秒,做了*1000处理\n                // 第二个参数为指定位置，意思接近的位置截图\n                Bitmap bitmap = mediaMetadataRetriever.getFrameAtTime(time * 1000,\n                        MediaMetadataRetriever.OPTION_CLOSEST_SYNC);\n                // 释放资源\n                mediaMetadataRetriever.release();\n                // 判断外部设备SD卡是否存在\n                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {\n                    // 存在获取外部文件路径\n                    path = Environment.getExternalStorageDirectory().getPath();\n                } else {\n                    // 不存在获取内部存储\n                    path = Environment.getDataDirectory().getPath();\n                }\n                // 设置文件名称 ，以事件毫秒为名称\n                fileName = Calendar.getInstance().getTimeInMillis() + \".jpg\";\n                // 设置保存文件\n                File file = new File(path + \"/shen/\" + fileName);\n\n                if (!file.exists()) {\n                    file.createNewFile();\n                }\n                FileOutputStream fileOutputStream = new FileOutputStream(file);\n                bitmap.compress(CompressFormat.JPEG, 100, fileOutputStream);\n                isSave = true;\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n            // 保存成功以后，展示图片\n            if (isSave) {\n                ImageView imageView = new ImageView(this);\n                imageView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,\n                        LayoutParams.WRAP_CONTENT));\n                imageView.setImageBitmap(BitmapFactory.decodeFile(path + \"/shen/\" + fileName));\n                new AlertDialog.Builder(this).setView(imageView).show();\n            }\n        }\n\n    }\n\n    /**\n     * 改变视频的显示大小，全屏，窗口，内容\n     */\n    public void changeVideoSize() {\n        // 改变视频大小\n        String videoSizeString = videoSizeButton.getText().toString();\n        // 获取视频的宽度和高度\n        int width = mediaPlayer.getVideoWidth();\n        int height = mediaPlayer.getVideoHeight();\n        // 如果按钮文字为窗口则设置为窗口模式\n        if (\"窗口\".equals(videoSizeString)) {\n            /*\n             * 如果为全屏模式则改为适应内容的，前提是视频宽高小于屏幕宽高，如果大于宽高 我们要做缩放\n             * 如果视频的宽高度有一方不满足我们就要进行缩放. 如果视频的大小都满足就直接设置并居中显示。\n             */\n            if (width > screenWidth || height > screenHeight) {\n                // 计算出宽高的倍数\n                float vWidth = (float) width / (float) screenWidth;\n                float vHeight = (float) height / (float) screenHeight;\n                // 获取最大的倍数值，按大数值进行缩放\n                float max = Math.max(vWidth, vHeight);\n                // 计算出缩放大小,取接近的正值\n                width = (int) Math.ceil((float) width / max);\n                height = (int) Math.ceil((float) height / max);\n            }\n            // 设置SurfaceView的大小并居中显示\n            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width,\n                    height);\n            layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);\n            surfaceView.setLayoutParams(layoutParams);\n            videoSizeButton.setText(\"全屏\");\n        } else if (\"全屏\".equals(videoSizeString)) {\n            // 设置全屏\n            // 设置SurfaceView的大小并居中显示\n            RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(screenWidth,\n                    screenHeight);\n            layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);\n            surfaceView.setLayoutParams(layoutParams);\n            videoSizeButton.setText(\"窗口\");\n        }\n    }\n}\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/res/layout/activity_main.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".MainActivity\">\n\n    <VideoView\n        android:id=\"@+id/videoView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n    <ProgressBar\n        android:id=\"@+id/progressBar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n</RelativeLayout>\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/res/layout/activity_surface_view_test.xml",
    "content": "<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"com.example.shenxiaolei.myapplication.SurfaceViewTestActivity\" >\n\n    <SurfaceView\n        android:id=\"@+id/surfaceView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <ProgressBar\n        android:id=\"@+id/progressBar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"#88000000\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"5dp\"\n        android:paddingRight=\"5dp\" >\n\n        <Button\n            android:id=\"@+id/button_play\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"暂停\" />\n\n        <Button\n            android:id=\"@+id/button_replay\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_toRightOf=\"@id/button_play\"\n            android:text=\"重播\" />\n\n        <Button\n            android:id=\"@+id/button_screenShot\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_toRightOf=\"@id/button_replay\"\n            android:text=\"截图\" />\n\n        <Button\n            android:id=\"@+id/button_videoSize\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_toRightOf=\"@id/button_screenShot\"\n            android:text=\"窗口\" />\n\n        <TextView\n            android:id=\"@+id/textView_showTime\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:textColor=\"#ffffff\" />\n\n        <SeekBar\n            android:id=\"@+id/seekbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toLeftOf=\"@id/textView_showTime\"\n            android:layout_toRightOf=\"@id/button_videoSize\" />\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "SurfaceViewDemo/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">VideoTest</string>\n    <string name=\"hello_world\">Hello world!</string>\n\n</resources>\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme, dependent on API level. This theme is replaced\n        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Light\">\n        <!--\n            Theme customizations available in newer API levels can go in\n            res/values-vXX/styles.xml, while customizations related to\n            backward-compatibility can go here.\n        -->\n    </style>\n\n    <!-- Application theme. -->\n    <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n        <!-- All customizations that are NOT specific to a particular API-level can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/res/values-v11/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme for API 11+. This theme completely replaces\n        AppBaseTheme from res/values/styles.xml on API 11+ devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Holo.Light\">\n        <!-- API 11 theme customizations can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "SurfaceViewDemo/src/main/res/values-v14/styles.xml",
    "content": "<resources>\n\n    <!--\n        Base application theme for API 14+. This theme completely replaces\n        AppBaseTheme from BOTH res/values/styles.xml and\n        res/values-v11/styles.xml on API 14+ devices.\n    -->\n    <style name=\"AppBaseTheme\" parent=\"android:Theme.Holo.Light.DarkActionBar\">\n        <!-- API 14 theme customizations can go here. -->\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"24.0.3\"\n    defaultConfig {\n        applicationId \"com.atguigu.mobileplayer1020\"\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    compile 'com.android.support:appcompat-v7:24.2.1'\n    testCompile 'junit:junit:4.12'\n    compile project(':vitamio')\n    compile project(':xutils')\n    compile project(':MaterialRefresh_library')\n    //    compile files('libs/glide-3.7.0.jar')\n    compile 'com.github.bumptech.glide:glide:3.5.2'\n    compile files('libs/picasso-2.5.2.jar')\n    //    compile project(':EventBus')\n    compile 'org.greenrobot:eventbus:3.0.0'\n    compile files('libs/Sunflower.jar')\n    compile files('libs/Msc.jar')\n    compile 'com.google.code.gson:gson:2.2.4'\n    compile 'com.jakewharton:butterknife:5.1.1'\n    //    compile 'fm.jiecao:jiecaovideoplayer:5.3'\n    compile project(':jcvideoplayer-lib')\n    compile project(':PhotoView_library')\n    compile 'com.android.support:recyclerview-v7:24.2.1'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:\\Android_studio_sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/androidTest/java/com/atguigu/mobileplayer1020/ExampleInstrumentedTest.java",
    "content": "package com.atguigu.mobileplayer1020;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.atguigu.mobileplayer1020\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.atguigu.mobileplayer1020\">\n\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n\n    <!-- Vitamio的权限 -->\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n\n    <!--连接网络权限，用于执行云端语音能力 -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <!--获取手机录音机使用权限，听写、识别、语义理解需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>\n    <!--读取网络信息状态 -->\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n    <!--获取当前wifi状态 -->\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>\n    <!--允许程序改变网络连接状态 -->\n    <uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>\n    <!--读取手机信息权限 -->\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>\n    <!--读取联系人权限，上传联系人需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.READ_CONTACTS\"/>\n    <!--外存储写权限， 构建语法需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <!--外存储读权限，构建语法需要用到此权限 -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>\n    <!--配置权限，用来记录应用配置信息 -->\n    <uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>\n    <!--手机定位信息，用来为语义等功能提供定位， 提供更精准的服务-->\n    <!--定位信息是敏感信息， 可通过Setting.setLocationEnable(false)关闭定位请求 -->\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>\n\n    <uses-permission android:name=\"android.permission.VIBRATE\" />\n\n\n    <application\n        android:name=\".app.MyApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".WelcomeActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".MainActivity\"\n            android:configChanges=\"screenSize|orientation|keyboardHidden\"\n            android:launchMode=\"singleTask\" />\n        <activity\n            android:name=\".activity.SystemVideoPlayerActivity\"\n            android:label=\"@string/player_name\"\n            android:screenOrientation=\"landscape\"\n            android:theme=\"@style/noAnimation_Theme\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"rtsp\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n\n                <data android:mimeType=\"video/*\" />\n                <data android:mimeType=\"application/sdp\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"http\" />\n                <data android:mimeType=\"video/mpeg4\" />\n                <data android:mimeType=\"video/mp4\" />\n                <data android:mimeType=\"video/3gp\" />\n                <data android:mimeType=\"video/3gpp\" />\n                <data android:mimeType=\"video/3gpp2\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"http\" />\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"rtsp\" />\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"rtmp\" />\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"udp\" />\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"tcp\" />\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"file\" />\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"content\" />\n                <data\n                    android:mimeType=\"video/*\"\n                    android:scheme=\"mms\" />\n                <data android:mimeType=\"application/octet-stream\" />\n                <data android:mimeType=\"application/x-mpegurl\" />\n                <data android:mimeType=\"application/vnd.apple.mpegurl\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n\n                <data android:scheme=\"content\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data\n                    android:mimeType=\"application/x-mpegurl\"\n                    android:scheme=\"http\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"rtsp\" />\n                <data android:scheme=\"rtmp\" />\n                <data android:scheme=\"mms\" />\n                <data android:scheme=\"tcp\" />\n                <data android:scheme=\"udp\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <action android:name=\"android.intent.action.SEND\" />\n                <action android:name=\"android.intent.action.SENDTO\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n\n                <data android:mimeType=\"video/*\" />\n                <data android:mimeType=\"application/sdp\" />\n                <data android:mimeType=\"application/octet-stream\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"http\" />\n                <data android:mimeType=\"video/*\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"file\" />\n                <data android:scheme=\"content\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:scheme=\"ftp\" />\n                <data android:scheme=\"rtsp\" />\n                <data android:scheme=\"rtmp\" />\n                <data android:scheme=\"mms\" />\n                <data android:scheme=\"tcp\" />\n                <data android:scheme=\"udp\" />\n                <data android:scheme=\"gopher\" />\n                <data android:mimeType=\"video/*\" />\n                <!-- <data android:mimeType=\"audio/*\" /> -->\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"file\" />\n                <data android:scheme=\"content\" />\n                <data android:scheme=\"http\" />\n                <data android:scheme=\"https\" />\n                <data android:scheme=\"ftp\" />\n                <data android:scheme=\"rtsp\" />\n                <data android:scheme=\"rtmp\" />\n                <data android:scheme=\"mms\" />\n                <data android:scheme=\"tcp\" />\n                <data android:scheme=\"udp\" />\n                <data android:scheme=\"gopher\" />\n                <data android:host=\"*\" />\n                <data android:pathPattern=\".*\\\\.avi\" />\n                <data android:pathPattern=\".*\\\\.asf\" />\n                <data android:pathPattern=\".*\\\\.f4v\" />\n                <data android:pathPattern=\".*\\\\.flv\" />\n                <data android:pathPattern=\".*\\\\.mkv\" />\n                <data android:pathPattern=\".*\\\\.mpeg\" />\n                <data android:pathPattern=\".*\\\\.mpg\" />\n                <data android:pathPattern=\".*\\\\.mov\" />\n                <data android:pathPattern=\".*\\\\.rm\" />\n                <data android:pathPattern=\".*\\\\.vob\" />\n                <data android:pathPattern=\".*\\\\.wmv\" />\n                <data android:pathPattern=\".*\\\\.ts\" />\n                <data android:pathPattern=\".*\\\\.tp\" />\n                <data android:pathPattern=\".*\\\\.m3u\" />\n                <data android:pathPattern=\".*\\\\.m3u8\" />\n                <data android:pathPattern=\".*\\\\.m4v\" />\n                <data android:pathPattern=\".*\\\\.mp4\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"rtsp\" />\n                <data android:mimeType=\"video/*\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"rtsp\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"http\" />\n                <data android:mimeType=\"video/*\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n\n                <data android:mimeType=\"video/*\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n\n                <data android:scheme=\"file\" />\n                <data android:mimeType=\"video/*\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.SEARCH\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n        </activity>\n\n        <!-- Don't forgot InitActivity -->\n        <activity\n            android:name=\"io.vov.vitamio.activity.InitActivity\"\n            android:configChanges=\"orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@android:style/Theme.NoTitleBar\"\n            android:windowSoftInputMode=\"stateAlwaysHidden\" />\n        <activity\n            android:name=\".activity.VitamioVideoPlayerActivity\"\n            android:screenOrientation=\"landscape\"\n            android:theme=\"@style/noAnimation_Theme\" />\n        <activity\n            android:name=\".activity.SystemAudioPlayerActivity\"\n            android:launchMode=\"singleTask\" />\n\n        <service android:name=\".service.MusicPlayerService\" />\n\n        <activity android:name=\".activity.SearchActivity\"></activity>\n        <activity android:name=\".activity.PicassoSampleActivity\"/>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/aidl/com/atguigu/mobileplayer1020/IMusicPlayerService.aidl",
    "content": "// IMusicPlayerService.aidl\npackage com.atguigu.mobileplayer1020;\n\n// Declare any non-default types here with import statements\n\ninterface IMusicPlayerService {\n    /**\n     * Demonstrates some basic types that you can use as parameters\n     * and return values in AIDL.\n     */\n    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,\n            double aDouble, String aString);\n\n             /**\n                 * 根据位置打开一个音频并且播放\n                 * @param position\n                 */\n                void openAudio(int position);\n                /**\n                 * 开始播放音频\n                 */\n                void start();\n\n                /**\n                 * 暂停\n                 */\n                void pause();\n\n                /**\n                 * 得到歌曲的名称\n                 */\n                String getAudioName();\n                /**\n                 * 得到歌曲演唱者的名字\n                 */\n                String getArtistName();\n                /**\n                 * 得到歌曲的当前播放进度\n                 */\n                int getCurrentPosition();\n                /**\n                 * 得到歌曲的当前总进度\n                 */\n                int getDuration();\n\n                /**\n                 * 播放下一首歌曲\n                 */\n                void next();\n                /**\n                 * 播放上一首歌曲\n                 */\n                void pre();\n                /**\n                 * 得到播放模式\n                 */\n                int getPlayMode();\n                /**\n                 * 设置播放模式\n                 */\n                void setPlayMode(int mode);\n\n                /**\n                 是否在播放\n                */\n                boolean isPlaying();\n\n                /**\n                 根据传入的位置，播放\n                */\n                void seekTo(int postion);\n\n                String getAudioPath();\n\n                int getAudioSessionId();\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/MainActivity.java",
    "content": "package com.atguigu.mobileplayer1020;\n\nimport android.Manifest;\nimport android.app.Activity;\nimport android.content.pm.PackageManager;\nimport android.hardware.Sensor;\nimport android.hardware.SensorManager;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentTransaction;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.widget.RadioGroup;\n\nimport com.atguigu.mobileplayer1020.base.BaseFragment;\nimport com.atguigu.mobileplayer1020.fragment.LocalAudioFragment;\nimport com.atguigu.mobileplayer1020.fragment.LocalVideoFragment;\nimport com.atguigu.mobileplayer1020.fragment.NetAudioFragment;\nimport com.atguigu.mobileplayer1020.fragment.NetVideoFragment;\nimport com.atguigu.mobileplayer1020.fragment.RecyclerViewFragment;\n\nimport java.util.ArrayList;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\n\npublic class MainActivity extends AppCompatActivity {\n\n    private RadioGroup rg_main;\n    private ArrayList<BaseFragment> fragments;\n\n    /**\n     * Fragment页面的下标位置\n     */\n    private int position;\n\n    /**\n     * 缓存的Fragment\n     */\n    private Fragment tempFragment;\n    /**\n     *\n     */\n    private SensorManager sensorManager;\n    private JCVideoPlayer.JCAutoFullscreenListener sensorEventListener;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Log.e(\"TAG\",\"onCreate\");\n        setContentView(R.layout.activity_main);\n        rg_main = (RadioGroup) findViewById(R.id.rg_main);\n\n        //Android6.0动态获取权限\n        isGrantExternalRW(this);\n\n        initFragment();\n\n        //设置RadioGroup的监听\n        initListenter();\n\n        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);\n        sensorEventListener = new JCVideoPlayer.JCAutoFullscreenListener();\n\n    }\n\n    private void initListenter() {\n        //设置RadioGroup选中状态变化的监听\n        rg_main.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {\n            @Override\n            public void onCheckedChanged(RadioGroup group, int checkedId) {\n                switch (checkedId) {\n                    case R.id.rb_local_video:\n                        position = 0;\n                        break;\n                    case R.id.rb_local_audio:\n                        position = 1;\n                        break;\n                    case R.id.rb_net_audio:\n                        position = 2;\n                        break;\n                    case R.id.rb_net_video:\n                        position = 3;\n                        break;\n                    case R.id.rb_recyclerview:\n                        position = 4;\n                        break;\n                }\n\n                //Fragment-当前的Fragment\n                Fragment currentFragment = fragments.get(position);\n                switchFragment(currentFragment);\n            }\n        });\n\n        //默认选中本地视频\n        rg_main.check(R.id.rb_local_video);//onCheckedChanged\n    }\n\n\n\n\n    private void switchFragment(Fragment currentFragment) {\n        if (tempFragment != currentFragment) {\n\n            //开启事务\n            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();\n            //切换\n            if (currentFragment != null) {\n                //是否添加过\n                if (!currentFragment.isAdded()) {\n\n                    //把之前显示的给隐藏\n                    if (tempFragment != null) {\n                        ft.hide(tempFragment);\n                    }\n                    //如果没有添加就添加\n                    ft.add(R.id.fl_mainc_content, currentFragment);\n\n                } else {\n\n                    //把之前的隐藏\n                    if (tempFragment != null) {\n                        ft.hide(tempFragment);\n                    }\n                    //如果添加了就直接显示\n                    ft.show(currentFragment);\n\n                }\n\n                //最后一步，提交事务\n                ft.commit();\n\n            }\n            tempFragment = currentFragment;\n\n        }\n\n\n\n    }\n\n    /**\n     * 初始化Fragment\n     * 有先后顺序要求\n     */\n    private void initFragment() {\n        fragments = new ArrayList<>();\n        fragments.add(new LocalVideoFragment());//本地视频\n        fragments.add(new LocalAudioFragment());//本地音乐\n        fragments.add(new NetAudioFragment());//网络音乐\n        fragments.add(new NetVideoFragment());//网络视频\n        fragments.add(new RecyclerViewFragment());//RecyclerView\n\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        Log.e(\"TAG\",\"onDestroy\");\n    }\n\n\n    /**\n     * 解决安卓6.0以上版本不能读取外部存储权限的问题\n     * @param activity\n     * @return\n     */\n    public static boolean isGrantExternalRW(Activity activity) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(\n                Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {\n\n            activity.requestPermissions(new String[]{\n                    Manifest.permission.READ_EXTERNAL_STORAGE,\n                    Manifest.permission.WRITE_EXTERNAL_STORAGE\n            }, 1);\n\n            return false;\n        }\n\n        return true;\n    }\n\n\n//    private boolean isExit = false;\n//\n//    @Override\n//    public boolean onKeyDown(int keyCode, KeyEvent event) {\n//        if(keyCode ==KeyEvent.KEYCODE_BACK){\n//            if(position !=0){\n//                //选中首页\n//                rg_main.check(R.id.rb_local_video);\n//                return true;\n//            }else if(!isExit){\n//                isExit = true;\n//                Toast.makeText(this, \"再按一次推出\", Toast.LENGTH_SHORT).show();\n//                new Handler().postDelayed(new Runnable() {\n//                    @Override\n//                    public void run() {\n//                        isExit = false;\n//                    }\n//                }, 2000);\n//                return true;\n//            }\n//        }\n//        return super.onKeyDown(keyCode, event);\n//    }\n\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        //监听传感器\n        Sensor accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);\n        sensorManager.registerListener(sensorEventListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        //取消注册\n        sensorManager.unregisterListener(sensorEventListener);\n        JCVideoPlayer.releaseAllVideos();\n    }\n\n\n    @Override\n    public void onBackPressed() {\n        if (JCVideoPlayer.backPress()) {\n            return;\n        }\n        super.onBackPressed();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/WelcomeActivity.java",
    "content": "package com.atguigu.mobileplayer1020;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.MotionEvent;\n\npublic class WelcomeActivity extends AppCompatActivity {\n\n    private Handler handler = new Handler();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_welcome);\n\n        //延迟两秒进入主页面\n        handler.postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                //两秒后执行\n                startMainActivity();\n            }\n        }, 2000);\n    }\n//    private boolean isStartMain = false;\n\n    /**\n     * 进入主页面\n     */\n    private void startMainActivity() {\n//        if(!isStartMain){\n//            isStartMain = true;\n            //1.进入主页面\n            Intent intent = new Intent(this,MainActivity.class);\n            startActivity(intent);\n            //2.关闭当前页面\n            finish();\n//        }\n\n\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        //按下和离开\n        startMainActivity();\n        return super.onTouchEvent(event);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        //移除所有消息\n        handler.removeCallbacksAndMessages(null);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/activity/PicassoSampleActivity.java",
    "content": "package com.atguigu.mobileplayer1020.activity;\n\nimport android.graphics.drawable.Drawable;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.widget.ImageView;\n\nimport com.atguigu.mobileplayer1020.R;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.DensityUtil;\nimport org.xutils.image.ImageOptions;\nimport org.xutils.x;\n\nimport uk.co.senab.photoview.PhotoView;\nimport uk.co.senab.photoview.PhotoViewAttacher;\n\npublic class PicassoSampleActivity extends AppCompatActivity {\n\n    private ImageOptions imageOptions;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_picasso_sample);\n        String url = getIntent().getStringExtra(\"url\");\n\n        PhotoView photoView = (PhotoView) findViewById(R.id.iv_photo);\n\n        final PhotoViewAttacher attacher = new PhotoViewAttacher(photoView);\n\n\n//        Glide.with(PicassoSampleActivity.this)\n//                .load(url)\n//                .centerCrop()\n//                .error(R.drawable.video_default)\n//                .crossFade()\n//                .into(new GlideDrawableImageViewTarget(photoView) {\n//                          @Override\n//                          public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {\n//                              super.onResourceReady(drawable, anim);\n//                              //在这里添加一些图片加载完成的操作\n//                              attacher.update();\n//                          }\n//                });\n\n        imageOptions = new ImageOptions.Builder()\n//                .setSize(DensityUtil.dip2px(80), DensityUtil.dip2px(80))\n                //设置圆角\n                .setRadius(DensityUtil.dip2px(5))\n                .setIgnoreGif(false)//是否忽略gif图。false表示不忽略。不写这句，默认是true\n                .setImageScaleType(ImageView.ScaleType.CENTER_CROP)\n                .setLoadingDrawableId(R.drawable.video_default)\n                .setFailureDrawableId(R.drawable.video_default)\n                .build();\n        x.image().bind(photoView, url, imageOptions, new Callback.CommonCallback<Drawable>() {\n            @Override\n            public void onSuccess(Drawable result) {\n                attacher.update();\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n\n            }\n\n            @Override\n            public void onFinished() {\n\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/activity/SearchActivity.java",
    "content": "package com.atguigu.mobileplayer1020.activity;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.EditText;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.adapter.SearchAdapter;\nimport com.atguigu.mobileplayer1020.bean.SearchBean;\nimport com.atguigu.mobileplayer1020.utils.Constant;\nimport com.atguigu.mobileplayer1020.utils.JsonParser;\nimport com.google.gson.Gson;\nimport com.iflytek.cloud.InitListener;\nimport com.iflytek.cloud.RecognizerResult;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechUtility;\nimport com.iflytek.cloud.ui.RecognizerDialog;\nimport com.iflytek.cloud.ui.RecognizerDialogListener;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.xutils.common.Callback;\nimport org.xutils.http.RequestParams;\nimport org.xutils.x;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\n\npublic class SearchActivity extends AppCompatActivity implements View.OnClickListener {\n\n    private EditText etSearch;\n    private ImageView ivVoice;\n    private TextView tvSearch;\n    private ListView listview;\n    // 用HashMap存储听写结果\n    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();\n\n\n    /**\n     * Find the Views in the layout<br />\n     * <br />\n     * Auto-created on 2017-01-14 11:56:07 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    private void findViews() {\n        setContentView(R.layout.activity_search);\n        SpeechUtility.createUtility(this, SpeechConstant.APPID + \"=5838f0d9\");\n        etSearch = (EditText) findViewById(R.id.et_search);\n        ivVoice = (ImageView) findViewById(R.id.iv_voice);\n        tvSearch = (TextView) findViewById(R.id.tv_search);\n        listview = (ListView) findViewById(R.id.listview);\n\n        ivVoice.setOnClickListener(this);\n        tvSearch.setOnClickListener(this);\n    }\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        findViews();\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.iv_voice:\n//                Toast.makeText(this, \"语音输入\", Toast.LENGTH_SHORT).show();\n                showDialogVoice();\n                break;\n            case R.id.tv_search:\n//                Toast.makeText(this, \"搜索\", Toast.LENGTH_SHORT).show();\n                gotoSearch();\n                break;\n        }\n    }\n\n    private void gotoSearch() {\n        String word = etSearch.getText().toString().trim();\n        try {\n            word = URLEncoder.encode(word,\"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n        }\n        if (!TextUtils.isEmpty(word)) {\n\n            String url = Constant.NET_SEARCH_URL + word;\n            Log.e(\"TAG\",\"url==\"+word);\n            getDataFromNet(url);\n\n\n        } else {\n            //请输入关键字\n            Toast.makeText(this, \"请输入关键字\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    private void getDataFromNet(String url) {\n        RequestParams params = new RequestParams(url);\n        x.http().get(params, new Callback.CommonCallback<String>() {\n            @Override\n            public void onSuccess(String result) {\n                processData(result);\n\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n\n            }\n\n            @Override\n            public void onFinished() {\n\n            }\n        });\n    }\n\n    private void processData(String result) {\n        SearchBean  searchBean = new Gson().fromJson(result,SearchBean.class);\n        List<SearchBean.ItemsBean> items =  searchBean.getItems();\n        if(items != null && items.size() >0){\n            SearchAdapter searchAdapter = new SearchAdapter(SearchActivity.this,items);\n            listview.setAdapter(searchAdapter);\n\n        }\n\n\n    }\n\n    private void showDialogVoice() {\n        //1.创建RecognizerDialog对象\n        RecognizerDialog mDialog = new RecognizerDialog(this, new MyInitListener());\n//2.设置accent、 language等参数\n        mDialog.setParameter(SpeechConstant.LANGUAGE, \"zh_cn\");\n        mDialog.setParameter(SpeechConstant.ACCENT, \"mandarin\");\n//若要将UI控件用于语义理解，必须添加以下参数设置，设置之后onResult回调返回将是语义理解\n//结果\n// mDialog.setParameter(\"asr_sch\", \"1\");\n// mDialog.setParameter(\"nlp_version\", \"2.0\");\n//3.设置回调接口\n        mDialog.setListener(new MyRecognizerDialogListener());\n//4.显示dialog，接收语音输入\n        mDialog.show();\n    }\n\n    class MyRecognizerDialogListener implements RecognizerDialogListener {\n\n        @Override\n        public void onResult(RecognizerResult recognizerResult, boolean b) {\n            String result = recognizerResult.getResultString();\n            System.out.println(result);\n            String text = JsonParser.parseIatResult(recognizerResult.getResultString());\n\n            String sn = null;\n            // 读取json结果中的sn字段\n            try {\n                JSONObject resultJson = new JSONObject(recognizerResult.getResultString());\n                sn = resultJson.optString(\"sn\");\n            } catch (JSONException e) {\n                e.printStackTrace();\n            }\n\n            mIatResults.put(sn, text);\n\n            StringBuffer resultBuffer = new StringBuffer();\n            for (String key : mIatResults.keySet()) {\n                resultBuffer.append(mIatResults.get(key));\n            }\n            String reulst = resultBuffer.toString();\n            reulst = reulst.replace(\"。\",\"\");\n            etSearch.setText(reulst);\n            etSearch.setSelection(etSearch.length());\n\n        }\n\n        @Override\n        public void onError(SpeechError speechError) {\n\n            Toast.makeText(SearchActivity.this, \"出错了哦\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    class MyInitListener implements InitListener {\n\n        @Override\n        public void onInit(int i) {\n\n\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/activity/SystemAudioPlayerActivity.java",
    "content": "package com.atguigu.mobileplayer1020.activity;\n\nimport android.content.BroadcastReceiver;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.content.ServiceConnection;\nimport android.graphics.drawable.AnimationDrawable;\nimport android.media.audiofx.Visualizer;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.Message;\nimport android.os.RemoteException;\nimport android.support.v7.app.AppCompatActivity;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.IMusicPlayerService;\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\nimport com.atguigu.mobileplayer1020.service.MusicPlayerService;\nimport com.atguigu.mobileplayer1020.utils.LyricParaser;\nimport com.atguigu.mobileplayer1020.utils.Utils;\nimport com.atguigu.mobileplayer1020.view.BaseVisualizerView;\nimport com.atguigu.mobileplayer1020.view.LyricShowView;\n\nimport org.greenrobot.eventbus.EventBus;\nimport org.greenrobot.eventbus.Subscribe;\nimport org.greenrobot.eventbus.ThreadMode;\n\nimport java.io.File;\n\npublic class SystemAudioPlayerActivity extends AppCompatActivity implements View.OnClickListener {\n\n\n    private ImageView ivIcon;\n    private TextView tvArtist;\n    private TextView tvName;\n    private TextView tvTime;\n    private SeekBar seekbarAudio;\n    private Button btnAudioPlaymode;\n    private Button btnAudioPre;\n    private Button btnAudioStartPause;\n    private Button btnAudioNext;\n    private Button btnSwichLyric;\n    private int position;\n    private LyricShowView lyric_show_view;\n    private BaseVisualizerView baseVisualizerView;\n\n    private MyReceiver receiver;\n    /**\n     * 进度更新\n     */\n    private static final int PROGRESS = 1;\n    private static final int SHOW_LYRIC = 2;\n    private Utils utils;\n    private boolean notification;\n    private Visualizer mVisualizer;\n\n\n    /**\n     * Find the Views in the layout<br />\n     * <br />\n     * Auto-created on 2017-01-11 15:19:08 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    private void findViews() {\n        setContentView(R.layout.activity_system_audio_player);\n        tvArtist = (TextView) findViewById(R.id.tv_artist);\n        tvName = (TextView) findViewById(R.id.tv_name);\n        tvTime = (TextView) findViewById(R.id.tv_time);\n        seekbarAudio = (SeekBar) findViewById(R.id.seekbar_audio);\n        btnAudioPlaymode = (Button) findViewById(R.id.btn_audio_playmode);\n        btnAudioPre = (Button) findViewById(R.id.btn_audio_pre);\n        btnAudioStartPause = (Button) findViewById(R.id.btn_audio_start_pause);\n        btnAudioNext = (Button) findViewById(R.id.btn_audio_next);\n        btnSwichLyric = (Button) findViewById(R.id.btn_swich_lyric);\n        ivIcon = (ImageView) findViewById(R.id.iv_icon);\n        lyric_show_view = (LyricShowView) findViewById(R.id.lyric_show_view);\n        baseVisualizerView = (BaseVisualizerView) findViewById(R.id.baseVisualizerView);\n\n        ivIcon.setBackgroundResource(R.drawable.animation_list);\n        AnimationDrawable drawable = (AnimationDrawable) ivIcon.getBackground();\n        drawable.start();\n\n        btnAudioPlaymode.setOnClickListener(this);\n        btnAudioPre.setOnClickListener(this);\n        btnAudioStartPause.setOnClickListener(this);\n        btnAudioNext.setOnClickListener(this);\n        btnSwichLyric.setOnClickListener(this);\n\n        //设置拖拽监听\n        seekbarAudio.setOnSeekBarChangeListener(new MyOnSeekBarChangeListener());\n    }\n\n    class MyOnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {\n\n        @Override\n        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n            if (fromUser) {\n                try {\n                    service.seekTo(progress);\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n\n        }\n\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {\n\n        }\n\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {\n\n        }\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (v == btnAudioPlaymode) {\n            // Handle clicks for btnAudioPlaymode\n            changePlaymode();\n        } else if (v == btnAudioPre) {\n            // Handle clicks for btnAudioPre\n            try {\n                service.pre();\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        } else if (v == btnAudioStartPause) {\n            // Handle clicks for btnAudioStartPause\n\n            try {\n                if (service.isPlaying()) {\n                    //暂停\n                    service.pause();\n                    //按钮状态-设置播放\n                    btnAudioStartPause.setBackgroundResource(R.drawable.btn_audio_start_selector);\n                } else {\n                    //播放\n                    service.start();\n                    //按钮状态-设置暂停\n                    btnAudioStartPause.setBackgroundResource(R.drawable.btn_audio_pause_selector);\n                }\n\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n\n\n        } else if (v == btnAudioNext) {\n            // Handle clicks for btnAudioNext\n            try {\n                service.next();\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n        } else if (v == btnSwichLyric) {\n            // Handle clicks for btnSwichLyric\n        }\n    }\n\n    /**\n     * 切换模式\n     */\n    private void changePlaymode() {\n        try {\n            //得到模式\n            int playmode = service.getPlayMode();\n\n            if (playmode == MusicPlayerService.REPEATE_NOMAL) {\n                playmode = MusicPlayerService.REPEATE_SINGLE;\n            } else if (playmode == MusicPlayerService.REPEATE_SINGLE) {\n                playmode = MusicPlayerService.REPEATE_ALL;\n            } else if (playmode == MusicPlayerService.REPEATE_ALL) {\n                playmode = MusicPlayerService.REPEATE_NOMAL;\n            } else {\n                playmode = MusicPlayerService.REPEATE_NOMAL;\n            }\n            //保存到服务中\n            service.setPlayMode(playmode);\n\n            //校验按钮状态\n            checkButtonStatu();\n\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void checkButtonStatu() {\n        int playmode = 0;\n        try {\n            playmode = service.getPlayMode();\n\n            if (playmode == MusicPlayerService.REPEATE_NOMAL) {\n                btnAudioPlaymode.setBackgroundResource(R.drawable.btn_audio_playmode_normal_selector);\n            } else if (playmode == MusicPlayerService.REPEATE_SINGLE) {\n                btnAudioPlaymode.setBackgroundResource(R.drawable.btn_audio_playmode_single_selector);\n            } else if (playmode == MusicPlayerService.REPEATE_ALL) {\n                btnAudioPlaymode.setBackgroundResource(R.drawable.btn_audio_playmode_all_selector);\n            } else {\n                btnAudioPlaymode.setBackgroundResource(R.drawable.btn_audio_playmode_normal_selector);\n            }\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n\n\n    }\n\n    private IMusicPlayerService service;\n\n    private ServiceConnection conn = new ServiceConnection() {\n        /**\n         * 当连接服务成功后回调\n         * @param name\n         * @param iBdinder\n         */\n        @Override\n        public void onServiceConnected(ComponentName name, IBinder iBdinder) {\n            service = IMusicPlayerService.Stub.asInterface(iBdinder);\n\n            if (service != null) {\n                //从列表进入\n                if (!notification) {\n                    try {\n                        //开始播放\n                        service.openAudio(position);\n\n                    } catch (RemoteException e) {\n                        e.printStackTrace();\n                    }\n                } else {\n\n                    //再次显示\n                    showViewData(null);\n\n                }\n\n            }\n        }\n\n        /**\n         * 当断开的时候回调\n         * @param name\n         */\n        @Override\n        public void onServiceDisconnected(ComponentName name) {\n\n        }\n    };\n\n    private Handler handler = new Handler() {\n        @Override\n        public void handleMessage(Message msg) {\n            super.handleMessage(msg);\n            switch (msg.what) {\n                case SHOW_LYRIC://显示歌词\n                    try {\n                        int currentPosition = service.getCurrentPosition();\n\n                        lyric_show_view.setNextShowLyric(currentPosition);\n\n\n                    } catch (RemoteException e) {\n                        e.printStackTrace();\n                    }\n                    removeMessages(SHOW_LYRIC);\n                    sendEmptyMessage(SHOW_LYRIC);\n\n                    break;\n                case PROGRESS:\n\n                    try {\n                        int currentPosition = service.getCurrentPosition();\n                        tvTime.setText(utils.stringForTime(currentPosition) + \"/\" + utils.stringForTime(service.getDuration()));\n\n\n                        //SeekBar进度更新\n                        seekbarAudio.setProgress(currentPosition);\n\n                    } catch (RemoteException e) {\n                        e.printStackTrace();\n                    }\n\n                    removeMessages(PROGRESS);\n                    sendEmptyMessageDelayed(PROGRESS, 1000);\n\n                    break;\n            }\n        }\n    };\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        initData();\n        findViews();\n        getData();\n        //绑定方式启动服务\n        startAndBindServide();\n\n\n    }\n\n    /**\n     * 接收广播\n     */\n    private void initData() {\n        receiver = new MyReceiver();\n        IntentFilter intentFilter = new IntentFilter();\n        intentFilter.addAction(MusicPlayerService.OPEN_COMPLETE);\n        registerReceiver(receiver, intentFilter);\n\n        utils = new Utils();\n\n        //1.注册\n        EventBus.getDefault().register(this);\n\n    }\n\n    class MyReceiver extends BroadcastReceiver {\n\n        @Override\n        public void onReceive(Context context, Intent intent) {\n\n            if (MusicPlayerService.OPEN_COMPLETE.equals(intent.getAction())) {\n\n                showViewData(null);\n            }\n\n        }\n    }\n\n    /**\n     * 显示视图的数据\n     */\n    @Subscribe(threadMode = ThreadMode.MAIN)\n    public void showViewData(MediaItem mediaItem) {\n\n        setupVisualizerFxAndUi();\n        try {\n            tvArtist.setText(service.getArtistName());\n            tvName.setText(service.getAudioName());\n\n            //得到总时长\n            int duration = service.getDuration();\n            seekbarAudio.setMax(duration);\n\n            //更新进度\n            handler.sendEmptyMessage(PROGRESS);\n\n            checkButtonStatu();\n\n            String path = service.getAudioPath();//mnt/sdcard/audio/beij.mp3\n\n            path = path.substring(0, path.lastIndexOf(\".\"));\n\n            File file = new File(path + \".lrc\");\n            if (!file.exists()) {\n                file = new File(path + \".txt\");\n            }\n\n            LyricParaser lyricParaser = new LyricParaser();\n            //解析歌词\n            lyricParaser.readFile(file);\n\n            if (lyricParaser.isExistsLyric()) {\n\n                lyric_show_view.setLyrics(lyricParaser.getLyricBeens());\n                //歌词同步\n                handler.sendEmptyMessage(SHOW_LYRIC);\n\n            }\n\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n\n    }\n\n\n    /**\n     * 生成一个VisualizerView对象，使音频频谱的波段能够反映到 VisualizerView上\n     */\n    private void setupVisualizerFxAndUi() {\n\n        int audioSessionid = 0;\n        try {\n            audioSessionid = service.getAudioSessionId();\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n        System.out.println(\"audioSessionid==\" + audioSessionid);\n        mVisualizer = new Visualizer(audioSessionid);\n        // 参数内必须是2的位数\n        mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);\n        // 设置允许波形表示，并且捕获它\n        baseVisualizerView.setVisualizer(mVisualizer);\n        mVisualizer.setEnabled(true);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        if (isFinishing()) {\n            mVisualizer.release();\n        }\n    }\n\n    @Override\n    protected void onDestroy() {\n        if (receiver != null) {\n            unregisterReceiver(receiver);\n            receiver = null;\n        }\n\n        if (conn != null) {\n            unbindService(conn);\n            conn = null;\n        }\n        handler.removeCallbacksAndMessages(null);\n        //2.取消注册\n        EventBus.getDefault().unregister(this);\n        super.onDestroy();\n    }\n\n    private void startAndBindServide() {\n        Intent intent = new Intent(this, MusicPlayerService.class);\n        //启动服务\n        startService(intent);//防止服务多次创建\n        //绑定服务\n        bindService(intent, conn, Context.BIND_AUTO_CREATE);\n\n    }\n\n    private void getData() {\n        //true:从状态栏进入\n        //false：从ListView中进入\n        notification = getIntent().getBooleanExtra(\"notification\", false);\n\n        if (!notification) {\n            /**\n             * 得到播放位置\n             */\n            position = getIntent().getIntExtra(\"position\", 0);\n        }\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/activity/SystemVideoPlayerActivity.java",
    "content": "package com.atguigu.mobileplayer1020.activity;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.media.AudioManager;\nimport android.media.MediaPlayer;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.os.Vibrator;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\nimport com.atguigu.mobileplayer1020.utils.Utils;\nimport com.atguigu.mobileplayer1020.view.VideoView;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\n\npublic class SystemVideoPlayerActivity extends Activity implements View.OnClickListener {\n\n    private static final String TAG = SystemVideoPlayerActivity.class.getSimpleName();//\"SystemVideoPlayerActivity;\n    /**\n     * 视频默认屏幕大小播放\n     */\n    private static final int VIDEO_TYPE_DEFAULT = 1;\n    /**\n     * 视频全屏播放\n     */\n    private static final int VIDEO_TYPE_FULL = 2;\n\n\n    private VideoView videoview;\n    /**\n     * 进度跟新\n     */\n    private static final int PROGRESS = 0;\n    /**\n     * 隐藏控制面板\n     */\n    private static final int HIDE_MEDIA_CONTROLLER = 1;\n\n    /**\n     * 显示网络速度\n     */\n    private static final int SHOW_NET_SPEED = 3;\n\n    private LinearLayout llTop;\n    private TextView tvName;\n    private ImageView ivBattery;\n    private TextView tvSystetime;\n    private Button btnVoice;\n    private SeekBar seekbarVoice;\n    private Button btnSwichePlayer;\n    private LinearLayout llBottom;\n    private TextView tvCurrenttime;\n    private SeekBar seekbarVideo;\n    private TextView tvDuration;\n    private Button btnExit;\n    private Button btnPre;\n    private Button btnStartPause;\n    private Button btnNext;\n    private Button btnSwichScreen;\n    private TextView tv_loading;\n    private LinearLayout ll_loading;\n    private LinearLayout ll_buffer;\n    private TextView tv_buffer;\n\n\n    private Utils utils;\n    private MyBroadcastReceiver receiver;\n    /**\n     * 列表数据\n     */\n    private ArrayList<MediaItem> mediaItems;\n    private int position;\n    private GestureDetector detector;\n    /**\n     * 是否显示控制面板\n     */\n    private boolean isShowMediaController = false;\n    /**\n     * 视频是否全屏显示\n     */\n    private boolean isFullScreen = false;\n    private int screenWidth = 0;\n    private int screeHeight = 0;\n    private int videoWidth = 0;\n    private int videoHeight = 0;\n\n    /**\n     * 音频管理者\n     */\n    private AudioManager am;\n    /**\n     * 当前音量\n     */\n    private int currentVolume;\n    /**\n     * 最大音量:0~15\n     */\n    private int maxVolume;\n\n    /**\n     * 是否静音\n     */\n    private boolean isMute = false;\n    /**\n     * 是否网络视频\n     */\n    private boolean isNetUrl;\n    /**\n     * 震动\n     */\n    private Vibrator vibrator;\n\n    /**\n     * Find the Views in the layout<br />\n     * <br />\n     * Auto-created on 2017-01-09 09:33:35 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    private void findViews() {\n        setContentView(R.layout.activity_system_video_player);\n        videoview = (VideoView) findViewById(R.id.videoview);\n        llTop = (LinearLayout) findViewById(R.id.ll_top);\n        tvName = (TextView) findViewById(R.id.tv_name);\n        ivBattery = (ImageView) findViewById(R.id.iv_battery);\n        tvSystetime = (TextView) findViewById(R.id.tv_systetime);\n        btnVoice = (Button) findViewById(R.id.btn_voice);\n        seekbarVoice = (SeekBar) findViewById(R.id.seekbar_voice);\n        btnSwichePlayer = (Button) findViewById(R.id.btn_swiche_player);\n        llBottom = (LinearLayout) findViewById(R.id.ll_bottom);\n        tvCurrenttime = (TextView) findViewById(R.id.tv_currenttime);\n        seekbarVideo = (SeekBar) findViewById(R.id.seekbar_video);\n        tvDuration = (TextView) findViewById(R.id.tv_duration);\n        btnExit = (Button) findViewById(R.id.btn_exit);\n        btnPre = (Button) findViewById(R.id.btn_pre);\n        btnStartPause = (Button) findViewById(R.id.btn_start_pause);\n        btnNext = (Button) findViewById(R.id.btn_next);\n        btnSwichScreen = (Button) findViewById(R.id.btn_swich_screen);\n        ll_loading = (LinearLayout) findViewById(R.id.ll_loading);\n        tv_loading = (TextView) findViewById(R.id.tv_loading);\n        ll_buffer = (LinearLayout) findViewById(R.id.ll_buffer);\n        tv_buffer = (TextView) findViewById(R.id.tv_buffer);\n\n        btnVoice.setOnClickListener(this);\n        btnSwichePlayer.setOnClickListener(this);\n        btnExit.setOnClickListener(this);\n        btnPre.setOnClickListener(this);\n        btnStartPause.setOnClickListener(this);\n        btnNext.setOnClickListener(this);\n        btnSwichScreen.setOnClickListener(this);\n\n        hideMediaController();//隐藏控制面板\n\n\n        //获取音频的最大值15，当前值\n        am = (AudioManager) getSystemService(AUDIO_SERVICE);\n        currentVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);\n        maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);\n\n        //和SeekBar关联\n        seekbarVoice.setMax(maxVolume);\n        Log.e(\"TAG\", maxVolume + \"----------\");\n        seekbarVoice.setProgress(currentVolume);\n\n        //发消息\n        handler.sendEmptyMessage(SHOW_NET_SPEED);\n\n    }\n\n\n    /**\n     * 视频播放地址\n     */\n    private Uri uri;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Log.e(TAG, \"onCreate\");\n\n        initData();\n        findViews();\n\n        getData();\n        //设置视频加载的监听\n        setLinstener();\n        setData();\n\n\n    }\n\n    private void initData() {\n        utils = new Utils();\n\n        //注册监听电量广播\n        receiver = new MyBroadcastReceiver();\n        IntentFilter filter = new IntentFilter();\n        //监听电量变化\n        filter.addAction(Intent.ACTION_BATTERY_CHANGED);\n        registerReceiver(receiver, filter);\n\n        //初始化手势识别器\n        detector = new GestureDetector(this, new MySimpleOnGestureListener());\n\n\n        //得到屏幕的宽和高\n        DisplayMetrics outMetrics = new DisplayMetrics();\n        //得到屏幕参数类\n        getWindowManager().getDefaultDisplay().getMetrics(outMetrics);\n        //屏幕的宽和高\n        screenWidth = outMetrics.widthPixels;\n        screeHeight = outMetrics.heightPixels;\n\n    }\n\n    class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {\n        @Override\n        public void onLongPress(MotionEvent e) {\n            super.onLongPress(e);\n//                Toast.makeText(SystemVideoPlayerActivity.this, \"我被长按了\", Toast.LENGTH_SHORT).show();\n            startAndPause();\n        }\n\n        @Override\n        public boolean onDoubleTap(MotionEvent e) {\n//            Toast.makeText(SystemVideoPlayerActivity.this, \"我被双击了\", Toast.LENGTH_SHORT).show();\n            if (isFullScreen) {\n                //设置默认\n                setVideoType(VIDEO_TYPE_DEFAULT);\n            } else {\n                //全屏显示\n                setVideoType(VIDEO_TYPE_FULL);\n            }\n            return super.onDoubleTap(e);\n\n        }\n\n        @Override\n        public boolean onSingleTapConfirmed(MotionEvent e) {\n//            Toast.makeText(SystemVideoPlayerActivity.this, \"我被单击\", Toast.LENGTH_SHORT).show();\n            if (isShowMediaController) {\n                //隐藏\n                hideMediaController();\n                //把消息移除\n                handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n            } else {\n                //显示\n                showMediaController();\n                //重新发消息-4秒隐藏\n                handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n            }\n            return super.onSingleTapConfirmed(e);\n        }\n    }\n\n    private void setVideoType(int videoTypeDefault) {\n        switch (videoTypeDefault) {\n            case VIDEO_TYPE_FULL:\n                isFullScreen = true;\n                videoview.setViewSize(screenWidth, screeHeight);\n\n                //把按钮设置-默认\n                btnSwichScreen.setBackgroundResource(R.drawable.btn_screen_default_selector);\n\n                break;\n            case VIDEO_TYPE_DEFAULT://视频画面的默认\n                isFullScreen = false;\n\n                //视频原始的画面大小\n                int mVideoWidth = videoWidth;\n                int mVideoHeight = videoHeight;\n\n                /**\n                 * 计算后的值\n                 */\n                int width = screenWidth;\n                int height = screeHeight;\n\n                if (mVideoWidth * height < width * mVideoHeight) {\n                    //Log.i(\"@@@\", \"image too wide, correcting\");\n                    width = height * mVideoWidth / mVideoHeight;\n                } else if (mVideoWidth * height > width * mVideoHeight) {\n                    //Log.i(\"@@@\", \"image too tall, correcting\");\n                    height = width * mVideoHeight / mVideoWidth;\n                }\n\n                //把计算好的视频大小传递过去\n                videoview.setViewSize(width, height);\n                //把按钮设置-全屏\n                btnSwichScreen.setBackgroundResource(R.drawable.btn_screen_full_selector);\n                break;\n        }\n    }\n\n    /**\n     * 显示控制面板\n     */\n    private void showMediaController() {\n        isShowMediaController = true;\n        llTop.setVisibility(View.VISIBLE);\n        llBottom.setVisibility(View.VISIBLE);\n\n    }\n\n    /**\n     * 隐藏控制面板\n     */\n    private void hideMediaController() {\n        isShowMediaController = false;\n        llTop.setVisibility(View.GONE);\n        llBottom.setVisibility(View.GONE);\n\n    }\n\n    private int prePosition;\n\n    private Handler handler = new Handler() {\n        @Override\n        public void handleMessage(Message msg) {\n            super.handleMessage(msg);\n            switch (msg.what) {\n                case SHOW_NET_SPEED:\n                    String netSpeed = utils.showNetSpeed(SystemVideoPlayerActivity.this);\n                    //不为空\n                    tv_loading.setText(\"正在加载....\"+netSpeed);\n                    tv_buffer.setText(\"缓存中....\"+netSpeed);\n\n                    removeMessages(SHOW_NET_SPEED);\n                    sendEmptyMessageDelayed(SHOW_NET_SPEED,1000);\n                    break;\n                case HIDE_MEDIA_CONTROLLER:\n                    hideMediaController();//隐藏控制面板\n                    break;\n                case PROGRESS://视频播放进度的更新\n                    int currentPosition = videoview.getCurrentPosition();\n                    //设置视频更新\n                    seekbarVideo.setProgress(currentPosition);\n\n\n                    //设置播放进度的时间\n                    tvCurrenttime.setText(utils.stringForTime(currentPosition));\n\n\n                    //得到系统的时间并且更新\n                    tvSystetime.setText(getSystemTime());\n\n\n                    //设置视频缓存经度更新\n                    if (isNetUrl) {\n\n                        int buffer = videoview.getBufferPercentage();//0~100\n                        //缓存进度\n                        int secondaryProgress = buffer * seekbarVideo.getMax() / 100;\n                        seekbarVideo.setSecondaryProgress(secondaryProgress);\n                    }\n\n\n                    if (isNetUrl && videoview.isPlaying()) {\n\n                        int buffer = currentPosition - prePosition;//1000左右\n\n                        //一秒之内播放的进度小于500毫秒就是卡了，否则不卡\n                        if (buffer < 500) {\n                            //卡显示缓冲\n                            ll_buffer.setVisibility(View.VISIBLE);\n                        } else {\n                            //不卡就隐藏\n                            ll_buffer.setVisibility(View.GONE);\n                        }\n\n                    }\n\n\n                    prePosition = currentPosition;\n                    //不断发消息\n                    removeMessages(PROGRESS);\n                    sendEmptyMessageDelayed(PROGRESS, 1000);\n\n                    break;\n            }\n        }\n    };\n\n    /**\n     * 得到系统的时间\n     *\n     * @return\n     */\n    private String getSystemTime() {\n        SimpleDateFormat format = new SimpleDateFormat(\"HH:mm:ss\");\n        return format.format(new Date());\n    }\n\n    class MyBroadcastReceiver extends BroadcastReceiver {\n\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            //得到电量:0~100\n            int level = intent.getIntExtra(\"level\", 0);\n            //主线程\n            setBattery(level);\n        }\n    }\n\n    /**\n     * 设置电量\n     *\n     * @param level\n     */\n    private void setBattery(int level) {\n        if (level <= 0) {\n            ivBattery.setImageResource(R.drawable.ic_battery_0);\n        } else if (level <= 10) {\n            ivBattery.setImageResource(R.drawable.ic_battery_10);\n        } else if (level <= 20) {\n            ivBattery.setImageResource(R.drawable.ic_battery_20);\n        } else if (level <= 40) {\n            ivBattery.setImageResource(R.drawable.ic_battery_40);\n        } else if (level <= 60) {\n            ivBattery.setImageResource(R.drawable.ic_battery_60);\n        } else if (level <= 80) {\n            ivBattery.setImageResource(R.drawable.ic_battery_80);\n        } else if (level <= 100) {\n            ivBattery.setImageResource(R.drawable.ic_battery_100);\n        } else {\n            ivBattery.setImageResource(R.drawable.ic_battery_100);\n        }\n\n    }\n\n    /**\n     * Handle button click events<br />\n     * <br />\n     * Auto-created on 2017-01-09 09:33:35 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    @Override\n    public void onClick(View v) {\n        if (v == btnVoice) {\n            // Handle clicks for btnVoice\n            isMute = !isMute;\n            updateVoice(currentVolume);\n        } else if (v == btnSwichePlayer) {\n            // Handle clicks for btnSwichePlayer\n            showSwichPlayerDialog();\n\n        } else if (v == btnExit) {\n            // Handle clicks for btnExit\n            finish();\n        } else if (v == btnPre) {//上一个的点击事件\n            // Handle clicks for btnPre\n            setPreVideo();\n        } else if (v == btnStartPause) {\n            startAndPause();\n            // Handle clicks for btnStartPause\n        } else if (v == btnNext) {//下一个的点击事件\n            // Handle clicks for btnNext\n            setNextVideo();\n        } else if (v == btnSwichScreen) {\n            // Handle clicks for btnSwichScreen\n            if (isFullScreen) {\n                //设置默认\n                setVideoType(VIDEO_TYPE_DEFAULT);\n            } else {\n                //全屏显示\n                setVideoType(VIDEO_TYPE_FULL);\n            }\n        }\n\n        //移除消息\n        handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n        //重新发消息\n        handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n    }\n\n\n    private void showSwichPlayerDialog() {\n        AlertDialog.Builder builder = new AlertDialog.Builder(this);\n        builder.setTitle(\"提醒\");\n        builder.setMessage(\"当前播放使用系统播放器播放，当播放出现有声音没有画面的时候，请切换万能播放器\");\n        builder.setNegativeButton(\"取消\", new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                dialog.dismiss();\n            }\n        });\n        builder.setPositiveButton(\"确定\", new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                startVitamioVideoPlayer();\n            }\n        });\n        builder.show();\n    }\n    private void startAndPause() {\n        if (videoview.isPlaying()) {//是否在播放\n            //当前在播放要设置为暂停\n            videoview.pause();\n            //按钮状态-播放状态\n            btnStartPause.setBackgroundResource(R.drawable.btn_start_selector);\n        } else {\n            //当前暂停状态要设置播放状态\n            videoview.start();\n            //按钮状态-暂停状态\n            btnStartPause.setBackgroundResource(R.drawable.btn_pause_selector);\n        }\n    }\n\n\n    @Override\n    protected void onRestart() {\n        super.onRestart();\n        Log.e(TAG, \"onRestart\");\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        Log.e(TAG, \"onStart\");\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        Log.e(TAG, \"onResume\");\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        Log.e(TAG, \"onPause\");\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        Log.e(TAG, \"onStop\");\n    }\n\n    @Override\n    protected void onDestroy() {\n        Log.e(TAG, \"onDestroy\");\n        //是否资源-释放孩子的\n        if (receiver != null) {\n            unregisterReceiver(receiver);\n            receiver = null;\n        }\n        //消息移除\n        handler.removeCallbacksAndMessages(null);\n        super.onDestroy();\n\n    }\n\n\n    private void setData() {\n\n        if (mediaItems != null && mediaItems.size() > 0) {\n            //根据位置获取播放视频的对象\n            MediaItem mediaItem = mediaItems.get(position);\n            videoview.setVideoPath(mediaItem.getData());\n            tvName.setText(mediaItem.getName());\n            isNetUrl = utils.isNetUrl(mediaItem.getData());\n        } else if (uri != null) {\n            //设置播放地址\n            videoview.setVideoURI(uri);\n            tvName.setText(uri.toString());\n            isNetUrl = utils.isNetUrl(uri.toString());\n        }\n\n\n        checkButtonStatus();\n\n    }\n\n    private void setLinstener() {\n        //设置视频播放监听：准备好的监听，播放出错监听，播放完成监听\n        videoview.setOnPreparedListener(new MyOnPreparedListener());\n\n        videoview.setOnErrorListener(new MyOnErrorListener());\n\n        videoview.setOnCompletionListener(new MyOnCompletionListener());\n\n        //设置控制面板\n//        videoview.setMediaController(new MediaController(this));\n\n        //设置视频的拖动监听\n        seekbarVideo.setOnSeekBarChangeListener(new VideoOnSeekBarChangeListener());\n\n        //设置监听滑动声音\n        seekbarVoice.setOnSeekBarChangeListener(new VoiceOnSeekBarChangeListener());\n\n        //监听播放卡，Android2.3开始有\n//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n//            videoview.setOnInfoListener(new MyOnInfoListener());\n//        }\n\n\n    }\n\n//    class MyOnInfoListener implements MediaPlayer.OnInfoListener{\n//\n//        @Override\n//        public boolean onInfo(MediaPlayer mp, int what, int extra) {\n//            switch (what){\n//                //播放卡，拖拽卡\n//                case MediaPlayer.MEDIA_INFO_BUFFERING_START:\n//                    ll_buffer.setVisibility(View.VISIBLE);\n//                    break;\n//                //播放不卡了，拖拽不卡了\n//                case MediaPlayer.MEDIA_INFO_BUFFERING_END:\n//                    ll_buffer.setVisibility(View.GONE);\n//                    break;\n//            }\n//            return true;\n//        }\n//    }\n\n    class VoiceOnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {\n\n        @Override\n        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n            if (fromUser) {\n                updateVoiceProgress(progress);\n            }\n\n        }\n\n        /**\n         * 当手指一按下的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {\n            //移除消息\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n        }\n\n        /**\n         * 当手指离开的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n        }\n    }\n\n    private void updateVoiceProgress(int progress) {\n\n        //第一个参数：声音的类型\n        //第二个参数：声音的值：0~15\n        //第三个参数：1，显示系统调声音的；0，不显示\n        am.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);\n        seekbarVoice.setProgress(progress);\n        if (progress <= 0) {\n            //设置静音\n            isMute = true;\n        } else {\n            isMute = false;\n        }\n\n        currentVolume = progress;\n\n    }\n\n    private void updateVoice(int progress) {\n\n        if (isMute) {\n            am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);\n            seekbarVoice.setProgress(0);\n        } else {\n            //第一个参数：声音的类型\n            //第二个参数：声音的值：0~15\n            //第三个参数：1，显示系统调声音的；0，不显示\n            am.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);\n            seekbarVoice.setProgress(progress);\n        }\n\n        currentVolume = progress;\n\n    }\n\n    class VideoOnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {\n\n        /**\n         * 状态变化的时候回调\n         *\n         * @param seekBar\n         * @param progress 当前改变的进度-要拖动到的位置\n         * @param fromUser 用户导致的改变true,否则false\n         */\n        @Override\n        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n            if (fromUser) {\n                //响应用户拖动\n                videoview.seekTo(progress);\n            }\n\n        }\n\n        /**\n         * 当手指一按下的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {\n            //移除消息\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n        }\n\n        /**\n         * 当手指离开的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n        }\n    }\n\n    class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {\n\n        @Override\n        public void onCompletion(MediaPlayer mp) {\n            //1.单个视频-退出播放器\n            //2.视频列表-播放下一个\n//            Toast.makeText(SystemVideoPlayerActivity.this, \"视频播放完成\", Toast.LENGTH_SHORT).show();\n            setNextVideo();\n        }\n    }\n\n    private void setPreVideo() {\n        //1.判断一下列表\n        if (mediaItems != null && mediaItems.size() > 0) {\n            position--;\n            if (position >= 0) {\n                //显示加载页面\n                ll_loading.setVisibility(View.VISIBLE);\n                MediaItem mediaItem = mediaItems.get(position);\n                //设置标题\n                tvName.setText(mediaItem.getName());\n\n                isNetUrl = utils.isNetUrl(mediaItem.getData());\n                //设置播放地址\n                videoview.setVideoPath(mediaItem.getData());\n\n                //校验按钮状态\n                checkButtonStatus();\n\n            } else {\n                //越界\n                position = 0;\n            }\n        }\n\n\n    }\n\n    /**\n     * 设置播放下一个\n     */\n    private void setNextVideo() {\n        //1.判断一下列表\n        if (mediaItems != null && mediaItems.size() > 0) {\n            position++;\n            if (position < mediaItems.size()) {\n                MediaItem mediaItem = mediaItems.get(position);\n\n                //显示加载页面\n                ll_loading.setVisibility(View.VISIBLE);\n                //设置标题\n                tvName.setText(mediaItem.getName());\n                isNetUrl = utils.isNetUrl(mediaItem.getData());\n                //设置播放地址\n                videoview.setVideoPath(mediaItem.getData());\n\n                //专题的校验\n                checkButtonStatus();\n\n                if (position == mediaItems.size() - 1) {\n                    Toast.makeText(this, \"哥们播放最后一个视频了哦\", Toast.LENGTH_SHORT).show();\n                }\n\n            } else {\n                //越界\n                position = mediaItems.size() - 1;\n                finish();\n            }\n        }\n        //2. 单个的uri\n        else if (uri != null) {\n            finish();\n        }\n\n\n    }\n\n    private void checkButtonStatus() {\n        //1.判断一下列表\n        if (mediaItems != null && mediaItems.size() > 0) {\n            //1.其他设置默认\n            setButtonEnable(true);\n\n            //2.播放第0个，上一个i设置灰色\n            if (position == 0) {\n                btnPre.setBackgroundResource(R.drawable.btn_pre_gray);\n                btnPre.setEnabled(false);\n            }\n            //3.播放最后一个设置，下一个按钮设置灰色\n            if (position == mediaItems.size() - 1) {\n                btnNext.setBackgroundResource(R.drawable.btn_next_gray);\n                btnNext.setEnabled(false);\n            }\n\n        }\n        //2. 单个的uri\n        else if (uri != null) {\n\n            //上一个和下一个都要设置灰色\n            setButtonEnable(false);\n        }\n    }\n\n    /***\n     * 设置按钮的可点状态\n     *\n     * @param isEnable\n     */\n    private void setButtonEnable(boolean isEnable) {\n        if (isEnable) {\n            btnPre.setBackgroundResource(R.drawable.btn_pre_selector);\n            btnNext.setBackgroundResource(R.drawable.btn_next_selector);\n\n        } else {\n            btnPre.setBackgroundResource(R.drawable.btn_pre_gray);\n            btnNext.setBackgroundResource(R.drawable.btn_next_gray);\n        }\n        btnPre.setEnabled(isEnable);\n        btnNext.setEnabled(isEnable);\n    }\n\n    class MyOnErrorListener implements MediaPlayer.OnErrorListener {\n\n        @Override\n        public boolean onError(MediaPlayer mp, int what, int extra) {\n//            Toast.makeText(SystemVideoPlayerActivity.this, \"播放出错了，亲\", Toast.LENGTH_SHORT).show();\n            //1.播放的视频格式不支持--跳转万能播放器播放\n            startVitamioVideoPlayer();\n\n            //2.播放网络资源视频的时候，断网了==提示-重试（3次）\n\n\n            //3.视频内容有缺损\n            return true;\n        }\n    }\n\n    /**\n     * 启动万能解码器\n     */\n    private void startVitamioVideoPlayer() {\n\n        if(videoview != null){\n            videoview.stopPlayback();\n        }\n\n        Intent intent = new Intent(this,VitamioVideoPlayerActivity.class);\n\n        if(mediaItems != null && mediaItems.size() >0){\n\n            Bundle bundle = new Bundle();\n            //列表数据\n            bundle.putSerializable(\"videolist\",mediaItems);\n            intent.putExtras(bundle);\n            //传递点击的位置\n            intent.putExtra(\"position\",position);\n\n        }else if(uri != null){\n            intent.setDataAndType(uri,\"video/*\");\n        }\n\n        startActivity(intent);\n\n        finish();\n\n    }\n\n    class MyOnPreparedListener implements MediaPlayer.OnPreparedListener {\n\n        /**\n         * 当底层加载视频准备完成的时候回调\n         *\n         * @param mp\n         */\n        @Override\n        public void onPrepared(MediaPlayer mp) {\n\n            //得到视频原始的大小\n            videoWidth = mp.getVideoWidth();\n            videoHeight = mp.getVideoHeight();\n\n            //设置默认大小\n            setVideoType(VIDEO_TYPE_DEFAULT);\n\n            //开始播放\n            videoview.start();\n\n            //统计用户的行为-拖动\n//            mp.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {\n//                @Override\n//                public void onSeekComplete(MediaPlayer mp) {\n//                    Toast.makeText(SystemVideoPlayerActivity.this, \"拖动完成了\", Toast.LENGTH_SHORT).show();\n//                }\n//            });\n            //准备好的时候\n            //1.视频的总播放时长和SeeKBar关联起来\n            int duration = videoview.getDuration();\n            seekbarVideo.setMax(duration);\n\n            //设置总时长\n            tvDuration.setText(utils.stringForTime(duration));\n\n            //发消息\n            handler.sendEmptyMessage(PROGRESS);\n\n            //隐藏加载等待页面\n            ll_loading.setVisibility(View.GONE);\n\n\n        }\n    }\n\n    private float startY;\n    /**\n     * 滑动的区域\n     */\n    private int touchRang = 0;\n\n    /**\n     * 当按下的时候的音量\n     */\n    private int mVol;\n    private float startX;\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        super.onTouchEvent(event);\n        detector.onTouchEvent(event);//把事件传递给手势识别器\n        if (event.getAction() == MotionEvent.ACTION_DOWN) {\n            //1.按下\n            //按下的时候记录起始坐标，最大的滑动区域（屏幕的高），当前的音量\n            startY = event.getY();\n            startX = event.getX();\n            touchRang = Math.min(screeHeight, screenWidth);//screeHeight\n            mVol = am.getStreamVolume(AudioManager.STREAM_MUSIC);\n            //把消息移除\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n\n\n        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {\n            float endY = event.getY();\n            float distanceY = startY - endY;\n            if(startX > screenWidth/2){\n                //屏幕滑动的距离\n\n                //滑动屏幕的距离 ： 总距离  = 改变的声音 ： 总声音\n\n                //改变的声音 = （滑动屏幕的距离 / 总距离)*总声音\n                float delta = (distanceY / touchRang) * maxVolume;\n                // 设置的声音  = 原来记录的 + 改变的声音\n                int volue = (int) Math.min(Math.max(mVol + delta, 0), maxVolume);\n                //判断\n                if (delta != 0) {\n                    updateVoiceProgress(volue);\n                }\n\n//            startY = event.getY();//不能添加\n            }else{\n\n                //左边屏幕--改变亮度\n                final double FLING_MIN_DISTANCE = 0.5;\n                final double FLING_MIN_VELOCITY = 0.5;\n                if (startY- endY > FLING_MIN_DISTANCE\n                        && Math.abs(distanceY) > FLING_MIN_VELOCITY) {\n                    Log.e(TAG, \"up\");\n                    setBrightness(20);\n                }\n                if (startY - endY < FLING_MIN_DISTANCE\n                        && Math.abs(distanceY) > FLING_MIN_VELOCITY) {\n                    Log.e(TAG, \"down\");\n                    setBrightness(-20);\n                }\n\n\n            }\n\n\n\n        } else if (event.getAction() == MotionEvent.ACTION_UP) {\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n        }\n        return true;\n    }\n\n    /*\n     *\n     * 设置屏幕亮度 lp = 0 全暗 ，lp= -1,根据系统设置， lp = 1; 最亮\n     */\n    public void setBrightness(float brightness) {\n        WindowManager.LayoutParams lp = getWindow().getAttributes();\n        // if (lp.screenBrightness <= 0.1) {\n        // return;\n        // }\n        lp.screenBrightness = lp.screenBrightness + brightness / 255.0f;\n        if (lp.screenBrightness > 1) {\n            lp.screenBrightness = 1;\n            vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);\n            long[] pattern = { 10, 200 }; // OFF/ON/OFF/ON...\n            vibrator.vibrate(pattern, -1);\n        } else if (lp.screenBrightness < 0.2) {\n            lp.screenBrightness = (float) 0.2;\n            vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);\n            long[] pattern = { 10, 200 }; // OFF/ON/OFF/ON...\n            vibrator.vibrate(pattern, -1);\n        }\n        Log.e(TAG, \"lp.screenBrightness= \" + lp.screenBrightness);\n        getWindow().setAttributes(lp);\n    }\n\n    /**\n     * 得到播放地址\n     */\n    private void getData() {\n        //一个地址：从文件发起的单个播放请求\n        uri = getIntent().getData();\n        Log.e(\"TAG\", \"uri===\" + uri);\n        //得到视频列表\n        mediaItems = (ArrayList<MediaItem>) getIntent().getSerializableExtra(\"videolist\");\n        position = getIntent().getIntExtra(\"position\", 0);\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {\n            //改变音量值\n            currentVolume--;\n            updateVoiceProgress(currentVolume);\n            //移除消息\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n            //发消息\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n            return true;\n        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {\n            currentVolume++;\n            updateVoiceProgress(currentVolume);\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n            return true;\n        }\n        return super.onKeyDown(keyCode, event);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/activity/VitamioVideoPlayerActivity.java",
    "content": "package com.atguigu.mobileplayer1020.activity;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.media.AudioManager;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.DisplayMetrics;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\nimport com.atguigu.mobileplayer1020.utils.Utils;\nimport com.atguigu.mobileplayer1020.view.VitamioVideoView;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.Vitamio;\n\npublic class VitamioVideoPlayerActivity extends Activity implements View.OnClickListener {\n\n    private static final String TAG = VitamioVideoPlayerActivity.class.getSimpleName();//\"SystemVideoPlayerActivity;\n    /**\n     * 视频默认屏幕大小播放\n     */\n    private static final int VIDEO_TYPE_DEFAULT = 1;\n    /**\n     * 视频全屏播放\n     */\n    private static final int VIDEO_TYPE_FULL = 2;\n\n\n    private VitamioVideoView videoview;\n    /**\n     * 进度跟新\n     */\n    private static final int PROGRESS = 0;\n    /**\n     * 隐藏控制面板\n     */\n    private static final int HIDE_MEDIA_CONTROLLER = 1;\n\n    /**\n     * 显示网络速度\n     */\n    private static final int SHOW_NET_SPEED = 3;\n\n    private LinearLayout llTop;\n    private TextView tvName;\n    private ImageView ivBattery;\n    private TextView tvSystetime;\n    private Button btnVoice;\n    private SeekBar seekbarVoice;\n    private Button btnSwichePlayer;\n    private LinearLayout llBottom;\n    private TextView tvCurrenttime;\n    private SeekBar seekbarVideo;\n    private TextView tvDuration;\n    private Button btnExit;\n    private Button btnPre;\n    private Button btnStartPause;\n    private Button btnNext;\n    private Button btnSwichScreen;\n    private TextView tv_loading;\n    private LinearLayout ll_loading;\n    private LinearLayout ll_buffer;\n    private TextView tv_buffer;\n\n\n    private Utils utils;\n    private MyBroadcastReceiver receiver;\n    /**\n     * 列表数据\n     */\n    private ArrayList<MediaItem> mediaItems;\n    private int position;\n    private GestureDetector detector;\n    /**\n     * 是否显示控制面板\n     */\n    private boolean isShowMediaController = false;\n    /**\n     * 视频是否全屏显示\n     */\n    private boolean isFullScreen = false;\n    private int screenWidth = 0;\n    private int screeHeight = 0;\n    private int videoWidth = 0;\n    private int videoHeight = 0;\n\n    /**\n     * 音频管理者\n     */\n    private AudioManager am;\n    /**\n     * 当前音量\n     */\n    private int currentVolume;\n    /**\n     * 最大音量:0~15\n     */\n    private int maxVolume;\n\n    /**\n     * 是否静音\n     */\n    private boolean isMute = false;\n    /**\n     * 是否网络视频\n     */\n    private boolean isNetUrl;\n\n    /**\n     * Find the Views in the layout<br />\n     * <br />\n     * Auto-created on 2017-01-09 09:33:35 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    private void findViews() {\n        setContentView(R.layout.activity_vitamio_video_player);\n        videoview = (VitamioVideoView) findViewById(R.id.videoview);\n        llTop = (LinearLayout) findViewById(R.id.ll_top);\n        tvName = (TextView) findViewById(R.id.tv_name);\n        ivBattery = (ImageView) findViewById(R.id.iv_battery);\n        tvSystetime = (TextView) findViewById(R.id.tv_systetime);\n        btnVoice = (Button) findViewById(R.id.btn_voice);\n        seekbarVoice = (SeekBar) findViewById(R.id.seekbar_voice);\n        btnSwichePlayer = (Button) findViewById(R.id.btn_swiche_player);\n        llBottom = (LinearLayout) findViewById(R.id.ll_bottom);\n        tvCurrenttime = (TextView) findViewById(R.id.tv_currenttime);\n        seekbarVideo = (SeekBar) findViewById(R.id.seekbar_video);\n        tvDuration = (TextView) findViewById(R.id.tv_duration);\n        btnExit = (Button) findViewById(R.id.btn_exit);\n        btnPre = (Button) findViewById(R.id.btn_pre);\n        btnStartPause = (Button) findViewById(R.id.btn_start_pause);\n        btnNext = (Button) findViewById(R.id.btn_next);\n        btnSwichScreen = (Button) findViewById(R.id.btn_swich_screen);\n        ll_loading = (LinearLayout) findViewById(R.id.ll_loading);\n        tv_loading = (TextView) findViewById(R.id.tv_loading);\n        ll_buffer = (LinearLayout) findViewById(R.id.ll_buffer);\n        tv_buffer = (TextView) findViewById(R.id.tv_buffer);\n\n        btnVoice.setOnClickListener(this);\n        btnSwichePlayer.setOnClickListener(this);\n        btnExit.setOnClickListener(this);\n        btnPre.setOnClickListener(this);\n        btnStartPause.setOnClickListener(this);\n        btnNext.setOnClickListener(this);\n        btnSwichScreen.setOnClickListener(this);\n\n        hideMediaController();//隐藏控制面板\n\n\n        //获取音频的最大值15，当前值\n        am = (AudioManager) getSystemService(AUDIO_SERVICE);\n        currentVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);\n        maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);\n\n        //和SeekBar关联\n        seekbarVoice.setMax(maxVolume);\n        Log.e(\"TAG\", maxVolume + \"----------\");\n        seekbarVoice.setProgress(currentVolume);\n\n        //发消息\n        handler.sendEmptyMessage(SHOW_NET_SPEED);\n\n    }\n\n\n    /**\n     * 视频播放地址\n     */\n    private Uri uri;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        Log.e(TAG, \"onCreate\");\n        //初始化Vitamio解码器\n        Vitamio.isInitialized(this);\n        initData();\n        findViews();\n\n        getData();\n        //设置视频加载的监听\n        setLinstener();\n        setData();\n\n\n    }\n\n    private void initData() {\n        utils = new Utils();\n\n        //注册监听电量广播\n        receiver = new MyBroadcastReceiver();\n        IntentFilter filter = new IntentFilter();\n        //监听电量变化\n        filter.addAction(Intent.ACTION_BATTERY_CHANGED);\n        registerReceiver(receiver, filter);\n\n        //初始化手势识别器\n        detector = new GestureDetector(this, new MySimpleOnGestureListener());\n\n\n        //得到屏幕的宽和高\n        DisplayMetrics outMetrics = new DisplayMetrics();\n        //得到屏幕参数类\n        getWindowManager().getDefaultDisplay().getMetrics(outMetrics);\n        //屏幕的宽和高\n        screenWidth = outMetrics.widthPixels;\n        screeHeight = outMetrics.heightPixels;\n\n    }\n\n    class MySimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {\n        @Override\n        public void onLongPress(MotionEvent e) {\n            super.onLongPress(e);\n//                Toast.makeText(SystemVideoPlayerActivity.this, \"我被长按了\", Toast.LENGTH_SHORT).show();\n            startAndPause();\n        }\n\n        @Override\n        public boolean onDoubleTap(MotionEvent e) {\n//            Toast.makeText(SystemVideoPlayerActivity.this, \"我被双击了\", Toast.LENGTH_SHORT).show();\n            if (isFullScreen) {\n                //设置默认\n                setVideoType(VIDEO_TYPE_DEFAULT);\n            } else {\n                //全屏显示\n                setVideoType(VIDEO_TYPE_FULL);\n            }\n            return super.onDoubleTap(e);\n\n        }\n\n        @Override\n        public boolean onSingleTapConfirmed(MotionEvent e) {\n//            Toast.makeText(SystemVideoPlayerActivity.this, \"我被单击\", Toast.LENGTH_SHORT).show();\n            if (isShowMediaController) {\n                //隐藏\n                hideMediaController();\n                //把消息移除\n                handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n            } else {\n                //显示\n                showMediaController();\n                //重新发消息-4秒隐藏\n                handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n            }\n            return super.onSingleTapConfirmed(e);\n        }\n    }\n\n    private void setVideoType(int videoTypeDefault) {\n        switch (videoTypeDefault) {\n            case VIDEO_TYPE_FULL:\n                isFullScreen = true;\n                videoview.setViewSize(screenWidth, screeHeight);\n\n                //把按钮设置-默认\n                btnSwichScreen.setBackgroundResource(R.drawable.btn_screen_default_selector);\n\n                break;\n            case VIDEO_TYPE_DEFAULT://视频画面的默认\n                isFullScreen = false;\n\n                //视频原始的画面大小\n                int mVideoWidth = videoWidth;\n                int mVideoHeight = videoHeight;\n\n                /**\n                 * 计算后的值\n                 */\n                int width = screenWidth;\n                int height = screeHeight;\n\n                if (mVideoWidth * height < width * mVideoHeight) {\n                    //Log.i(\"@@@\", \"image too wide, correcting\");\n                    width = height * mVideoWidth / mVideoHeight;\n                } else if (mVideoWidth * height > width * mVideoHeight) {\n                    //Log.i(\"@@@\", \"image too tall, correcting\");\n                    height = width * mVideoHeight / mVideoWidth;\n                }\n\n                //把计算好的视频大小传递过去\n                videoview.setViewSize(width, height);\n                //把按钮设置-全屏\n                btnSwichScreen.setBackgroundResource(R.drawable.btn_screen_full_selector);\n                break;\n        }\n    }\n\n    /**\n     * 显示控制面板\n     */\n    private void showMediaController() {\n        isShowMediaController = true;\n        llTop.setVisibility(View.VISIBLE);\n        llBottom.setVisibility(View.VISIBLE);\n\n    }\n\n    /**\n     * 隐藏控制面板\n     */\n    private void hideMediaController() {\n        isShowMediaController = false;\n        llTop.setVisibility(View.GONE);\n        llBottom.setVisibility(View.GONE);\n\n    }\n\n    private int prePosition;\n\n    private Handler handler = new Handler() {\n        @Override\n        public void handleMessage(Message msg) {\n            super.handleMessage(msg);\n            switch (msg.what) {\n                case SHOW_NET_SPEED:\n                    String netSpeed = utils.showNetSpeed(VitamioVideoPlayerActivity.this);\n                    //不为空\n                    tv_loading.setText(\"正在加载....\"+netSpeed);\n                    tv_buffer.setText(\"缓存中....\"+netSpeed);\n\n                    removeMessages(SHOW_NET_SPEED);\n                    sendEmptyMessageDelayed(SHOW_NET_SPEED,1000);\n                    break;\n                case HIDE_MEDIA_CONTROLLER:\n                    hideMediaController();//隐藏控制面板\n                    break;\n                case PROGRESS://视频播放进度的更新\n                    int currentPosition = (int) videoview.getCurrentPosition();\n                    //设置视频更新\n                    seekbarVideo.setProgress(currentPosition);\n\n\n                    //设置播放进度的时间\n                    tvCurrenttime.setText(utils.stringForTime(currentPosition));\n\n\n                    //得到系统的时间并且更新\n                    tvSystetime.setText(getSystemTime());\n\n\n                    //设置视频缓存经度更新\n                    if (isNetUrl) {\n\n                        int buffer = videoview.getBufferPercentage();//0~100\n                        //缓存进度\n                        int secondaryProgress = buffer * seekbarVideo.getMax() / 100;\n                        seekbarVideo.setSecondaryProgress(secondaryProgress);\n                    }\n\n\n                    if (isNetUrl && videoview.isPlaying()) {\n\n                        int buffer = currentPosition - prePosition;//1000左右\n\n                        //一秒之内播放的进度小于500毫秒就是卡了，否则不卡\n                        if (buffer < 500) {\n                            //卡显示缓冲\n                            ll_buffer.setVisibility(View.VISIBLE);\n                        } else {\n                            //不卡就隐藏\n                            ll_buffer.setVisibility(View.GONE);\n                        }\n\n                    }\n\n\n                    prePosition = currentPosition;\n                    //不断发消息\n                    removeMessages(PROGRESS);\n                    sendEmptyMessageDelayed(PROGRESS, 1000);\n\n                    break;\n            }\n        }\n    };\n\n    /**\n     * 得到系统的时间\n     *\n     * @return\n     */\n    private String getSystemTime() {\n        SimpleDateFormat format = new SimpleDateFormat(\"HH:mm:ss\");\n        return format.format(new Date());\n    }\n\n    class MyBroadcastReceiver extends BroadcastReceiver {\n\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            //得到电量:0~100\n            int level = intent.getIntExtra(\"level\", 0);\n            //主线程\n            setBattery(level);\n        }\n    }\n\n    /**\n     * 设置电量\n     *\n     * @param level\n     */\n    private void setBattery(int level) {\n        if (level <= 0) {\n            ivBattery.setImageResource(R.drawable.ic_battery_0);\n        } else if (level <= 10) {\n            ivBattery.setImageResource(R.drawable.ic_battery_10);\n        } else if (level <= 20) {\n            ivBattery.setImageResource(R.drawable.ic_battery_20);\n        } else if (level <= 40) {\n            ivBattery.setImageResource(R.drawable.ic_battery_40);\n        } else if (level <= 60) {\n            ivBattery.setImageResource(R.drawable.ic_battery_60);\n        } else if (level <= 80) {\n            ivBattery.setImageResource(R.drawable.ic_battery_80);\n        } else if (level <= 100) {\n            ivBattery.setImageResource(R.drawable.ic_battery_100);\n        } else {\n            ivBattery.setImageResource(R.drawable.ic_battery_100);\n        }\n\n    }\n\n    /**\n     * Handle button click events<br />\n     * <br />\n     * Auto-created on 2017-01-09 09:33:35 by Android Layout Finder\n     * (http://www.buzzingandroid.com/tools/android-layout-finder)\n     */\n    @Override\n    public void onClick(View v) {\n        if (v == btnVoice) {\n            // Handle clicks for btnVoice\n            isMute = !isMute;\n            updateVoice(currentVolume);\n        } else if (v == btnSwichePlayer) {\n            // Handle clicks for btnSwichePlayer\n            showSwichPlayerDialog();\n        } else if (v == btnExit) {\n            // Handle clicks for btnExit\n            finish();\n        } else if (v == btnPre) {//上一个的点击事件\n            // Handle clicks for btnPre\n            setPreVideo();\n        } else if (v == btnStartPause) {\n            startAndPause();\n            // Handle clicks for btnStartPause\n        } else if (v == btnNext) {//下一个的点击事件\n            // Handle clicks for btnNext\n            setNextVideo();\n        } else if (v == btnSwichScreen) {\n            // Handle clicks for btnSwichScreen\n            if (isFullScreen) {\n                //设置默认\n                setVideoType(VIDEO_TYPE_DEFAULT);\n            } else {\n                //全屏显示\n                setVideoType(VIDEO_TYPE_FULL);\n            }\n        }\n\n        //移除消息\n        handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n        //重新发消息\n        handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n    }\n\n    private void showSwichPlayerDialog() {\n        AlertDialog.Builder builder = new AlertDialog.Builder(this);\n        builder.setTitle(\"提醒\");\n        builder.setMessage(\"当前播放使用万能播放器播放，当播放出现有视频有色块，播放效果不理想，请切换系统播放器播放\");\n        builder.setNegativeButton(\"取消\", new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                dialog.dismiss();\n            }\n        });\n        builder.setPositiveButton(\"确定\", new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                startSystemVideoPlayer();\n            }\n        });\n        builder.show();\n    }\n\n    private void startSystemVideoPlayer() {\n        if(videoview != null){\n            videoview.stopPlayback();\n        }\n\n        Intent intent = new Intent(this,SystemVideoPlayerActivity.class);\n\n        if(mediaItems != null && mediaItems.size() >0){\n\n            Bundle bundle = new Bundle();\n            //列表数据\n            bundle.putSerializable(\"videolist\",mediaItems);\n            intent.putExtras(bundle);\n            //传递点击的位置\n            intent.putExtra(\"position\",position);\n\n        }else if(uri != null){\n            intent.setDataAndType(uri,\"video/*\");\n        }\n\n        startActivity(intent);\n\n        finish();\n    }\n\n    private void startAndPause() {\n        if (videoview.isPlaying()) {//是否在播放\n            //当前在播放要设置为暂停\n            videoview.pause();\n            //按钮状态-播放状态\n            btnStartPause.setBackgroundResource(R.drawable.btn_start_selector);\n        } else {\n            //当前暂停状态要设置播放状态\n            videoview.start();\n            //按钮状态-暂停状态\n            btnStartPause.setBackgroundResource(R.drawable.btn_pause_selector);\n        }\n    }\n\n\n    @Override\n    protected void onRestart() {\n        super.onRestart();\n        Log.e(TAG, \"onRestart\");\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        Log.e(TAG, \"onStart\");\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        Log.e(TAG, \"onResume\");\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        Log.e(TAG, \"onPause\");\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        Log.e(TAG, \"onStop\");\n    }\n\n    @Override\n    protected void onDestroy() {\n        Log.e(TAG, \"onDestroy\");\n        //是否资源-释放孩子的\n        if (receiver != null) {\n            unregisterReceiver(receiver);\n            receiver = null;\n        }\n        //消息移除\n        handler.removeCallbacksAndMessages(null);\n        super.onDestroy();\n\n    }\n\n\n    private void setData() {\n\n        if (mediaItems != null && mediaItems.size() > 0) {\n            //根据位置获取播放视频的对象\n            MediaItem mediaItem = mediaItems.get(position);\n            videoview.setVideoPath(mediaItem.getData());\n            tvName.setText(mediaItem.getName());\n            isNetUrl = utils.isNetUrl(mediaItem.getData());\n        } else if (uri != null) {\n            //设置播放地址\n            videoview.setVideoURI(uri);\n            tvName.setText(uri.toString());\n            isNetUrl = utils.isNetUrl(uri.toString());\n        }\n\n\n        checkButtonStatus();\n\n    }\n\n    private void setLinstener() {\n        //设置视频播放监听：准备好的监听，播放出错监听，播放完成监听\n        videoview.setOnPreparedListener(new MyOnPreparedListener());\n\n        videoview.setOnErrorListener(new MyOnErrorListener());\n\n        videoview.setOnCompletionListener(new MyOnCompletionListener());\n\n        //设置控制面板\n//        videoview.setMediaController(new MediaController(this));\n\n        //设置视频的拖动监听\n        seekbarVideo.setOnSeekBarChangeListener(new VideoOnSeekBarChangeListener());\n\n        //设置监听滑动声音\n        seekbarVoice.setOnSeekBarChangeListener(new VoiceOnSeekBarChangeListener());\n\n        //监听播放卡，Android2.3开始有\n//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n//            videoview.setOnInfoListener(new MyOnInfoListener());\n//        }\n\n\n    }\n\n//    class MyOnInfoListener implements MediaPlayer.OnInfoListener{\n//\n//        @Override\n//        public boolean onInfo(MediaPlayer mp, int what, int extra) {\n//            switch (what){\n//                //播放卡，拖拽卡\n//                case MediaPlayer.MEDIA_INFO_BUFFERING_START:\n//                    ll_buffer.setVisibility(View.VISIBLE);\n//                    break;\n//                //播放不卡了，拖拽不卡了\n//                case MediaPlayer.MEDIA_INFO_BUFFERING_END:\n//                    ll_buffer.setVisibility(View.GONE);\n//                    break;\n//            }\n//            return true;\n//        }\n//    }\n\n    class VoiceOnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {\n\n        @Override\n        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n            if (fromUser) {\n                updateVoiceProgress(progress);\n            }\n\n        }\n\n        /**\n         * 当手指一按下的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {\n            //移除消息\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n        }\n\n        /**\n         * 当手指离开的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n        }\n    }\n\n    private void updateVoiceProgress(int progress) {\n\n        //第一个参数：声音的类型\n        //第二个参数：声音的值：0~15\n        //第三个参数：1，显示系统调声音的；0，不显示\n        am.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);\n        seekbarVoice.setProgress(progress);\n        if (progress <= 0) {\n            //设置静音\n            isMute = true;\n        } else {\n            isMute = false;\n        }\n\n        currentVolume = progress;\n\n    }\n\n    private void updateVoice(int progress) {\n\n        if (isMute) {\n            am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);\n            seekbarVoice.setProgress(0);\n        } else {\n            //第一个参数：声音的类型\n            //第二个参数：声音的值：0~15\n            //第三个参数：1，显示系统调声音的；0，不显示\n            am.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);\n            seekbarVoice.setProgress(progress);\n        }\n\n        currentVolume = progress;\n\n    }\n\n    class VideoOnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {\n\n        /**\n         * 状态变化的时候回调\n         *\n         * @param seekBar\n         * @param progress 当前改变的进度-要拖动到的位置\n         * @param fromUser 用户导致的改变true,否则false\n         */\n        @Override\n        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n            if (fromUser) {\n                //响应用户拖动\n                videoview.seekTo(progress);\n            }\n\n        }\n\n        /**\n         * 当手指一按下的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {\n            //移除消息\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n        }\n\n        /**\n         * 当手指离开的时候回调\n         *\n         * @param seekBar\n         */\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n        }\n    }\n\n    class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {\n\n        @Override\n        public void onCompletion(MediaPlayer mp) {\n            //1.单个视频-退出播放器\n            //2.视频列表-播放下一个\n//            Toast.makeText(SystemVideoPlayerActivity.this, \"视频播放完成\", Toast.LENGTH_SHORT).show();\n            setNextVideo();\n        }\n    }\n\n    private void setPreVideo() {\n        //1.判断一下列表\n        if (mediaItems != null && mediaItems.size() > 0) {\n            position--;\n            if (position >= 0) {\n                //显示加载页面\n                ll_loading.setVisibility(View.VISIBLE);\n                MediaItem mediaItem = mediaItems.get(position);\n                //设置标题\n                tvName.setText(mediaItem.getName());\n\n                isNetUrl = utils.isNetUrl(mediaItem.getData());\n                //设置播放地址\n                videoview.setVideoPath(mediaItem.getData());\n\n                //校验按钮状态\n                checkButtonStatus();\n\n            } else {\n                //越界\n                position = 0;\n            }\n        }\n\n\n    }\n\n    /**\n     * 设置播放下一个\n     */\n    private void setNextVideo() {\n        //1.判断一下列表\n        if (mediaItems != null && mediaItems.size() > 0) {\n            position++;\n            if (position < mediaItems.size()) {\n                MediaItem mediaItem = mediaItems.get(position);\n\n                //显示加载页面\n                ll_loading.setVisibility(View.VISIBLE);\n                //设置标题\n                tvName.setText(mediaItem.getName());\n                isNetUrl = utils.isNetUrl(mediaItem.getData());\n                //设置播放地址\n                videoview.setVideoPath(mediaItem.getData());\n\n                //专题的校验\n                checkButtonStatus();\n\n                if (position == mediaItems.size() - 1) {\n                    Toast.makeText(this, \"哥们播放最后一个视频了哦\", Toast.LENGTH_SHORT).show();\n                }\n\n            } else {\n                //越界\n                position = mediaItems.size() - 1;\n                finish();\n            }\n        }\n        //2. 单个的uri\n        else if (uri != null) {\n            finish();\n        }\n\n\n    }\n\n    private void checkButtonStatus() {\n        //1.判断一下列表\n        if (mediaItems != null && mediaItems.size() > 0) {\n            //1.其他设置默认\n            setButtonEnable(true);\n\n            //2.播放第0个，上一个i设置灰色\n            if (position == 0) {\n                btnPre.setBackgroundResource(R.drawable.btn_pre_gray);\n                btnPre.setEnabled(false);\n            }\n            //3.播放最后一个设置，下一个按钮设置灰色\n            if (position == mediaItems.size() - 1) {\n                btnNext.setBackgroundResource(R.drawable.btn_next_gray);\n                btnNext.setEnabled(false);\n            }\n\n        }\n        //2. 单个的uri\n        else if (uri != null) {\n\n            //上一个和下一个都要设置灰色\n            setButtonEnable(false);\n        }\n    }\n\n    /***\n     * 设置按钮的可点状态\n     *\n     * @param isEnable\n     */\n    private void setButtonEnable(boolean isEnable) {\n        if (isEnable) {\n            btnPre.setBackgroundResource(R.drawable.btn_pre_selector);\n            btnNext.setBackgroundResource(R.drawable.btn_next_selector);\n\n        } else {\n            btnPre.setBackgroundResource(R.drawable.btn_pre_gray);\n            btnNext.setBackgroundResource(R.drawable.btn_next_gray);\n        }\n        btnPre.setEnabled(isEnable);\n        btnNext.setEnabled(isEnable);\n    }\n\n    class MyOnErrorListener implements MediaPlayer.OnErrorListener {\n\n        @Override\n        public boolean onError(MediaPlayer mp, int what, int extra) {\n//          //显示播放出错提醒\n            showErrorDialog();\n            return true;\n        }\n    }\n\n    private void showErrorDialog() {\n        AlertDialog.Builder builder = new AlertDialog.Builder(this);\n        builder.setTitle(\"提醒\");\n        builder.setMessage(\"播放器播放出错了，请检查视频是否损坏，或者网络中断\");\n        builder.setNegativeButton(\"确定\", new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                finish();\n            }\n        });\n        builder.show();\n    }\n\n    class MyOnPreparedListener implements MediaPlayer.OnPreparedListener {\n\n        /**\n         * 当底层加载视频准备完成的时候回调\n         *\n         * @param mp\n         */\n        @Override\n        public void onPrepared(MediaPlayer mp) {\n\n            //得到视频原始的大小\n            videoWidth = mp.getVideoWidth();\n            videoHeight = mp.getVideoHeight();\n\n            //设置默认大小\n            setVideoType(VIDEO_TYPE_DEFAULT);\n\n            //开始播放\n//            videoview.start();\n            mp.setPlaybackSpeed(1.0f);//二倍速度播放\n\n            //统计用户的行为-拖动\n//            mp.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() {\n//                @Override\n//                public void onSeekComplete(MediaPlayer mp) {\n//                    Toast.makeText(SystemVideoPlayerActivity.this, \"拖动完成了\", Toast.LENGTH_SHORT).show();\n//                }\n//            });\n            //准备好的时候\n            //1.视频的总播放时长和SeeKBar关联起来\n            int duration = (int) videoview.getDuration();\n            seekbarVideo.setMax(duration);\n\n            //设置总时长\n            tvDuration.setText(utils.stringForTime(duration));\n\n            //发消息\n            handler.sendEmptyMessage(PROGRESS);\n\n            //隐藏加载等待页面\n            ll_loading.setVisibility(View.GONE);\n\n\n        }\n    }\n\n    private float startY;\n    /**\n     * 滑动的区域\n     */\n    private int touchRang = 0;\n\n    /**\n     * 当按下的时候的音量\n     */\n    private int mVol;\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        super.onTouchEvent(event);\n        detector.onTouchEvent(event);//把事件传递给手势识别器\n        if (event.getAction() == MotionEvent.ACTION_DOWN) {\n            //1.按下\n            //按下的时候记录起始坐标，最大的滑动区域（屏幕的高），当前的音量\n            startY = event.getY();\n            touchRang = Math.min(screeHeight, screenWidth);//screeHeight\n            mVol = am.getStreamVolume(AudioManager.STREAM_MUSIC);\n            //把消息移除\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n\n\n        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {\n            float endY = event.getY();\n            //屏幕滑动的距离\n            float distanceY = startY - endY;\n            //滑动屏幕的距离 ： 总距离  = 改变的声音 ： 总声音\n\n            //改变的声音 = （滑动屏幕的距离 / 总距离)*总声音\n            float delta = (distanceY / touchRang) * maxVolume;\n            // 设置的声音  = 原来记录的 + 改变的声音\n            int volue = (int) Math.min(Math.max(mVol + delta, 0), maxVolume);\n            //判断\n            if (delta != 0) {\n                updateVoiceProgress(volue);\n            }\n\n//            startY = event.getY();//不能添加\n\n        } else if (event.getAction() == MotionEvent.ACTION_UP) {\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n        }\n        return true;\n    }\n\n    /**\n     * 得到播放地址\n     */\n    private void getData() {\n        //一个地址：从文件发起的单个播放请求\n        uri = getIntent().getData();\n        Log.e(\"TAG\", \"uri===\" + uri);\n        //得到视频列表\n        mediaItems = (ArrayList<MediaItem>) getIntent().getSerializableExtra(\"videolist\");\n        position = getIntent().getIntExtra(\"position\", 0);\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {\n            //改变音量值\n            currentVolume--;\n            updateVoiceProgress(currentVolume);\n            //移除消息\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n            //发消息\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n            return true;\n        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {\n            currentVolume++;\n            updateVoiceProgress(currentVolume);\n            handler.removeMessages(HIDE_MEDIA_CONTROLLER);\n            handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);\n            return true;\n        }\n        return super.onKeyDown(keyCode, event);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/adapter/LocalVideoAdapter.java",
    "content": "package com.atguigu.mobileplayer1020.adapter;\n\nimport android.content.Context;\nimport android.text.format.Formatter;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\nimport com.atguigu.mobileplayer1020.utils.Utils;\n\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/7 11:48\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：本地视频的适配器\n */\npublic class LocalVideoAdapter extends BaseAdapter {\n    private final Context mContext;\n    private final ArrayList<MediaItem> datas;\n    /**\n     * true:视频\n     * false:音频\n     */\n    private final boolean isVideo;\n    private Utils utils;\n\n    public LocalVideoAdapter(Context mContext, ArrayList<MediaItem> mediaItems, boolean b) {\n        this.mContext = mContext;\n        this.datas = mediaItems;\n        utils = new Utils();\n        this.isVideo = b;\n    }\n\n    @Override\n    public int getCount() {\n        return datas.size();\n    }\n\n    @Override\n    public Object getItem(int position) {\n        return null;\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return 0;\n    }\n\n    @Override\n    public View getView(int position, View convertView, ViewGroup parent) {\n        ViewHolder viewHolder;\n        if(convertView==null){\n            convertView = View.inflate(mContext, R.layout.item_local_video,null);\n            viewHolder = new ViewHolder();\n            viewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);\n            viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);\n            viewHolder.tv_duration = (TextView) convertView.findViewById(R.id.tv_duration);\n            viewHolder.tv_size = (TextView) convertView.findViewById(R.id.tv_size);\n            //设置tag\n            convertView.setTag(viewHolder);\n        }else{\n            viewHolder = (ViewHolder) convertView.getTag();\n        }\n\n        //根据位置得到对应的数据\n        MediaItem mediaItem = datas.get(position);\n        viewHolder.tv_name.setText(mediaItem.getName());//设置名称\n        //设置文件大小\n        viewHolder.tv_size.setText(Formatter.formatFileSize(mContext,mediaItem.getSize()));\n        //设置时间\n        viewHolder.tv_duration.setText(utils.stringForTime((int) mediaItem.getDuration()));\n\n        if(!isVideo){\n            //音频\n            viewHolder.iv_icon.setImageResource(R.drawable.music_default_bg);\n        }\n\n\n        return convertView;\n    }\n\n    static class ViewHolder{\n        TextView tv_name;\n        TextView tv_duration;\n        TextView tv_size;\n        ImageView iv_icon;\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/adapter/NetAudioFragmentAdapter.java",
    "content": "package com.atguigu.mobileplayer1020.adapter;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.bean.NetAudioBean;\nimport com.atguigu.mobileplayer1020.utils.Constant;\nimport com.atguigu.mobileplayer1020.utils.Utils;\nimport com.bumptech.glide.Glide;\nimport com.bumptech.glide.load.engine.DiskCacheStrategy;\n\nimport org.xutils.common.util.DensityUtil;\nimport org.xutils.image.ImageOptions;\nimport org.xutils.x;\n\nimport java.util.List;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/18 09:35\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：xxxx\n */\npublic class NetAudioFragmentAdapter extends BaseAdapter {\n    private final Context mContext;\n    private final List<NetAudioBean.ListBean> listDatas;\n\n    /**\n     * 视频\n     */\n    private static final int TYPE_VIDEO = 0;\n\n    /**\n     * 图片\n     */\n    private static final int TYPE_IMAGE = 1;\n\n    /**\n     * 文字\n     */\n    private static final int TYPE_TEXT = 2;\n\n    /**\n     * GIF图片\n     */\n    private static final int TYPE_GIF = 3;\n\n\n    /**\n     * 软件推广\n     */\n    private static final int TYPE_AD = 4;\n\n\n    public NetAudioFragmentAdapter(Context mContext, List<NetAudioBean.ListBean> listDatas) {\n        this.mContext = mContext;\n        this.listDatas = listDatas;\n    }\n\n    /**\n     * 得到列表的总数\n     * @return\n     */\n    @Override\n    public int getCount() {\n        return listDatas.size();\n    }\n\n    /*\n    根据位置得到当前Item的类型--系统提供\n     */\n    @Override\n    public int getItemViewType(int position) {\n       String type =  listDatas.get(position).getType();//video,gif,image,text,ad\n        int itemViewType = TYPE_VIDEO;\n        if (\"video\".equals(type)) {\n            itemViewType = TYPE_VIDEO;\n        } else if (\"image\".equals(type)) {\n            itemViewType = TYPE_IMAGE;\n        } else if (\"text\".equals(type)) {\n            itemViewType = TYPE_TEXT;\n        } else if (\"gif\".equals(type)) {\n            itemViewType = TYPE_GIF;\n        } else {\n            itemViewType = TYPE_AD;//广播\n        }\n        return itemViewType;\n    }\n\n    /**\n     * 得到类型总数 --系统\n     * @return\n     */\n    @Override\n    public int getViewTypeCount() {\n        return 5;\n    }\n\n    @Override\n    public Object getItem(int position) {\n        return null;\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return 0;\n    }\n\n    @Override\n    public View getView(int position, View convertView, ViewGroup parent) {\n        //根据位置得到不同类型\n        int itemViewType = getItemViewType(position);\n        //取出不同的数据\n        NetAudioBean.ListBean listBean =  listDatas.get(position);\n        convertView = initView(convertView, itemViewType, listBean);\n        return convertView;\n    }\n\n    /**\n     *\n     * @param convertView 缓存的视图\n     * @param itemViewType 类型\n     * @param mediaItem 数据\n     * @return\n     */\n    private View initView(View convertView, int itemViewType, NetAudioBean.ListBean mediaItem) {\n        switch (itemViewType) {\n            case TYPE_VIDEO://视频\n\n                VideoHoder videoHoder;\n                if (convertView == null) {\n                    convertView = View.inflate(mContext, R.layout.all_video_item, null);\n                    videoHoder = new VideoHoder(convertView);\n                    convertView.setTag(videoHoder);\n                } else {\n                    videoHoder = (VideoHoder) convertView.getTag();\n                }\n\n                //设置数据\n                videoHoder.setData(mediaItem);\n\n                break;\n            case TYPE_IMAGE://图片\n                ImageHolder imageHolder;\n                if (convertView == null) {\n                    convertView = View.inflate(mContext, R.layout.all_image_item, null);\n                    imageHolder = new ImageHolder(convertView);\n                    convertView.setTag(imageHolder);\n                } else {\n                    imageHolder = (ImageHolder) convertView.getTag();\n                }\n                //设置数据\n                imageHolder.setData(mediaItem);\n                break;\n            case TYPE_TEXT://文字\n\n                TextHolder textHolder;\n                if (convertView == null) {\n                    convertView = View.inflate(mContext, R.layout.all_text_item, null);\n                    textHolder = new TextHolder(convertView);\n\n                    convertView.setTag(textHolder);\n                } else {\n                    textHolder = (TextHolder) convertView.getTag();\n                }\n\n                textHolder.setData(mediaItem);\n\n                break;\n            case TYPE_GIF://gif\n\n                GifHolder gifHolder;\n                if (convertView == null) {\n                    convertView = View.inflate(mContext, R.layout.all_gif_item, null);\n                    gifHolder = new GifHolder(convertView);\n\n                    convertView.setTag(gifHolder);\n                } else {\n                    gifHolder = (GifHolder) convertView.getTag();\n                }\n\n                gifHolder.setData(mediaItem);\n\n                break;\n            case TYPE_AD://软件广告\n\n                ADHolder adHolder;\n                if (convertView == null) {\n                    convertView = View.inflate(mContext, R.layout.all_ad_item, null);\n                    adHolder = new ADHolder(convertView);\n                    convertView.setTag(adHolder);\n                } else {\n                    adHolder = (ADHolder) convertView.getTag();\n                }\n\n                break;\n        }\n\n        return convertView;\n    }\n\n    /**\n     * 公共的BaseViewHolder,初始化公共部分的布局\n     * 绑定公共部分的数据\n     */\n    class BaseViewHolder{\n        ImageView ivHeadpic;\n        TextView tvName;\n        TextView tvTimeRefresh;\n        ImageView ivRightMore;\n        ImageView ivVideoKind;\n        TextView tvVideoKindText;\n        TextView tvShenheDingNumber;\n        TextView tvShenheCaiNumber;\n        TextView tvPostsNumber;\n        LinearLayout llDownload;\n\n        public BaseViewHolder (View convertView) {\n            //公共的\n            ivHeadpic = (ImageView) convertView.findViewById(R.id.iv_headpic);\n            tvName = (TextView) convertView.findViewById(R.id.tv_name);\n            tvTimeRefresh = (TextView) convertView.findViewById(R.id.tv_time_refresh);\n            ivRightMore = (ImageView) convertView.findViewById(R.id.iv_right_more);\n            //bottom\n            ivVideoKind = (ImageView) convertView.findViewById(R.id.iv_video_kind);\n            tvVideoKindText = (TextView) convertView.findViewById(R.id.tv_video_kind_text);\n            tvShenheDingNumber = (TextView) convertView.findViewById(R.id.tv_shenhe_ding_number);\n            tvShenheCaiNumber = (TextView) convertView.findViewById(R.id.tv_shenhe_cai_number);\n            tvPostsNumber = (TextView) convertView.findViewById(R.id.tv_posts_number);\n            llDownload = (LinearLayout) convertView.findViewById(R.id.ll_download);\n        }\n\n        /**\n         * 设置公共部分的数据\n         * @param mediaItem\n         */\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            //设置头像\n            if (mediaItem.getU() != null && mediaItem.getU().getHeader() != null && mediaItem.getU().getHeader().get(0) != null) {\n                x.image().bind(ivHeadpic, Constant.BASE_URL+mediaItem.getU().getHeader().get(0));\n            }\n            //设置文本\n            if (mediaItem.getU() != null && mediaItem.getU().getName() != null) {\n                tvName.setText(mediaItem.getU().getName() + \"\");\n            }\n\n            tvTimeRefresh.setText(mediaItem.getPasstime());\n\n            //设置标签\n            List<NetAudioBean.ListBean.TagsBean> tagsEntities = mediaItem.getTags();\n            if (tagsEntities != null && tagsEntities.size() > 0) {\n                StringBuffer buffer = new StringBuffer();\n                for (int i = 0; i < tagsEntities.size(); i++) {\n                    buffer.append(tagsEntities.get(i).getName() + \" \");\n                }\n                tvVideoKindText.setText(buffer.toString());\n            }\n\n            //设置点赞，踩,转发\n\n            tvShenheDingNumber.setText(mediaItem.getUp());\n            tvShenheCaiNumber.setText(mediaItem.getDown() + \"\");\n            tvPostsNumber.setText(mediaItem.getForward() + \"\");\n\n        }\n    }\n\n\n    class VideoHoder extends BaseViewHolder{\n\n        Utils utils;\n        TextView tvContext;\n        JCVideoPlayerStandard jcvVideoplayer;\n        TextView tvPlayNums;\n        TextView tvVideoDuration;\n        ImageView ivCommant;\n        TextView tvCommantContext;\n\n        VideoHoder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            utils = new Utils();\n            tvPlayNums = (TextView) convertView.findViewById(R.id.tv_play_nums);\n            tvVideoDuration = (TextView) convertView.findViewById(R.id.tv_video_duration);\n            ivCommant = (ImageView) convertView.findViewById(R.id.iv_commant);\n            tvCommantContext = (TextView) convertView.findViewById(R.id.tv_commant_context);\n            jcvVideoplayer = (JCVideoPlayerStandard) convertView.findViewById(R.id.jcv_videoplayer);\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有,只有广告没有哦\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n\n            //视频特有的------------------------\n            //第一个参数是视频播放地址，第二个参数是显示封面的地址，第三参数是标题\n            boolean setUp = jcvVideoplayer.setUp( Constant.BASE_URL+mediaItem.getVideo().getVideo().get(0), JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                    \"\");\n            //加载图片\n            if (setUp) {\n                //设置默认封面\n                Glide.with(mContext).load(Constant.BASE_URL+mediaItem.getVideo().getThumbnail().get(0)).into(jcvVideoplayer.thumbImageView);\n            }\n            tvPlayNums.setText(mediaItem.getVideo().getPlaycount() + \"次播放\");\n            tvVideoDuration.setText(utils.stringForTime(mediaItem.getVideo().getDuration() * 1000) + \"\");\n\n        }\n    }\n\n    class ImageHolder extends BaseViewHolder {\n        TextView tvContext;\n        ImageView ivImageIcon;\n\n        ImageHolder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            ivImageIcon = (ImageView) convertView.findViewById(R.id.iv_image_icon);\n\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n            //图片特有的\n\n            ivImageIcon.setImageResource(R.drawable.bg_item);\n            if (mediaItem.getImage() != null && mediaItem.getImage() != null && mediaItem.getImage().getThumbnail_small() != null) {\n                Glide.with(mContext).load(Constant.BASE_URL+mediaItem.getImage().getThumbnail_small().get(0)).\n                        placeholder(R.drawable.bg_item).\n                        error(R.drawable.bg_item).\n                        diskCacheStrategy(DiskCacheStrategy.ALL).\n                        into(ivImageIcon);\n            }\n\n\n        }\n    }\n\n\n    class TextHolder extends BaseViewHolder {\n        TextView tvContext;\n\n        TextHolder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n\n\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n        }\n    }\n\n\n    class GifHolder extends BaseViewHolder {\n        TextView tvContext;\n        ImageView ivImageGif;\n        private ImageOptions imageOptions;\n\n        GifHolder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            ivImageGif = (ImageView) convertView.findViewById(R.id.iv_image_gif);\n\n            imageOptions = new ImageOptions.Builder()\n                    //包裹类型\n                    .setSize(ViewGroup.LayoutParams.WRAP_CONTENT, -2)\n                    //设置圆角\n                    .setRadius(DensityUtil.dip2px(5))\n                    .setIgnoreGif(false)//是否忽略gif图。false表示不忽略。不写这句，默认是true\n                    .setImageScaleType(ImageView.ScaleType.CENTER_CROP)\n                    .setLoadingDrawableId(R.drawable.video_default)\n                    .setFailureDrawableId(R.drawable.video_default)\n                    .build();\n\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n\n            //下面是gif\n            if (mediaItem.getGif() != null && mediaItem.getGif() != null && mediaItem.getGif().getImages() != null) {\n                Glide.with(mContext).load(Constant.BASE_URL+mediaItem.getGif().getImages().get(0)).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(ivImageGif);\n//                x.image().bind(ivImageGif, mediaItem.getGif().getImages().get(0), imageOptions);\n            }\n\n        }\n    }\n\n\n    static class ADHolder {\n        TextView tvContext;\n        ImageView ivImageIcon;\n        Button btnInstall;\n\n        ADHolder(View convertView) {\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            btnInstall = (Button) convertView.findViewById(R.id.btn_install);\n            ivImageIcon = (ImageView) convertView.findViewById(R.id.iv_image_icon);\n        }\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/adapter/NetVideoAdapter.java",
    "content": "package com.atguigu.mobileplayer1020.adapter;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\nimport com.atguigu.mobileplayer1020.utils.Constant;\nimport com.atguigu.mobileplayer1020.utils.Utils;\nimport com.bumptech.glide.Glide;\n\nimport org.xutils.common.util.DensityUtil;\nimport org.xutils.image.ImageOptions;\n\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/7 11:48\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：本地视频的适配器\n */\npublic class NetVideoAdapter extends BaseAdapter {\n    private final Context mContext;\n    private final ArrayList<MediaItem> datas;\n    private Utils utils;\n    private ImageOptions imageOptions;\n\n    public NetVideoAdapter(Context mContext, ArrayList<MediaItem> mediaItems) {\n        this.mContext = mContext;\n        this.datas = mediaItems;\n        utils = new Utils();\n\n        imageOptions = new ImageOptions.Builder()\n                .setSize(DensityUtil.dip2px(120), DensityUtil.dip2px(120))\n                .setRadius(DensityUtil.dip2px(5))\n                // 如果ImageView的大小不是定义为wrap_content, 不要crop.\n                .setCrop(true) // 很多时候设置了合适的scaleType也不需要它.\n                // 加载中或错误图片的ScaleType\n                //.setPlaceholderScaleType(ImageView.ScaleType.MATRIX)\n                .setImageScaleType(ImageView.ScaleType.CENTER_CROP)\n                .setLoadingDrawableId(R.drawable.video_default)//加载过程中的默认图片\n                .setFailureDrawableId(R.drawable.video_default)//就挨着出错的图片\n                .build();\n    }\n\n    @Override\n    public int getCount() {\n        return datas.size();\n    }\n\n    @Override\n    public Object getItem(int position) {\n        return null;\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return 0;\n    }\n\n    @Override\n    public View getView(int position, View convertView, ViewGroup parent) {\n        ViewHolder viewHolder;\n        if(convertView==null){\n            convertView = View.inflate(mContext, R.layout.item_net_video,null);\n            viewHolder = new ViewHolder();\n            viewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);\n            viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);\n            viewHolder.tv_duration = (TextView) convertView.findViewById(R.id.tv_duration);\n            viewHolder.tv_size = (TextView) convertView.findViewById(R.id.tv_size);\n            //设置tag\n            convertView.setTag(viewHolder);\n        }else{\n            viewHolder = (ViewHolder) convertView.getTag();\n        }\n\n        //根据位置得到对应的数据\n        MediaItem mediaItem = datas.get(position);\n        viewHolder.tv_name.setText(mediaItem.getName());//设置名称\n        //设置文件大小\n        viewHolder.tv_size.setText(mediaItem.getDuration()+\"秒\");\n        //设置时间\n        viewHolder.tv_duration.setText(mediaItem.getDesc());\n\n        //s使用xUtils3请求图片\n//        x.image().bind(viewHolder.iv_icon,mediaItem.getImageUrl(),imageOptions);\n        //使用Glide或者Picasso请求图片\n//        Picasso.with(mContext)\n//                .load(mediaItem.getImageUrl())\n//                .placeholder(R.drawable.video_default)\n//                .error(R.drawable.video_default)\n//                .into(viewHolder.iv_icon);\n\n\n        Glide.with(mContext)\n                .load(Constant.BASE_URL+mediaItem.getImageUrl())\n                .placeholder(R.drawable.video_default)\n                .error(R.drawable.video_default)\n                .into(viewHolder.iv_icon);\n\n\n\n        return convertView;\n    }\n\n    static class ViewHolder{\n        TextView tv_name;\n        TextView tv_duration;\n        TextView tv_size;\n        ImageView iv_icon;\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/adapter/RecyclerFragmentAdapter.java",
    "content": "package com.atguigu.mobileplayer1020.adapter;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.activity.PicassoSampleActivity;\nimport com.atguigu.mobileplayer1020.bean.NetAudioBean;\nimport com.atguigu.mobileplayer1020.utils.Constant;\nimport com.atguigu.mobileplayer1020.utils.Utils;\nimport com.bumptech.glide.Glide;\nimport com.bumptech.glide.load.engine.DiskCacheStrategy;\n\nimport org.xutils.common.util.DensityUtil;\nimport org.xutils.image.ImageOptions;\nimport org.xutils.x;\n\nimport java.util.List;\n\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayer;\nimport fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/18 16:27\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：xxxx\n */\npublic class RecyclerFragmentAdapter extends RecyclerView.Adapter {\n    private final Context mContext;\n    private final List<NetAudioBean.ListBean> listDatas;\n    /**\n     * 视频\n     */\n    private static final int TYPE_VIDEO = 0;\n\n    /**\n     * 图片\n     */\n    private static final int TYPE_IMAGE = 1;\n\n    /**\n     * 文字\n     */\n    private static final int TYPE_TEXT = 2;\n\n    /**\n     * GIF图片\n     */\n    private static final int TYPE_GIF = 3;\n\n\n    /**\n     * 软件推广\n     */\n    private static final int TYPE_AD = 4;\n\n\n    public RecyclerFragmentAdapter(Context mContext, List<NetAudioBean.ListBean> listDatas) {\n        this.mContext = mContext;\n        this.listDatas = listDatas;\n    }\n\n    @Override\n    public int getItemCount() {\n        return listDatas.size();\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        String type = listDatas.get(position).getType();//video,gif,image,text,ad\n        int itemViewType = TYPE_VIDEO;\n        if (\"video\".equals(type)) {\n            itemViewType = TYPE_VIDEO;\n        } else if (\"image\".equals(type)) {\n            itemViewType = TYPE_IMAGE;\n        } else if (\"text\".equals(type)) {\n            itemViewType = TYPE_TEXT;\n        } else if (\"gif\".equals(type)) {\n            itemViewType = TYPE_GIF;\n        } else {\n            itemViewType = TYPE_AD;//广播\n        }\n        return itemViewType;\n    }\n\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        RecyclerView.ViewHolder viewHolder = null;\n        switch (viewType) {\n            case TYPE_VIDEO://视频\n\n                viewHolder = new VideoHoder(View.inflate(mContext, R.layout.all_video_item, null));\n\n                break;\n            case TYPE_IMAGE://图片\n                viewHolder = new ImageHolder(View.inflate(mContext, R.layout.all_image_item, null));\n                break;\n            case TYPE_TEXT://文字\n\n                viewHolder = new TextHolder(View.inflate(mContext, R.layout.all_text_item, null));\n\n                break;\n            case TYPE_GIF://gif\n\n                viewHolder = new GifHolder(View.inflate(mContext, R.layout.all_gif_item, null));\n\n                break;\n            case TYPE_AD://软件广告\n                viewHolder = new ADHolder(View.inflate(mContext, R.layout.all_ad_item, null));\n                break;\n            default:\n                viewHolder = new ADHolder(View.inflate(mContext, R.layout.all_ad_item, null));\n                break;\n        }\n        return viewHolder;\n    }\n\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {\n       int itemViewType =  getItemViewType(position);\n        if(itemViewType==TYPE_VIDEO){\n            VideoHoder videoHoder = (VideoHoder) holder;\n            videoHoder.setData(listDatas.get(position));\n        } else if (itemViewType == TYPE_IMAGE) {\n            ImageHolder imageHolder = (ImageHolder) holder;\n            imageHolder.setData(listDatas.get(position));\n        } else if (itemViewType == TYPE_TEXT) {\n            TextHolder textHolder = (TextHolder) holder;\n            textHolder.setData(listDatas.get(position));\n        } else if (itemViewType == TYPE_GIF) {\n            GifHolder gifHolder = (GifHolder) holder;\n            gifHolder.setData(listDatas.get(position));\n        } else {\n            ADHolder adHolder = (ADHolder) holder;\n//            adHolder.setData(listDatas.get(position));\n        }\n    }\n\n    class BaseViewHolder extends RecyclerView.ViewHolder{\n        ImageView ivHeadpic;\n        TextView tvName;\n        TextView tvTimeRefresh;\n        ImageView ivRightMore;\n        ImageView ivVideoKind;\n        TextView tvVideoKindText;\n        TextView tvShenheDingNumber;\n        TextView tvShenheCaiNumber;\n        TextView tvPostsNumber;\n        LinearLayout llDownload;\n\n        public BaseViewHolder(View convertView) {\n            super(convertView);\n            //公共的\n            ivHeadpic = (ImageView) convertView.findViewById(R.id.iv_headpic);\n            tvName = (TextView) convertView.findViewById(R.id.tv_name);\n            tvTimeRefresh = (TextView) convertView.findViewById(R.id.tv_time_refresh);\n            ivRightMore = (ImageView) convertView.findViewById(R.id.iv_right_more);\n            //bottom\n            ivVideoKind = (ImageView) convertView.findViewById(R.id.iv_video_kind);\n            tvVideoKindText = (TextView) convertView.findViewById(R.id.tv_video_kind_text);\n            tvShenheDingNumber = (TextView) convertView.findViewById(R.id.tv_shenhe_ding_number);\n            tvShenheCaiNumber = (TextView) convertView.findViewById(R.id.tv_shenhe_cai_number);\n            tvPostsNumber = (TextView) convertView.findViewById(R.id.tv_posts_number);\n            llDownload = (LinearLayout) convertView.findViewById(R.id.ll_download);\n\n\n            //设置item的点击事件\n            itemView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    NetAudioBean.ListBean listEntity = listDatas.get(getLayoutPosition());\n                    if(listEntity !=null ){\n                        //3.传递视频列表\n                        Intent intent = new Intent(mContext,PicassoSampleActivity.class);\n                        if(listEntity.getType().equals(\"gif\")){\n                            String url = Constant.BASE_URL+listEntity.getGif().getImages().get(0);\n                            intent.putExtra(\"url\",url);\n                            mContext.startActivity(intent);\n                        }else if(listEntity.getType().equals(\"image\")){\n                            String url = Constant.BASE_URL+listEntity.getImage().getThumbnail_small().get(0);\n                            intent.putExtra(\"url\",url);\n                            mContext.startActivity(intent);\n                        }\n                    }\n                }\n            });\n\n        }\n\n        /**\n         * 设置公共部分的数据\n         * @param mediaItem\n         */\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            //设置头像\n            if (mediaItem.getU() != null && mediaItem.getU().getHeader() != null && mediaItem.getU().getHeader().get(0) != null) {\n                x.image().bind(ivHeadpic, Constant.BASE_URL+mediaItem.getU().getHeader().get(0));\n            }\n            //设置文本\n            if (mediaItem.getU() != null && mediaItem.getU().getName() != null) {\n                tvName.setText(mediaItem.getU().getName() + \"\");\n            }\n\n            tvTimeRefresh.setText(mediaItem.getPasstime());\n\n            //设置标签\n            List<NetAudioBean.ListBean.TagsBean> tagsEntities = mediaItem.getTags();\n            if (tagsEntities != null && tagsEntities.size() > 0) {\n                StringBuffer buffer = new StringBuffer();\n                for (int i = 0; i < tagsEntities.size(); i++) {\n                    buffer.append(tagsEntities.get(i).getName() + \" \");\n                }\n                tvVideoKindText.setText(buffer.toString());\n            }\n\n            //设置点赞，踩,转发\n\n            tvShenheDingNumber.setText(mediaItem.getUp());\n            tvShenheCaiNumber.setText(mediaItem.getDown() + \"\");\n            tvPostsNumber.setText(mediaItem.getForward() + \"\");\n\n        }\n    }\n\n\n    class VideoHoder extends BaseViewHolder {\n\n        Utils utils;\n        TextView tvContext;\n        JCVideoPlayerStandard jcvVideoplayer;\n        TextView tvPlayNums;\n        TextView tvVideoDuration;\n        ImageView ivCommant;\n        TextView tvCommantContext;\n\n        VideoHoder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            utils = new Utils();\n            tvPlayNums = (TextView) convertView.findViewById(R.id.tv_play_nums);\n            tvVideoDuration = (TextView) convertView.findViewById(R.id.tv_video_duration);\n            ivCommant = (ImageView) convertView.findViewById(R.id.iv_commant);\n            tvCommantContext = (TextView) convertView.findViewById(R.id.tv_commant_context);\n            jcvVideoplayer = (JCVideoPlayerStandard) convertView.findViewById(R.id.jcv_videoplayer);\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有,只有广告没有哦\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n\n            //视频特有的------------------------\n            //第一个参数是视频播放地址，第二个参数是显示封面的地址，第三参数是标题\n            boolean setUp = jcvVideoplayer.setUp(Constant.BASE_URL+ mediaItem.getVideo().getVideo().get(0), JCVideoPlayer.SCREEN_LAYOUT_LIST,\n                    \"\");\n            //加载图片\n            if (setUp) {\n                //设置默认封面\n                Glide.with(mContext).load(Constant.BASE_URL+mediaItem.getVideo().getThumbnail().get(0)).into(jcvVideoplayer.thumbImageView);\n            }\n            tvPlayNums.setText(mediaItem.getVideo().getPlaycount() + \"次播放\");\n            tvVideoDuration.setText(utils.stringForTime(mediaItem.getVideo().getDuration() * 1000) + \"\");\n\n        }\n\n\n    }\n\n    class ImageHolder extends BaseViewHolder{\n\n        TextView tvContext;\n        ImageView ivImageIcon;\n\n        ImageHolder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            ivImageIcon = (ImageView) convertView.findViewById(R.id.iv_image_icon);\n\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n            //图片特有的\n\n            ivImageIcon.setImageResource(R.drawable.bg_item);\n            if (mediaItem.getImage() != null && mediaItem.getImage() != null && mediaItem.getImage().getThumbnail_small() != null) {\n                Glide.with(mContext).load(Constant.BASE_URL+mediaItem.getImage().getThumbnail_small().get(0)).\n                        placeholder(R.drawable.bg_item).\n                        error(R.drawable.bg_item).\n                        diskCacheStrategy(DiskCacheStrategy.ALL).\n                        into(ivImageIcon);\n            }\n\n\n        }\n    }\n\n    class TextHolder extends BaseViewHolder{\n\n        TextView tvContext;\n\n        TextHolder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n\n\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n        }\n    }\n\n    class GifHolder extends BaseViewHolder{\n\n        TextView tvContext;\n        ImageView ivImageGif;\n        private ImageOptions imageOptions;\n\n        GifHolder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            ivImageGif = (ImageView) convertView.findViewById(R.id.iv_image_gif);\n\n            imageOptions = new ImageOptions.Builder()\n                    //包裹类型\n                    .setSize(ViewGroup.LayoutParams.WRAP_CONTENT, -2)\n                    //设置圆角\n                    .setRadius(DensityUtil.dip2px(5))\n                    .setIgnoreGif(false)//是否忽略gif图。false表示不忽略。不写这句，默认是true\n                    .setImageScaleType(ImageView.ScaleType.CENTER_CROP)\n                    .setLoadingDrawableId(R.drawable.video_default)\n                    .setFailureDrawableId(R.drawable.video_default)\n                    .build();\n\n        }\n\n        public void setData(NetAudioBean.ListBean mediaItem) {\n            super.setData(mediaItem);\n            //设置文本-所有的都有\n            tvContext.setText(mediaItem.getText() + \"_\" + mediaItem.getType());\n\n            //下面是gif\n            if (mediaItem.getGif() != null && mediaItem.getGif() != null && mediaItem.getGif().getImages() != null) {\n                Glide.with(mContext).load(Constant.BASE_URL+mediaItem.getGif().getImages().get(0)).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(ivImageGif);\n//                x.image().bind(ivImageGif, mediaItem.getGif().getImages().get(0), imageOptions);\n            }\n\n        }\n    }\n\n    class ADHolder extends RecyclerView.ViewHolder{\n\n        TextView tvContext;\n        ImageView ivImageIcon;\n        Button btnInstall;\n\n        ADHolder(View convertView) {\n            super(convertView);\n            //中间公共部分 -所有的都有\n            tvContext = (TextView) convertView.findViewById(R.id.tv_context);\n            btnInstall = (Button) convertView.findViewById(R.id.btn_install);\n            ivImageIcon = (ImageView) convertView.findViewById(R.id.iv_image_icon);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/adapter/SearchAdapter.java",
    "content": "package com.atguigu.mobileplayer1020.adapter;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.bean.SearchBean;\nimport com.atguigu.mobileplayer1020.utils.Utils;\nimport com.bumptech.glide.Glide;\n\nimport org.xutils.common.util.DensityUtil;\nimport org.xutils.image.ImageOptions;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/7 11:48\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：本地视频的适配器\n */\npublic class SearchAdapter extends BaseAdapter {\n    private final Context mContext;\n    private final ArrayList<SearchBean.ItemsBean> datas;\n    private Utils utils;\n    private ImageOptions imageOptions;\n\n    public SearchAdapter(Context mContext, List<SearchBean.ItemsBean> items) {\n        this.mContext = mContext;\n        this.datas = (ArrayList<SearchBean.ItemsBean>) items;\n        utils = new Utils();\n\n        imageOptions = new ImageOptions.Builder()\n                .setSize(DensityUtil.dip2px(120), DensityUtil.dip2px(120))\n                .setRadius(DensityUtil.dip2px(5))\n                // 如果ImageView的大小不是定义为wrap_content, 不要crop.\n                .setCrop(true) // 很多时候设置了合适的scaleType也不需要它.\n                // 加载中或错误图片的ScaleType\n                //.setPlaceholderScaleType(ImageView.ScaleType.MATRIX)\n                .setImageScaleType(ImageView.ScaleType.CENTER_CROP)\n                .setLoadingDrawableId(R.drawable.video_default)//加载过程中的默认图片\n                .setFailureDrawableId(R.drawable.video_default)//就挨着出错的图片\n                .build();\n    }\n\n    @Override\n    public int getCount() {\n        return datas.size();\n    }\n\n    @Override\n    public Object getItem(int position) {\n        return null;\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return 0;\n    }\n\n    @Override\n    public View getView(int position, View convertView, ViewGroup parent) {\n        ViewHolder viewHolder;\n        if(convertView==null){\n            convertView = View.inflate(mContext, R.layout.item_net_video,null);\n            viewHolder = new ViewHolder();\n            viewHolder.iv_icon = (ImageView) convertView.findViewById(R.id.iv_icon);\n            viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);\n            viewHolder.tv_duration = (TextView) convertView.findViewById(R.id.tv_duration);\n            viewHolder.tv_size = (TextView) convertView.findViewById(R.id.tv_size);\n            //设置tag\n            convertView.setTag(viewHolder);\n        }else{\n            viewHolder = (ViewHolder) convertView.getTag();\n        }\n\n        //根据位置得到对应的数据\n        SearchBean.ItemsBean mediaItem = datas.get(position);\n        viewHolder.tv_name.setText(mediaItem.getItemTitle());//设置名称\n        //设置文件大小\n        viewHolder.tv_size.setText(mediaItem.getDatecheck());\n        //设置时间\n        viewHolder.tv_duration.setText(mediaItem.getPubTime());\n\n        //s使用xUtils3请求图片\n//        x.image().bind(viewHolder.iv_icon,mediaItem.getImageUrl(),imageOptions);\n        //使用Glide或者Picasso请求图片\n//        Picasso.with(mContext)\n//                .load(mediaItem.getImageUrl())\n//                .placeholder(R.drawable.video_default)\n//                .error(R.drawable.video_default)\n//                .into(viewHolder.iv_icon);\n\n\n        Glide.with(mContext)\n                .load(mediaItem.getItemImage().getImgUrl1())\n                .placeholder(R.drawable.video_default)\n                .error(R.drawable.video_default)\n                .into(viewHolder.iv_icon);\n\n\n\n        return convertView;\n    }\n\n    static class ViewHolder{\n        TextView tv_name;\n        TextView tv_duration;\n        TextView tv_size;\n        ImageView iv_icon;\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/app/MyApplication.java",
    "content": "package com.atguigu.mobileplayer1020.app;\n\nimport android.app.Application;\n\nimport org.xutils.x;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/10 15:56\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：代表整个软件\n */\npublic class MyApplication extends Application {\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        x.Ext.init(this);\n        x.Ext.setDebug(true); // 是否输出debug日志...\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/base/BaseFragment.java",
    "content": "package com.atguigu.mobileplayer1020.base;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.support.annotation.Nullable;\nimport android.support.v4.app.Fragment;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/6 16:39\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：基类Fragment\n */\npublic abstract class BaseFragment extends Fragment {\n\n    /**\n     * 上下文\n     */\n    public Context mContext;\n\n    /**\n     * 当系统创建当前BaseFragment类的时候回调\n     * @param savedInstanceState\n     */\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        mContext = getActivity();\n    }\n\n    /**\n     * 当系统要创建Fragment的视图的时候回调这个方法\n     * @param inflater\n     * @param container\n     * @param savedInstanceState\n     * @return\n     */\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        return initView();\n    }\n\n\n\n    /**\n     * 抽象方法，让孩子实现-强制子类实现\n     *\n     * @return\n     */\n    public abstract View initView() ;\n\n    /**\n     * 当Activty创建成功的时候回调该方法\n     * 初始化数据：\n     * 联网请求数据\n     * 绑定数据\n     * @param savedInstanceState\n     */\n    @Override\n    public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n        super.onActivityCreated(savedInstanceState);\n        initData();\n    }\n\n    /**\n     *当子类需要：\n     * 1.联网请求网络，的时候重写该方法\n     * 2.绑定数据\n     */\n    public void initData() {\n\n    }\n\n\n    /**\n     *\n     * @param hidden false：当前类显示\n     *               true:当前类隐藏\n     */\n    @Override\n    public void onHiddenChanged(boolean hidden) {\n        super.onHiddenChanged(hidden);\n        Log.e(\"TAG\",\"onHiddenChanged。。\"+this.toString()+\",hidden==\"+hidden);\n        if(!hidden){\n            onRefrshData();\n        }\n\n    }\n\n\n\n    /**\n     * 当子类要刷新数据的时候重写该方法\n     */\n    public void onRefrshData() {\n\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/bean/LyricBean.java",
    "content": "package com.atguigu.mobileplayer1020.bean;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/13 14:24\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：一句歌词\n * [00:27.24]听个小姐说 她一月八千真的不多\n */\npublic class LyricBean {\n    /**\n     * 歌词内容\n     */\n    private String content;\n\n    /**\n     * 时间戳\n     */\n    private long timePoint;\n    /**\n     * 高亮时间\n     */\n    private long sleepTime;\n\n    public String getContent() {\n        return content;\n    }\n\n    public void setContent(String content) {\n        this.content = content;\n    }\n\n    public long getTimePoint() {\n        return timePoint;\n    }\n\n    public void setTimePoint(long timePoint) {\n        this.timePoint = timePoint;\n    }\n\n    public long getSleepTime() {\n        return sleepTime;\n    }\n\n    public void setSleepTime(long sleepTime) {\n        this.sleepTime = sleepTime;\n    }\n\n    @Override\n    public String toString() {\n        return \"LyricBean{\" +\n                \"content='\" + content + '\\'' +\n                \", timePoint=\" + timePoint +\n                \", sleepTime=\" + sleepTime +\n                '}';\n    }\n}\n\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/bean/MediaItem.java",
    "content": "package com.atguigu.mobileplayer1020.bean;\n\nimport java.io.Serializable;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/7 11:39\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用： 代码一个视频或者音频\n */\npublic class MediaItem implements Serializable{\n\n    String name ;\n    long duration ;\n    long size ;\n    String data ;\n    String artist ;\n    /**\n     * 图片路径\n     */\n    String imageUrl;\n    /**\n     * 描述\n     */\n    String desc;\n\n    String heightUrl;\n\n    public String getHeightUrl() {\n        return heightUrl;\n    }\n\n    public void setHeightUrl(String heightUrl) {\n        this.heightUrl = heightUrl;\n    }\n\n    public String getImageUrl() {\n        return imageUrl;\n    }\n\n    public void setImageUrl(String imageUrl) {\n        this.imageUrl = imageUrl;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public void setDesc(String desc) {\n        this.desc = desc;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public long getDuration() {\n        return duration;\n    }\n\n    public void setDuration(long duration) {\n        this.duration = duration;\n    }\n\n    public long getSize() {\n        return size;\n    }\n\n    public void setSize(long size) {\n        this.size = size;\n    }\n\n    public String getData() {\n        return data;\n    }\n\n    public void setData(String data) {\n        this.data = data;\n    }\n\n    public String getArtist() {\n        return artist;\n    }\n\n    public void setArtist(String artist) {\n        this.artist = artist;\n    }\n\n    @Override\n    public String toString() {\n        return \"MediaItem{\" +\n                \"name='\" + name + '\\'' +\n                \", duration=\" + duration +\n                \", size=\" + size +\n                \", data='\" + data + '\\'' +\n                \", artist='\" + artist + '\\'' +\n                \", imageUrl='\" + imageUrl + '\\'' +\n                \", desc='\" + desc + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/bean/NetAudioBean.java",
    "content": "package com.atguigu.mobileplayer1020.bean;\n\nimport java.util.List;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/18 09:25\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：网络音频的数据\n */\npublic class NetAudioBean {\n\n    /**\n     * info : {\"count\":4430,\"np\":1484664361}\n     * list : [{\"status\":4,\"comment\":\"24\",\"tags\":[{\"id\":30157,\"name\":\"百思原创\"},{\"id\":62,\"name\":\"内涵\"},{\"id\":4670,\"name\":\"涨姿势\"}],\"bookmark\":\"3\",\"text\":\"这道\\u201c成人料理\\u201d，你学会了吗？\",\"gif\":{\"images\":[\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24.gif\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24.gif\"],\"width\":400,\"gif_thumbnail\":[\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\"],\"height\":1781},\"up\":\"106\",\"share_url\":\"http://a.f.budejie.com/share/23183944.html?wx.qq.com\",\"down\":15,\"forward\":6,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/09/22/57e3d9083f5e3_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/09/22/57e3d9083f5e3_mini.jpg\"],\"uid\":\"18614232\",\"is_vip\":true,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"舞凡秀\",\"room_role\":\"帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_1.png\",\"name\":\"原生君 [舞凡秀]\"},\"passtime\":\"2017-01-18 09:12:02\",\"type\":\"gif\",\"id\":\"23183944\"},{\"status\":4,\"comment\":\"6\",\"top_comments\":[{\"voicetime\":0,\"status\":2,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"9个赞 10个踩也能上精华？你特么在逗我？\",\"like_count\":1,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\"],\"uid\":\"15512124\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Lgg丶\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:10:19\",\"voiceuri\":\"\",\"id\":72883349},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"没有跑了。！头！象v！恭喜\",\"like_count\":0,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2017/01/17/587da086205ff_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2017/01/17/587da086205ff_mini.jpg\"],\"uid\":\"20059417\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"f\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"质料里啦\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:10:22\",\"voiceuri\":\"\",\"id\":72883352},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"女的丑死了\",\"like_count\":0,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/59A1F296A4379B672987679362B93363/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/59A1F296A4379B672987679362B93363/100\"],\"uid\":\"18648939\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Mylove000\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:11:50\",\"voiceuri\":\"\",\"id\":72883403}],\"tags\":[{\"id\":18910,\"name\":\"hx\"},{\"id\":1,\"name\":\"搞笑\"},{\"id\":58,\"name\":\"跳舞\"}],\"bookmark\":\"4\",\"text\":\"火力全开，哥们你受得了这些小电臀吗？\",\"up\":\"101\",\"share_url\":\"http://a.f.budejie.com/share/23213214.html?wx.qq.com\",\"down\":14,\"forward\":6,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/07/05/577b7f3a0e43e_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/07/05/577b7f3a0e43e_mini.jpg\"],\"uid\":\"7705011\",\"is_vip\":true,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"百思葫芦娃\",\"room_role\":\"帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_8.png\",\"name\":\"我是星辰 [百思葫芦娃]\"},\"passtime\":\"2017-01-18 09:00:01\",\"video\":{\"playfcount\":1293,\"height\":640,\"width\":480,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\"],\"duration\":22,\"playcount\":9306,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\"]},\"type\":\"video\",\"id\":\"23213214\"},{\"status\":4,\"comment\":\"56\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"哪里是粉色的，你说清楚\",\"like_count\":238,\"u\":{\"header\":[\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\",\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\"],\"uid\":\"15129882\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"FutNotice\"},\"preuid\":0,\"passtime\":\"2015-12-10 11:31:11\",\"voiceuri\":\"\",\"id\":38864637},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"我来解释下，就算没被日过，那里也不是粉红色的。主要跟穿裤子摩擦有关\",\"like_count\":12,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2015/10/26/562e3e915e9f4_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2015/10/26/562e3e915e9f4_mini.jpg\"],\"uid\":\"16110503\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"风靡飘香\"},\"preuid\":0,\"passtime\":\"2015-12-11 16:17:32\",\"voiceuri\":\"\",\"id\":38944210},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"我也粉嫩啊～还没毕业\",\"like_count\":8,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2015/11/22/565110f95ac60_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2015/11/22/565110f95ac60_mini.jpg\"],\"uid\":\"16682327\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"f\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"心为谁乖\"},\"preuid\":0,\"passtime\":\"2015-12-11 09:06:42\",\"voiceuri\":\"\",\"id\":38924884}],\"tags\":[{\"id\":61,\"name\":\"恶搞\"},{\"id\":64,\"name\":\"糗事\"},{\"id\":60,\"name\":\"吐槽\"}],\"bookmark\":\"33\",\"text\":\"以前上大学的时候，看见大四学姐，感觉这么老的女人有谁要啊。\\n现在工作了，看到刚分配进来的女大学生，我靠，怎么会有这么粉嫩的女孩子。\",\"up\":\"2574\",\"share_url\":\"http://a.f.budejie.com/share/16518335.html?wx.qq.com\",\"down\":797,\"forward\":12,\"top_comment\":{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"哪里是粉色的，你说清楚\",\"like_count\":238,\"u\":{\"header\":[\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\",\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\"],\"uid\":\"15129882\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"FutNotice\"},\"preuid\":0,\"passtime\":\"2015-12-10 11:31:11\",\"voiceuri\":\"\",\"id\":38864637},\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2015/10/28/5630289605d1f_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2015/10/28/5630289605d1f_mini.jpg\"],\"uid\":\"8489979\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"豆子请来的猴比\"},\"passtime\":\"2017-01-18 08:51:04\",\"type\":\"text\",\"id\":\"16518335\"},{\"status\":4,\"comment\":\"29\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":2,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"插眼，插眼，插尼玛比的，这种回复让人最想问候你\",\"like_count\":5,\"u\":{\"header\":[\"http://wx.qlogo.cn/mmopen/NsXicLUicickpcPia8G7PqRyY8FYrtbx1bvcJNEdsY1roKhpzibMfiacGyT4NvznAUOx1jXNDoSrGNMJtQLibicr4wqI7WpiaPcfhwYBs/0\",\"http://wx.qlogo.cn/mmopen/NsXicLUicickpcPia8G7PqRyY8FYrtbx1bvcJNEdsY1roKhpzibMfiacGyT4NvznAUOx1jXNDoSrGNMJtQLibicr4wqI7WpiaPcfhwYBs/0\"],\"uid\":\"19753245\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"vivo柔道白带九段充气娃娃\"},\"preuid\":0,\"passtime\":\"2017-01-18 03:12:54\",\"voiceuri\":\"\",\"id\":72876274},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"www.tepian.com   搜索善良的嫂子\",\"like_count\":0,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/5DBA9E86913A5431483FAEADDB3E7EFA/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/5DBA9E86913A5431483FAEADDB3E7EFA/100\"],\"uid\":\"19294644\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"我叫你傻叼\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:11:47\",\"voiceuri\":\"\",\"id\":72883402}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":28,\"name\":\"动态图\"},{\"id\":62,\"name\":\"内涵\"}],\"bookmark\":\"40\",\"text\":\"这一定是幻觉，我还没有睡醒？！\",\"gif\":{\"images\":[\"http://wimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96.gif\",\"http://dimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96.gif\"],\"width\":271,\"gif_thumbnail\":[\"http://wimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96_a_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/18/587e45cecee96_a_1.jpg\"],\"height\":157},\"up\":\"198\",\"share_url\":\"http://a.f.budejie.com/share/23227415.html?wx.qq.com\",\"down\":23,\"forward\":4,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/12/26/586059118dd30_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/12/26/586059118dd30_mini.jpg\"],\"uid\":\"18619888\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"BS嫖基金\",\"room_role\":\"副帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_17.png\",\"name\":\"搞笑小村 [BS嫖基金]\"},\"passtime\":\"2017-01-18 08:46:01\",\"type\":\"gif\",\"id\":\"23227415\"},{\"status\":4,\"comment\":\"20\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"黄西，超有才华的。\",\"like_count\":5,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/CD8E52516339A4469BF7E85AD4FA122B/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/CD8E52516339A4469BF7E85AD4FA122B/100\"],\"uid\":\"14928129\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Superman777\"},\"preuid\":0,\"passtime\":\"2017-01-16 23:03:04\",\"voiceuri\":\"\",\"id\":72805873}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":55,\"name\":\"微视频\"},{\"id\":329,\"name\":\"生活\"},{\"id\":3176,\"name\":\"冷知识\"},{\"id\":4375,\"name\":\"综艺\"}],\"bookmark\":\"47\",\"text\":\"过年期间低调炫富的11种方法\\u2014\\u2014我抬头看看天上有没有牛在飞！\",\"up\":\"355\",\"share_url\":\"http://a.f.budejie.com/share/23208122.html?wx.qq.com\",\"down\":61,\"forward\":69,\"u\":{\"header\":[\"http://img.spriteapp.cn/profile/20160310104526.jpg\",\"http://img.spriteapp.cn/profile/20160310104526.jpg\"],\"uid\":\"17738731\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"是真的吗\"},\"passtime\":\"2017-01-18 08:35:02\",\"video\":{\"playfcount\":1886,\"height\":352,\"width\":640,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0116/587cd4a20adf5_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/587cd4a20adf5_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0116/587cd4a20adf5_wpc.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/587cd4a20adf5_wpc.mp4\"],\"duration\":149,\"playcount\":14958,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0116/587cd4a20adf5_wpd_17.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0116/587cd4a20adf5_wpd_17.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0116/587cd4a20adf5_wpd_17.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0116/587cd4a20adf5_wpd_17.jpg\"]},\"type\":\"video\",\"id\":\"23208122\"},{\"status\":4,\"comment\":\"52\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"。。。。熟悉的五毛又回来了\",\"like_count\":70,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/CB367DE071301FCE430ABBE09B473F2D/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/CB367DE071301FCE430ABBE09B473F2D/100\"],\"uid\":\"15582926\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"米佳烈烈罗\"},\"preuid\":0,\"passtime\":\"2017-01-15 19:05:14\",\"voiceuri\":\"\",\"id\":72736303},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"5毛的团队都能做出这样的特效，为什么电视上的不行\",\"like_count\":38,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/08/28/57c26049e7c29_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/08/28/57c26049e7c29_mini.jpg\"],\"uid\":\"15663363\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"原来名字真的可以起这样长\"},\"preuid\":0,\"passtime\":\"2017-01-16 00:14:03\",\"voiceuri\":\"\",\"id\":72755567},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"这个不止五毛，起码一块\",\"like_count\":16,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/08/15/57b199c0eb4f7_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/08/15/57b199c0eb4f7_mini.jpg\"],\"uid\":\"18176642\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"三界舞乾坤\"},\"preuid\":0,\"passtime\":\"2017-01-17 14:49:45\",\"voiceuri\":\"\",\"id\":72839216}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":115,\"name\":\"原创\"},{\"id\":3096,\"name\":\"百思红人\"},{\"id\":14047,\"name\":\"5毛特效\"},{\"id\":15194,\"name\":\"5毛团队\"}],\"bookmark\":\"13\",\"text\":\"高手对决，这才叫真正的5毛特效\",\"up\":\"874\",\"share_url\":\"http://a.f.budejie.com/share/23187416.html?wx.qq.com\",\"down\":54,\"forward\":32,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/04/13/570de778028b0_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/04/13/570de778028b0_mini.jpg\"],\"uid\":\"18042900\",\"is_vip\":false,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"5毛团队\"},\"passtime\":\"2017-01-18 08:10:02\",\"video\":{\"playfcount\":17354,\"height\":800,\"width\":600,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0115/587b4fbc89133_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0115/587b4fbc89133_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0115/587b4fbc89133_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0115/587b4fbc89133_wpd.mp4\"],\"duration\":23,\"playcount\":56737,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0115/587b4fbc4850c__b_37.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0115/587b4fbc4850c__b_37.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0115/587b4fbc4850c__b_37.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0115/587b4fbc4850c__b_37.jpg\"]},\"type\":\"video\",\"id\":\"23187416\"},{\"status\":4,\"comment\":\"61\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":2,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"看第一张我还以为他妈文章！\",\"like_count\":39,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/E89B98C0981BCCE90864180258AD3898/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/E89B98C0981BCCE90864180258AD3898/100\"],\"uid\":\"13609270\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Troublemaker333\"},\"preuid\":0,\"passtime\":\"2017-01-17 19:18:04\",\"voiceuri\":\"\",\"id\":72851566},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"是艺术就别打码，是色情就请别发。\",\"like_count\":10,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2015/12/01/565da81f18dc2_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2015/12/01/565da81f18dc2_mini.jpg\"],\"uid\":\"15881479\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"f\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Fetter-似懂非懂\"},\"preuid\":0,\"passtime\":\"2017-01-18 08:36:08\",\"voiceuri\":\"\",\"id\":72882072}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":60,\"name\":\"吐槽\"},{\"id\":117,\"name\":\"美女\"},{\"id\":922,\"name\":\"尤物\"},{\"id\":7723,\"name\":\"极品\"}],\"bookmark\":\"112\",\"text\":\"这波COS兔女郎水准有点高啊~\",\"image\":{\"medium\":[],\"big\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08.jpg\"],\"height\":9364,\"width\":710,\"small\":[],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587dc4f94bc08.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587dc4f94bc08.jpg\"]},\"up\":\"173\",\"share_url\":\"http://a.f.budejie.com/share/23218451.html?wx.qq.com\",\"down\":43,\"forward\":20,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/05/01/572555a248804_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/05/01/572555a248804_mini.jpg\"],\"uid\":\"18192453\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"菊花姐夫\"},\"passtime\":\"2017-01-18 08:02:02\",\"type\":\"image\",\"id\":\"23218451\"},{\"status\":4,\"comment\":\"18\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"室内  高清   无码  中文字幕\",\"like_count\":10,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2017/01/02/586a2e93d62f7_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2017/01/02/586a2e93d62f7_mini.jpg\"],\"uid\":\"17842087\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"风吹丶奶罩飞\"},\"preuid\":0,\"passtime\":\"2017-01-17 15:23:48\",\"voiceuri\":\"\",\"id\":72840728},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"男朋友：我的天哪！明明是旺仔小馒头！还骗我说小白兔！\",\"like_count\":2,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/05/17/573a77de22f7b_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/05/17/573a77de22f7b_mini.jpg\"],\"uid\":\"17836168\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"宠爱它\"},\"preuid\":0,\"passtime\":\"2017-01-18 08:28:31\",\"voiceuri\":\"\",\"id\":72881793},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"老段子了，最后她男友把她绑好给我送过来了。\",\"like_count\":0,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/4D4D694766F5A3AFBFF85D13DB4B5D18/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/4D4D694766F5A3AFBFF85D13DB4B5D18/100\"],\"uid\":\"10813814\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"他大舅的二姨夫\"},\"preuid\":0,\"passtime\":\"2017-01-18 08:24:28\",\"voiceuri\":\"\",\"id\":72881673}],\"tags\":[{\"id\":61,\"name\":\"恶搞\"},{\"id\":62,\"name\":\"内涵\"},{\"id\":64,\"name\":\"糗事\"}],\"bookmark\":\"6\",\"text\":\"带男朋友回家。我拉着他的手说：\\u201c我养了2只大白兔，你要不要看？\\u201d说完，我准备拉他去阳台。结果他愣愣地看着我\\u201c真的吗？\\u201d然后，把我推到在沙发上\\u2026\\u2026\\u2026\",\"up\":\"210\",\"share_url\":\"http://a.f.budejie.com/share/23210345.html?wx.qq.com\",\"down\":40,\"forward\":4,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/10/11/57fcb4e17513a_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/10/11/57fcb4e17513a_mini.jpg\"],\"uid\":\"19490048\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"青璃FF\"},\"passtime\":\"2017-01-18 07:51:20\",\"type\":\"text\",\"id\":\"23210345\"},{\"status\":4,\"comment\":\"21\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":2,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"好久没看到这么好的作品了，厉害了，转了～\",\"like_count\":20,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/10/19/5806e1f95f9ad_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/10/19/5806e1f95f9ad_mini.jpg\"],\"uid\":\"18175863\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"巭孬嫑夯茓勥昆烎菿奣\"},\"preuid\":0,\"passtime\":\"2017-01-17 13:05:02\",\"voiceuri\":\"\",\"id\":72834358},{\"voicetime\":0,\"status\":0,\"hate_count\":1,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"美中不足的是不应该说潘长江，人家也算个艺术家。换成拍爵迹那个导演，我觉得就对了！\",\"like_count\":12,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/02/20/56c7dd165bb3a_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/02/20/56c7dd165bb3a_mini.jpg\"],\"uid\":\"17540296\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"小道不才\"},\"preuid\":0,\"passtime\":\"2017-01-18 08:13:12\",\"voiceuri\":\"\",\"id\":72881284}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":1788,\"name\":\"小品\"},{\"id\":18910,\"name\":\"hx\"},{\"id\":4375,\"name\":\"综艺\"}],\"bookmark\":\"218\",\"text\":\"郭阳郭亮最新小品《考场风波》，这对活宝，太逗了\",\"up\":\"1085\",\"share_url\":\"http://a.f.budejie.com/share/23216451.html?wx.qq.com\",\"down\":43,\"forward\":236,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/12/21/585a18d78f9e8_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/12/21/585a18d78f9e8_mini.jpg\"],\"uid\":\"15425720\",\"is_vip\":true,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"江南皮革厂\",\"room_role\":\"头目\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_17.png\",\"name\":\"清欢十年温如水灬 [江南皮革厂]\"},\"passtime\":\"2017-01-18 07:45:02\",\"video\":{\"playfcount\":3259,\"height\":254,\"width\":456,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpc.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpc.mp4\"],\"duration\":775,\"playcount\":25513,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpd.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0117/be004588-dc6d-11e6-8da6-d4ae5296039d_wpd.jpg\"]},\"type\":\"video\",\"id\":\"23216451\"},{\"status\":4,\"comment\":49,\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":7,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"歌坛里有几个人敢不敬李宗盛的！\",\"like_count\":84,\"u\":{\"header\":[\"http://wx.qlogo.cn/mmopen/2EzJggZltBOOQaI0jqW38l8N2tmm6C2kOjrIyE6YdiczkAye3Q1qE53uzLIaxJX8OQ5NvjFX1HqJumoVQ8A3arQ/0\",\"http://wx.qlogo.cn/mmopen/2EzJggZltBOOQaI0jqW38l8N2tmm6C2kOjrIyE6YdiczkAye3Q1qE53uzLIaxJX8OQ5NvjFX1HqJumoVQ8A3arQ/0\"],\"uid\":\"19882445\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"左道d01\"},\"preuid\":0,\"passtime\":\"2017-01-17 14:22:39\",\"voiceuri\":\"\",\"id\":72838118},{\"voicetime\":0,\"status\":0,\"hate_count\":3,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"李宗盛大哥属音乐界传奇，赞一个\",\"like_count\":28,\"u\":{\"header\":[\"http://wx.qlogo.cn/mmopen/ZeuSZYeUCFKfGe1ufic7YAYl2qkZpWP1KbFzvOianvyJYpZNhEwicYHC8uO5Y6Jp3tsn3n4JThCbe7f1p7W8CveYwpbicRkX9aK5/0\",\"http://wx.qlogo.cn/mmopen/ZeuSZYeUCFKfGe1ufic7YAYl2qkZpWP1KbFzvOianvyJYpZNhEwicYHC8uO5Y6Jp3tsn3n4JThCbe7f1p7W8CveYwpbicRkX9aK5/0\"],\"uid\":\"19894585\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"天涯vkq\"},\"preuid\":0,\"passtime\":\"2017-01-18 07:56:32\",\"voiceuri\":\"\",\"id\":72880761},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"你我皆凡人，生在人世间。终日奔波苦，一刻不得闲。\",\"like_count\":9,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/E7B7B303ED05E092D3A97C47F401D928/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/E7B7B303ED05E092D3A97C47F401D928/100\"],\"uid\":\"13842895\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"告别碎羽321\"},\"preuid\":0,\"passtime\":\"2017-01-18 07:59:29\",\"voiceuri\":\"\",\"id\":72880858}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":62,\"name\":\"内涵\"},{\"id\":167,\"name\":\"明星\"},{\"id\":2611,\"name\":\"经典\"}],\"bookmark\":\"90\",\"text\":\"年少不听李宗盛，听懂已是不惑年！\",\"image\":{\"medium\":[],\"big\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587daba3b5313_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587daba3b5313_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587daba3b5313_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587daba3b5313_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587daba3b5313.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587daba3b5313.jpg\"],\"height\":8477,\"width\":440,\"small\":[],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587daba3b5313.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587daba3b5313.jpg\"]},\"up\":\"496\",\"share_url\":\"http://a.f.budejie.com/share/23217187.html?wx.qq.com\",\"down\":49,\"forward\":78,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2017/01/11/58758bd42f6a1_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2017/01/11/58758bd42f6a1_mini.jpg\"],\"uid\":\"19809241\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"恶魔猎手\",\"room_role\":\"副帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_1.png\",\"name\":\"与君共醉 [恶魔猎手]\"},\"passtime\":\"2017-01-18 07:36:01\",\"type\":\"image\",\"id\":\"23217187\"},{\"status\":4,\"comment\":\"75\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"我老家那就是这样办酒席的，咋啦，碍着你啦\",\"like_count\":127,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/08/09/57a9d90bbe5eb_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/08/09/57a9d90bbe5eb_mini.jpg\"],\"uid\":\"15827200\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"f\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"华夏五千年\"},\"preuid\":0,\"passtime\":\"2017-01-17 17:29:46\",\"voiceuri\":\"\",\"id\":72846501},{\"voicetime\":0,\"status\":0,\"hate_count\":3,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"把你表姑叫过来，我怼她一下\",\"like_count\":78,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/CC19647832806B62C52FD2F0ACB3607B/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/CC19647832806B62C52FD2F0ACB3607B/100\"],\"uid\":\"20045133\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"132132132\"},\"preuid\":0,\"passtime\":\"2017-01-18 07:28:41\",\"voiceuri\":\"\",\"id\":72880031},{\"voicetime\":0,\"status\":2,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"你想表达什么意思？？\",\"like_count\":0,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/10/21/5809a94358f66_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/10/21/5809a94358f66_mini.jpg\"],\"uid\":\"7797725\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"百思送葬大队_队长\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:09:23\",\"voiceuri\":\"\",\"id\":72883306}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":60,\"name\":\"吐槽\"}],\"bookmark\":\"4\",\"text\":\"城里表姐嫁给了农村小伙，看到新郎的婚车后，表姑就吵着闹离婚\",\"image\":{\"medium\":[],\"big\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587d91d93e09e_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587d91d93e09e_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587d91d93e09e_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587d91d93e09e_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587d91d93e09e.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587d91d93e09e.jpg\"],\"height\":5505,\"width\":572,\"small\":[],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587d91d93e09e.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587d91d93e09e.jpg\"]},\"up\":\"179\",\"share_url\":\"http://a.f.budejie.com/share/23215745.html?wx.qq.com\",\"down\":100,\"forward\":9,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/10/18/5805e84841790_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/10/18/5805e84841790_mini.jpg\"],\"uid\":\"15076679\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"一轩2009\"},\"passtime\":\"2017-01-18 07:10:02\",\"type\":\"image\",\"id\":\"23215745\"},{\"status\":4,\"comment\":\"33\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"为了啪啪啪找这么多理由！\",\"like_count\":34,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile\",\"http://dimg.spriteapp.cn/profile\"],\"uid\":\"17269815\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"百思送葬集团礼仪队队长\"},\"preuid\":0,\"passtime\":\"2017-01-17 07:02:39\",\"voiceuri\":\"\",\"id\":72819894},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"老夫看完视频，掐指一算，必有一战。\",\"like_count\":24,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2015/05/15/5554e3b8c0ab9_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2015/05/15/5554e3b8c0ab9_mini.jpg\"],\"uid\":\"14703035\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"大老二K\"},\"preuid\":0,\"passtime\":\"2017-01-17 03:50:46\",\"voiceuri\":\"\",\"id\":72817796},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"一般，妹子那么有情趣，一会儿都会吃香蕉\",\"like_count\":22,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/12/24/585dfdfa55d32_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/12/24/585dfdfa55d32_mini.jpg\"],\"uid\":\"8252550\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"小鹏D8D\"},\"preuid\":0,\"passtime\":\"2017-01-17 13:52:30\",\"voiceuri\":\"\",\"id\":72836772}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":55,\"name\":\"微视频\"},{\"id\":60,\"name\":\"吐槽\"},{\"id\":61,\"name\":\"恶搞\"},{\"id\":62,\"name\":\"内涵\"},{\"id\":117,\"name\":\"美女\"},{\"id\":11117,\"name\":\"套路\"},{\"id\":18910,\"name\":\"hx\"}],\"bookmark\":\"197\",\"text\":\"让男友降低智商的四个问题，满满的套路，另一半必看！\",\"up\":\"697\",\"share_url\":\"http://a.f.budejie.com/share/23211678.html?wx.qq.com\",\"down\":105,\"forward\":140,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/09/20/57e0cd620e99f_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/09/20/57e0cd620e99f_mini.jpg\"],\"uid\":\"15262111\",\"is_vip\":true,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"快乐驿站\",\"room_role\":\"帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_10.png\",\"name\":\"共醉江湖 [快乐驿站]\"},\"passtime\":\"2017-01-18 06:50:01\",\"video\":{\"playfcount\":7596,\"height\":480,\"width\":854,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpc.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpc.mp4\"],\"duration\":118,\"playcount\":34822,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpd.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0117/c2641a8c-dc13-11e6-8398-90b11c479401cut_wpd.jpg\"]},\"type\":\"video\",\"id\":\"23211678\"},{\"status\":4,\"comment\":\"79\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":1,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"我敢打赌在场的每一个男的都想干她\",\"like_count\":62,\"u\":{\"header\":[\"http://wx.qlogo.cn/mmopen/ZeuSZYeUCFJcbImXmGdUlcZlNqbibO0emkDQRS1MTNRxf1vcBcgA3gXEtzLQ64TlCicC4IUVcXAZyaibXMfjyKLR96nZx0cbeBs/0\",\"http://wx.qlogo.cn/mmopen/ZeuSZYeUCFJcbImXmGdUlcZlNqbibO0emkDQRS1MTNRxf1vcBcgA3gXEtzLQ64TlCicC4IUVcXAZyaibXMfjyKLR96nZx0cbeBs/0\"],\"uid\":\"19942365\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"用户etFaiq\"},\"preuid\":0,\"passtime\":\"2017-01-17 20:41:05\",\"voiceuri\":\"\",\"id\":72855759},{\"voicetime\":0,\"status\":2,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"给拖地的大妈加个蛋\",\"like_count\":1,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/01/24/56a48c5c4123e_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/01/24/56a48c5c4123e_mini.jpg\"],\"uid\":\"17289769\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"卢二爷嗯嗯\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:14:05\",\"voiceuri\":\"\",\"id\":72883496},{\"voicetime\":0,\"status\":2,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"😘\",\"like_count\":1,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/09/26/57e7fba9e592f_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/09/26/57e7fba9e592f_mini.jpg\"],\"uid\":\"14291376\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"f\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"蓝翔高级技工学校校草\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:13:09\",\"voiceuri\":\"\",\"id\":72883458}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":61,\"name\":\"恶搞\"},{\"id\":7723,\"name\":\"极品\"}],\"bookmark\":\"73\",\"text\":\"厉害了妹子，就你这功夫这姿势，我给你十分！给地板九十分！\",\"gif\":{\"images\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66.gif\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66.gif\"],\"width\":225,\"gif_thumbnail\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66_a_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587e05cd88c66_a_1.jpg\"],\"height\":284},\"up\":\"380\",\"share_url\":\"http://a.f.budejie.com/share/23221970.html?wx.qq.com\",\"down\":28,\"forward\":28,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2017/01/18/587ea8ab38da4_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2017/01/18/587ea8ab38da4_mini.jpg\"],\"uid\":\"20060906\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"吉祥如意\",\"room_role\":\"\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_1.png\",\"name\":\"笑公馆V [吉祥如意]\"},\"passtime\":\"2017-01-18 06:44:02\",\"type\":\"gif\",\"id\":\"23221970\"},{\"status\":4,\"comment\":\"79\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"同城的帮忙打一下。\",\"like_count\":113,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/06/24/576cdc0544a37_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/06/24/576cdc0544a37_mini.jpg\"],\"uid\":\"10065227\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"红色贝鱼88\"},\"preuid\":0,\"passtime\":\"2017-01-15 15:13:42\",\"voiceuri\":\"\",\"id\":72724659},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"2017之最流行大号护舒宝发型\",\"like_count\":18,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/04/20/5716b37ea4a58_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/04/20/5716b37ea4a58_mini.jpg\"],\"uid\":\"5305777\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"嵗月\"},\"preuid\":0,\"passtime\":\"2017-01-18 07:25:13\",\"voiceuri\":\"\",\"id\":72879951},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"当年的八神庵的发型 祸害了多少人\",\"like_count\":0,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/CF954AF0BFD29629F2C4F14415988540/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/CF954AF0BFD29629F2C4F14415988540/100\"],\"uid\":\"11137628\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"恶魔X777\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:12:04\",\"voiceuri\":\"\",\"id\":72883414}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":55,\"name\":\"微视频\"},{\"id\":60,\"name\":\"吐槽\"},{\"id\":61,\"name\":\"恶搞\"},{\"id\":62,\"name\":\"内涵\"}],\"bookmark\":\"18\",\"text\":\"快过年了，换个发型换个心情..大家觉得如何\",\"up\":\"413\",\"share_url\":\"http://a.f.budejie.com/share/23185109.html?wx.qq.com\",\"down\":89,\"forward\":48,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/10/16/580334fcde3f5_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/10/16/580334fcde3f5_mini.jpg\"],\"uid\":\"18262333\",\"is_vip\":false,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"百思UFO卡特\",\"room_role\":\"帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_12.png\",\"name\":\"夜未央之叶秋 [百思UFO卡特]\"},\"passtime\":\"2017-01-18 06:25:02\",\"video\":{\"playfcount\":10504,\"height\":446,\"width\":360,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.mp4\"],\"duration\":12,\"playcount\":33265,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0115/242eb89c-daef-11e6-8366-90b11c479401_wpd.jpg\"]},\"type\":\"video\",\"id\":\"23185109\"},{\"status\":4,\"comment\":\"50\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"我只想说四个字   干的漂亮\",\"like_count\":61,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/9562052D50E3FA1BA2B87CB796CC5F54/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/9562052D50E3FA1BA2B87CB796CC5F54/100\"],\"uid\":\"18742671\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"4G了\"},\"preuid\":0,\"passtime\":\"2017-01-17 19:35:56\",\"voiceuri\":\"\",\"id\":72852375},{\"voicetime\":0,\"status\":2,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"他是怎么开进去的\",\"like_count\":0,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/6A4913F3BE8813A353DF7039BAD224F3/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/6A4913F3BE8813A353DF7039BAD224F3/100\"],\"uid\":\"18110980\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"羊羊羊RER\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:04:56\",\"voiceuri\":\"\",\"id\":72883138}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":61,\"name\":\"恶搞\"}],\"bookmark\":\"16\",\"text\":\"乱停车的下场，这结果我表示很满意。\",\"gif\":{\"images\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc.gif\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc.gif\"],\"width\":230,\"gif_thumbnail\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc_a_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dffbad38bc_a_1.jpg\"],\"height\":279},\"up\":\"494\",\"share_url\":\"http://a.f.budejie.com/share/23221565.html?wx.qq.com\",\"down\":18,\"forward\":24,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2017/01/18/587ea8ab38da4_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2017/01/18/587ea8ab38da4_mini.jpg\"],\"uid\":\"20060906\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"吉祥如意\",\"room_role\":\"\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_1.png\",\"name\":\"笑公馆V [吉祥如意]\"},\"passtime\":\"2017-01-18 06:18:01\",\"type\":\"gif\",\"id\":\"23221565\"},{\"status\":4,\"comment\":\"23\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":3,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"黄磊都是靠吃软饭的，说的当然好听，呵呵了！\",\"like_count\":6,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/347E935C3F254029AE2BC261CAAB948B/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/347E935C3F254029AE2BC261CAAB948B/100\"],\"uid\":\"19604308\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"明1OK\"},\"preuid\":0,\"passtime\":\"2017-01-16 22:25:48\",\"voiceuri\":\"\",\"id\":72803293},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"好好的出轨和吸毒不学你让我学这个？\",\"like_count\":6,\"u\":{\"header\":[\"http://tp2.sinaimg.cn/1903744153/50/5719987536/1\",\"http://tp2.sinaimg.cn/1903744153/50/5719987536/1\"],\"uid\":\"14272570\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"郭大虾xs\"},\"preuid\":0,\"passtime\":\"2017-01-18 05:34:20\",\"voiceuri\":\"\",\"id\":72878271}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":62,\"name\":\"内涵\"},{\"id\":70,\"name\":\"情感\"}],\"bookmark\":\"83\",\"text\":\"结婚12年，黄磊首曝\\u201c鲜婚秘诀\\u201d，没事就要么么哒，吃一个锅睡一张床！赶紧学习学习\",\"up\":\"548\",\"share_url\":\"http://a.f.budejie.com/share/23207141.html?wx.qq.com\",\"down\":59,\"forward\":159,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/08/17/57b4535f343bf_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/08/17/57b4535f343bf_mini.jpg\"],\"uid\":\"18263501\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"百思UFO卡特\",\"room_role\":\"副帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_12.png\",\"name\":\"UFO卡特 [百思UFO卡特]\"},\"passtime\":\"2017-01-18 04:55:02\",\"video\":{\"playfcount\":1710,\"height\":480,\"width\":854,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpc.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpc.mp4\"],\"duration\":144,\"playcount\":18679,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpd.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0116/382bf69c-dbef-11e6-91a6-d4ae5296039d_wpd.jpg\"]},\"type\":\"video\",\"id\":\"23207141\"},{\"status\":4,\"comment\":\"68\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":2,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"为什么感觉莫名的心疼！来哥帮你拉9个人陪你打！\",\"like_count\":78,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/E9E2B37571F86B39CA3E33A6C7EB4430/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/E9E2B37571F86B39CA3E33A6C7EB4430/100\"],\"uid\":\"11868933\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"一张辞职单\"},\"preuid\":0,\"passtime\":\"2017-01-17 21:26:11\",\"voiceuri\":\"\",\"id\":72858192},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"什么游戏\",\"like_count\":0,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/05/21/573f90217e939_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/05/21/573f90217e939_mini.jpg\"],\"uid\":\"11556299\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"A我是神经病\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:12:50\",\"voiceuri\":\"\",\"id\":72883446}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":28,\"name\":\"动态图\"},{\"id\":30157,\"name\":\"百思原创\"}],\"bookmark\":\"77\",\"text\":\"这几个坑货队友，把妹子都坑哭了你们知道吗？！\",\"gif\":{\"images\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479.gif\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479.gif\"],\"width\":197,\"gif_thumbnail\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479_a_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587de8fdd1479_a_1.jpg\"],\"height\":242},\"up\":\"591\",\"share_url\":\"http://a.f.budejie.com/share/23220244.html?wx.qq.com\",\"down\":77,\"forward\":113,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/12/26/586059118dd30_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/12/26/586059118dd30_mini.jpg\"],\"uid\":\"18619888\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"BS嫖基金\",\"room_role\":\"副帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_17.png\",\"name\":\"搞笑小村 [BS嫖基金]\"},\"passtime\":\"2017-01-18 04:25:35\",\"type\":\"gif\",\"id\":\"23220244\"},{\"status\":4,\"comment\":\"53\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":3,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"我的评论就像处女 从来没被顶过！\",\"like_count\":73,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/C2EDBA66A6ED56A2263A2FB2E1B19028/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/C2EDBA66A6ED56A2263A2FB2E1B19028/100\"],\"uid\":\"19669268\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"請允許珴尐尐旳驕傲\"},\"preuid\":0,\"passtime\":\"2017-01-16 19:32:31\",\"voiceuri\":\"\",\"id\":72793610},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"九鸡齐鸣迎春到，不会下蛋只会叫，家中公鸡不知道，半夜还在这里下胡闹。\",\"like_count\":19,\"u\":{\"header\":[\"http://wx.qlogo.cn/mmopen/NsXicLUicickpel973x87uu1Dnv6yv48mwicZZHIgicrFZVzKaAYszUL6eMxicULLfnZicVQseSlias7biap2AicJSzC3j83EITw6O1Xqc/0\",\"http://wx.qlogo.cn/mmopen/NsXicLUicickpel973x87uu1Dnv6yv48mwicZZHIgicrFZVzKaAYszUL6eMxicULLfnZicVQseSlias7biap2AicJSzC3j83EITw6O1Xqc/0\"],\"uid\":\"19725499\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"梦想oSU\"},\"preuid\":0,\"passtime\":\"2017-01-18 07:14:18\",\"voiceuri\":\"\",\"id\":72879705}],\"tags\":[{\"id\":55,\"name\":\"微视频\"},{\"id\":1,\"name\":\"搞笑\"},{\"id\":61,\"name\":\"恶搞\"}],\"bookmark\":\"81\",\"text\":\"快过年了，群主你看着办吧~\",\"up\":\"903\",\"share_url\":\"http://a.f.budejie.com/share/23204274.html?wx.qq.com\",\"down\":186,\"forward\":209,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/picture1/M00/11/3A/wKiFQ1Sz74yAO3RqAACDTxgf9Gs883.jpg\",\"http://dimg.spriteapp.cn/profile/picture1/M00/11/3A/wKiFQ1Sz74yAO3RqAACDTxgf9Gs883.jpg\"],\"uid\":\"13096130\",\"is_vip\":false,\"is_v\":false,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"小易\"},\"passtime\":\"2017-01-17 23:36:02\",\"video\":{\"playfcount\":23285,\"height\":640,\"width\":480,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.mp4\"],\"duration\":12,\"playcount\":105301,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0116/ac3e6af2dbd511e6a90e842b2b4c75ab_wpd.jpg\"]},\"type\":\"video\",\"id\":\"23204274\"},{\"status\":4,\"comment\":\"86\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"有这么个女儿，别说母女双飞，就是父女双飞都行！\",\"like_count\":32,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/AF64D2B02D0B2D73AB0682D22AFF4C29/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/AF64D2B02D0B2D73AB0682D22AFF4C29/100\"],\"uid\":\"19621462\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"林枫秋逝\"},\"preuid\":0,\"passtime\":\"2017-01-18 01:32:46\",\"voiceuri\":\"\",\"id\":72873338},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"我的板砖妹儿了。还给我\",\"like_count\":10,\"u\":{\"header\":[\"http://tp4.sinaimg.cn/2649415571/50/0/1\",\"http://tp4.sinaimg.cn/2649415571/50/0/1\"],\"uid\":\"5171389\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"墨林8023\"},\"preuid\":0,\"passtime\":\"2017-01-18 01:43:52\",\"voiceuri\":\"\",\"id\":72873697},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"看完我居然可耻的石更了\",\"like_count\":0,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2015/09/14/55f62e013f5d0_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2015/09/14/55f62e013f5d0_mini.jpg\"],\"uid\":\"15936029\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"草莓小臭\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:06:14\",\"voiceuri\":\"\",\"id\":72883188}],\"tags\":[{\"id\":55,\"name\":\"微视频\"},{\"id\":63,\"name\":\"笑话\"},{\"id\":5617,\"name\":\"情侣\"},{\"id\":17406,\"name\":\"尖叫耐撕男女\"}],\"bookmark\":\"46\",\"text\":\"不要轻易跟美女回家！说多了都是泪\",\"up\":\"1184\",\"share_url\":\"http://a.f.budejie.com/share/23198902.html?wx.qq.com\",\"down\":146,\"forward\":88,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/10/29/5814c406553e9_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/10/29/5814c406553e9_mini.jpg\"],\"uid\":\"18392795\",\"is_vip\":false,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"尖叫耐撕男女\"},\"passtime\":\"2017-01-17 23:11:01\",\"video\":{\"playfcount\":11655,\"height\":600,\"width\":1066,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0116/587c3e803c9c4_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/587c3e803c9c4_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0116/587c3e803c9c4_wpc.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/587c3e803c9c4_wpc.mp4\"],\"duration\":238,\"playcount\":77304,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0116/587c3e803c9c4_wpd_65_46.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0116/587c3e803c9c4_wpd_65_46.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0116/587c3e803c9c4_wpd_65_46.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0116/587c3e803c9c4_wpd_65_46.jpg\"]},\"type\":\"video\",\"id\":\"23198902\"},{\"status\":4,\"comment\":\"115\",\"top_comments\":[{\"voicetime\":0,\"status\":0,\"hate_count\":1,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"中国足球队哪去了？没脸出来了呢\",\"like_count\":67,\"u\":{\"header\":[\"http://tva3.sinaimg.cn/crop.0.0.180.180.50/86cce65fjw1e8qgp5bmzyj2050050aa8.jpg\",\"http://tva3.sinaimg.cn/crop.0.0.180.180.50/86cce65fjw1e8qgp5bmzyj2050050aa8.jpg\"],\"uid\":\"18574346\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"百思不得姐的爱\"},\"preuid\":0,\"passtime\":\"2017-01-16 08:02:40\",\"voiceuri\":\"\",\"id\":72765680},{\"voicetime\":0,\"status\":0,\"hate_count\":1,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"挺有口才的\",\"like_count\":41,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/6EE41EAA1F6393BB37E51FF7D5E457C1/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/6EE41EAA1F6393BB37E51FF7D5E457C1/100\"],\"uid\":\"9005901\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"fate0\"},\"preuid\":0,\"passtime\":\"2017-01-16 02:20:16\",\"voiceuri\":\"\",\"id\":72760811},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"别的不说 能站在这么多人面前侃侃而谈 这般自然也是蛮屌的了 何况还是个女生\",\"like_count\":22,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/396B0906260FA7B28FE2DCB3421D71F0/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/396B0906260FA7B28FE2DCB3421D71F0/100\"],\"uid\":\"14852460\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Mr柏\"},\"preuid\":0,\"passtime\":\"2017-01-18 01:08:36\",\"voiceuri\":\"\",\"id\":72872405}],\"tags\":[{\"id\":1,\"name\":\"搞笑\"},{\"id\":60,\"name\":\"吐槽\"},{\"id\":87,\"name\":\"逗比\"},{\"id\":1073,\"name\":\"正能量\"},{\"id\":18910,\"name\":\"hx\"}],\"bookmark\":\"164\",\"text\":\"傅园慧幽默单口独白，风趣采访现场嘉宾带Hi全场！\",\"up\":\"1623\",\"share_url\":\"http://a.f.budejie.com/share/23195007.html?wx.qq.com\",\"down\":241,\"forward\":153,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/09/25/57e753b7913e9_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/09/25/57e753b7913e9_mini.jpg\"],\"uid\":\"19137604\",\"is_vip\":false,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"爆米花\",\"room_role\":\"帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_1.png\",\"name\":\"繁花落尽 [爆米花]\"},\"passtime\":\"2017-01-17 22:46:01\",\"video\":{\"playfcount\":3400,\"height\":352,\"width\":640,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpc.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpc.mp4\"],\"duration\":660,\"playcount\":70775,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpd_95.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpd_95.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpd_95.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0116/3507da64-db48-11e6-b844-d4ae5296039d_wpd_95.jpg\"]},\"type\":\"video\",\"id\":\"23195007\"}]\n     */\n\n    private InfoBean info;\n    private List<ListBean> list;\n\n    public InfoBean getInfo() {\n        return info;\n    }\n\n    public void setInfo(InfoBean info) {\n        this.info = info;\n    }\n\n    public List<ListBean> getList() {\n        return list;\n    }\n\n    public void setList(List<ListBean> list) {\n        this.list = list;\n    }\n\n    public static class InfoBean {\n        /**\n         * count : 4430\n         * np : 1484664361\n         */\n\n        private int count;\n        private int np;\n\n        public int getCount() {\n            return count;\n        }\n\n        public void setCount(int count) {\n            this.count = count;\n        }\n\n        public int getNp() {\n            return np;\n        }\n\n        public void setNp(int np) {\n            this.np = np;\n        }\n    }\n\n    public static class ListBean {\n        /**\n         * status : 4\n         * comment : 24\n         * tags : [{\"id\":30157,\"name\":\"百思原创\"},{\"id\":62,\"name\":\"内涵\"},{\"id\":4670,\"name\":\"涨姿势\"}]\n         * bookmark : 3\n         * text : 这道“成人料理”，你学会了吗？\n         * gif : {\"images\":[\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24.gif\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24.gif\"],\"width\":400,\"gif_thumbnail\":[\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\"],\"height\":1781}\n         * up : 106\n         * share_url : http://a.f.budejie.com/share/23183944.html?wx.qq.com\n         * down : 15\n         * forward : 6\n         * u : {\"header\":[\"http://wimg.spriteapp.cn/profile/large/2016/09/22/57e3d9083f5e3_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/09/22/57e3d9083f5e3_mini.jpg\"],\"uid\":\"18614232\",\"is_vip\":true,\"is_v\":true,\"room_url\":\"\",\"room_name\":\"舞凡秀\",\"room_role\":\"帮主\",\"room_icon\":\"http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_1.png\",\"name\":\"原生君 [舞凡秀]\"}\n         * passtime : 2017-01-18 09:12:02\n         * type : gif\n         * id : 23183944\n         * top_comments : [{\"voicetime\":0,\"status\":2,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"9个赞 10个踩也能上精华？你特么在逗我？\",\"like_count\":1,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\"],\"uid\":\"15512124\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Lgg丶\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:10:19\",\"voiceuri\":\"\",\"id\":72883349},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"没有跑了。！头！象v！恭喜\",\"like_count\":0,\"u\":{\"header\":[\"http://wimg.spriteapp.cn/profile/large/2017/01/17/587da086205ff_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2017/01/17/587da086205ff_mini.jpg\"],\"uid\":\"20059417\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"f\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"质料里啦\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:10:22\",\"voiceuri\":\"\",\"id\":72883352},{\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"女的丑死了\",\"like_count\":0,\"u\":{\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/59A1F296A4379B672987679362B93363/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/59A1F296A4379B672987679362B93363/100\"],\"uid\":\"18648939\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Mylove000\"},\"preuid\":0,\"passtime\":\"2017-01-18 09:11:50\",\"voiceuri\":\"\",\"id\":72883403}]\n         * video : {\"playfcount\":1293,\"height\":640,\"width\":480,\"video\":[\"http://wvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\"],\"download\":[\"http://wvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\"],\"duration\":22,\"playcount\":9306,\"thumbnail\":[\"http://wimg.spriteapp.cn/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\"],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\"]}\n         * top_comment : {\"voicetime\":0,\"status\":0,\"hate_count\":0,\"cmt_type\":\"text\",\"precid\":0,\"content\":\"哪里是粉色的，你说清楚\",\"like_count\":238,\"u\":{\"header\":[\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\",\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\"],\"uid\":\"15129882\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"FutNotice\"},\"preuid\":0,\"passtime\":\"2015-12-10 11:31:11\",\"voiceuri\":\"\",\"id\":38864637}\n         * image : {\"medium\":[],\"big\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_1.jpg\"],\"download_url\":[\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08.jpg\"],\"height\":9364,\"width\":710,\"small\":[],\"thumbnail_small\":[\"http://wimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587dc4f94bc08.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587dc4f94bc08.jpg\"]}\n         */\n\n        private int status;\n        private String comment;\n        private String bookmark;\n        private String text;\n        private GifEntity gif;\n        private String up;\n        private String share_url;\n        private int down;\n        private int forward;\n        private UEntity u;\n        private String passtime;\n        private String type;\n        private String id;\n        private VideoEntity video;\n        private TopCommentBean top_comment;\n        private ImageEntity image;\n        private List<TagsBean> tags;\n        private List<TopCommentsBean> top_comments;\n\n        public int getStatus() {\n            return status;\n        }\n\n        public void setStatus(int status) {\n            this.status = status;\n        }\n\n        public String getComment() {\n            return comment;\n        }\n\n        public void setComment(String comment) {\n            this.comment = comment;\n        }\n\n        public String getBookmark() {\n            return bookmark;\n        }\n\n        public void setBookmark(String bookmark) {\n            this.bookmark = bookmark;\n        }\n\n        public String getText() {\n            return text;\n        }\n\n        public void setText(String text) {\n            this.text = text;\n        }\n\n        public GifEntity getGif() {\n            return gif;\n        }\n\n        public void setGif(GifEntity gif) {\n            this.gif = gif;\n        }\n\n        public String getUp() {\n            return up;\n        }\n\n        public void setUp(String up) {\n            this.up = up;\n        }\n\n        public String getShare_url() {\n            return share_url;\n        }\n\n        public void setShare_url(String share_url) {\n            this.share_url = share_url;\n        }\n\n        public int getDown() {\n            return down;\n        }\n\n        public void setDown(int down) {\n            this.down = down;\n        }\n\n        public int getForward() {\n            return forward;\n        }\n\n        public void setForward(int forward) {\n            this.forward = forward;\n        }\n\n        public UEntity getU() {\n            return u;\n        }\n\n        public void setU(UEntity u) {\n            this.u = u;\n        }\n\n        public String getPasstime() {\n            return passtime;\n        }\n\n        public void setPasstime(String passtime) {\n            this.passtime = passtime;\n        }\n\n        public String getType() {\n            return type;\n        }\n\n        public void setType(String type) {\n            this.type = type;\n        }\n\n        public String getId() {\n            return id;\n        }\n\n        public void setId(String id) {\n            this.id = id;\n        }\n\n        public VideoEntity getVideo() {\n            return video;\n        }\n\n        public void setVideo(VideoEntity video) {\n            this.video = video;\n        }\n\n        public TopCommentBean getTop_comment() {\n            return top_comment;\n        }\n\n        public void setTop_comment(TopCommentBean top_comment) {\n            this.top_comment = top_comment;\n        }\n\n        public ImageEntity getImage() {\n            return image;\n        }\n\n        public void setImage(ImageEntity image) {\n            this.image = image;\n        }\n\n        public List<TagsBean> getTags() {\n            return tags;\n        }\n\n        public void setTags(List<TagsBean> tags) {\n            this.tags = tags;\n        }\n\n        public List<TopCommentsBean> getTop_comments() {\n            return top_comments;\n        }\n\n        public void setTop_comments(List<TopCommentsBean> top_comments) {\n            this.top_comments = top_comments;\n        }\n\n        public static class GifEntity {\n            /**\n             * images : [\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24.gif\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24.gif\"]\n             * width : 400\n             * gif_thumbnail : [\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\"]\n             * download_url : [\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/15/587b036ac2e24_a_1.jpg\"]\n             * height : 1781\n             */\n\n            private int width;\n            private int height;\n            private List<String> images;\n            private List<String> gif_thumbnail;\n            private List<String> download_url;\n\n            public int getWidth() {\n                return width;\n            }\n\n            public void setWidth(int width) {\n                this.width = width;\n            }\n\n            public int getHeight() {\n                return height;\n            }\n\n            public void setHeight(int height) {\n                this.height = height;\n            }\n\n            public List<String> getImages() {\n                return images;\n            }\n\n            public void setImages(List<String> images) {\n                this.images = images;\n            }\n\n            public List<String> getGif_thumbnail() {\n                return gif_thumbnail;\n            }\n\n            public void setGif_thumbnail(List<String> gif_thumbnail) {\n                this.gif_thumbnail = gif_thumbnail;\n            }\n\n            public List<String> getDownload_url() {\n                return download_url;\n            }\n\n            public void setDownload_url(List<String> download_url) {\n                this.download_url = download_url;\n            }\n        }\n\n        public static class UEntity {\n            /**\n             * header : [\"http://wimg.spriteapp.cn/profile/large/2016/09/22/57e3d9083f5e3_mini.jpg\",\"http://dimg.spriteapp.cn/profile/large/2016/09/22/57e3d9083f5e3_mini.jpg\"]\n             * uid : 18614232\n             * is_vip : true\n             * is_v : true\n             * room_url :\n             * room_name : 舞凡秀\n             * room_role : 帮主\n             * room_icon : http://wimg.spriteapp.cn/ugc/2016/1101/gang_level_1.png\n             * name : 原生君 [舞凡秀]\n             */\n\n            private String uid;\n            private boolean is_vip;\n            private boolean is_v;\n            private String room_url;\n            private String room_name;\n            private String room_role;\n            private String room_icon;\n            private String name;\n            private List<String> header;\n\n            public String getUid() {\n                return uid;\n            }\n\n            public void setUid(String uid) {\n                this.uid = uid;\n            }\n\n            public boolean isIs_vip() {\n                return is_vip;\n            }\n\n            public void setIs_vip(boolean is_vip) {\n                this.is_vip = is_vip;\n            }\n\n            public boolean isIs_v() {\n                return is_v;\n            }\n\n            public void setIs_v(boolean is_v) {\n                this.is_v = is_v;\n            }\n\n            public String getRoom_url() {\n                return room_url;\n            }\n\n            public void setRoom_url(String room_url) {\n                this.room_url = room_url;\n            }\n\n            public String getRoom_name() {\n                return room_name;\n            }\n\n            public void setRoom_name(String room_name) {\n                this.room_name = room_name;\n            }\n\n            public String getRoom_role() {\n                return room_role;\n            }\n\n            public void setRoom_role(String room_role) {\n                this.room_role = room_role;\n            }\n\n            public String getRoom_icon() {\n                return room_icon;\n            }\n\n            public void setRoom_icon(String room_icon) {\n                this.room_icon = room_icon;\n            }\n\n            public String getName() {\n                return name;\n            }\n\n            public void setName(String name) {\n                this.name = name;\n            }\n\n            public List<String> getHeader() {\n                return header;\n            }\n\n            public void setHeader(List<String> header) {\n                this.header = header;\n            }\n        }\n\n        public static class VideoEntity {\n            /**\n             * playfcount : 1293\n             * height : 640\n             * width : 480\n             * video : [\"http://wvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\"]\n             * download : [\"http://wvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\",\"http://dvideo.spriteapp.cn/video/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.mp4\"]\n             * duration : 22\n             * playcount : 9306\n             * thumbnail : [\"http://wimg.spriteapp.cn/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\"]\n             * thumbnail_small : [\"http://wimg.spriteapp.cn/crop/150x150/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/picture/2017/0117/9955801c-dc4b-11e6-8398-90b11c479401_wpd.jpg\"]\n             */\n\n            private int playfcount;\n            private int height;\n            private int width;\n            private int duration;\n            private int playcount;\n            private List<String> video;\n            private List<String> download;\n            private List<String> thumbnail;\n            private List<String> thumbnail_small;\n\n            public int getPlayfcount() {\n                return playfcount;\n            }\n\n            public void setPlayfcount(int playfcount) {\n                this.playfcount = playfcount;\n            }\n\n            public int getHeight() {\n                return height;\n            }\n\n            public void setHeight(int height) {\n                this.height = height;\n            }\n\n            public int getWidth() {\n                return width;\n            }\n\n            public void setWidth(int width) {\n                this.width = width;\n            }\n\n            public int getDuration() {\n                return duration;\n            }\n\n            public void setDuration(int duration) {\n                this.duration = duration;\n            }\n\n            public int getPlaycount() {\n                return playcount;\n            }\n\n            public void setPlaycount(int playcount) {\n                this.playcount = playcount;\n            }\n\n            public List<String> getVideo() {\n                return video;\n            }\n\n            public void setVideo(List<String> video) {\n                this.video = video;\n            }\n\n            public List<String> getDownload() {\n                return download;\n            }\n\n            public void setDownload(List<String> download) {\n                this.download = download;\n            }\n\n            public List<String> getThumbnail() {\n                return thumbnail;\n            }\n\n            public void setThumbnail(List<String> thumbnail) {\n                this.thumbnail = thumbnail;\n            }\n\n            public List<String> getThumbnail_small() {\n                return thumbnail_small;\n            }\n\n            public void setThumbnail_small(List<String> thumbnail_small) {\n                this.thumbnail_small = thumbnail_small;\n            }\n        }\n\n        public static class TopCommentBean {\n            /**\n             * voicetime : 0\n             * status : 0\n             * hate_count : 0\n             * cmt_type : text\n             * precid : 0\n             * content : 哪里是粉色的，你说清楚\n             * like_count : 238\n             * u : {\"header\":[\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\",\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\"],\"uid\":\"15129882\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"FutNotice\"}\n             * preuid : 0\n             * passtime : 2015-12-10 11:31:11\n             * voiceuri :\n             * id : 38864637\n             */\n\n            private int voicetime;\n            private int status;\n            private int hate_count;\n            private String cmt_type;\n            private int precid;\n            private String content;\n            private int like_count;\n            private UEntityX u;\n            private int preuid;\n            private String passtime;\n            private String voiceuri;\n            private int id;\n\n            public int getVoicetime() {\n                return voicetime;\n            }\n\n            public void setVoicetime(int voicetime) {\n                this.voicetime = voicetime;\n            }\n\n            public int getStatus() {\n                return status;\n            }\n\n            public void setStatus(int status) {\n                this.status = status;\n            }\n\n            public int getHate_count() {\n                return hate_count;\n            }\n\n            public void setHate_count(int hate_count) {\n                this.hate_count = hate_count;\n            }\n\n            public String getCmt_type() {\n                return cmt_type;\n            }\n\n            public void setCmt_type(String cmt_type) {\n                this.cmt_type = cmt_type;\n            }\n\n            public int getPrecid() {\n                return precid;\n            }\n\n            public void setPrecid(int precid) {\n                this.precid = precid;\n            }\n\n            public String getContent() {\n                return content;\n            }\n\n            public void setContent(String content) {\n                this.content = content;\n            }\n\n            public int getLike_count() {\n                return like_count;\n            }\n\n            public void setLike_count(int like_count) {\n                this.like_count = like_count;\n            }\n\n            public UEntityX getU() {\n                return u;\n            }\n\n            public void setU(UEntityX u) {\n                this.u = u;\n            }\n\n            public int getPreuid() {\n                return preuid;\n            }\n\n            public void setPreuid(int preuid) {\n                this.preuid = preuid;\n            }\n\n            public String getPasstime() {\n                return passtime;\n            }\n\n            public void setPasstime(String passtime) {\n                this.passtime = passtime;\n            }\n\n            public String getVoiceuri() {\n                return voiceuri;\n            }\n\n            public void setVoiceuri(String voiceuri) {\n                this.voiceuri = voiceuri;\n            }\n\n            public int getId() {\n                return id;\n            }\n\n            public void setId(int id) {\n                this.id = id;\n            }\n\n            public static class UEntityX {\n                /**\n                 * header : [\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\",\"http://tp4.sinaimg.cn/5587782911/50/5723992125/1\"]\n                 * uid : 15129882\n                 * is_vip : false\n                 * room_url :\n                 * sex : m\n                 * room_name :\n                 * room_role :\n                 * room_icon :\n                 * name : FutNotice\n                 */\n\n                private String uid;\n                private boolean is_vip;\n                private String room_url;\n                private String sex;\n                private String room_name;\n                private String room_role;\n                private String room_icon;\n                private String name;\n                private List<String> header;\n\n                public String getUid() {\n                    return uid;\n                }\n\n                public void setUid(String uid) {\n                    this.uid = uid;\n                }\n\n                public boolean isIs_vip() {\n                    return is_vip;\n                }\n\n                public void setIs_vip(boolean is_vip) {\n                    this.is_vip = is_vip;\n                }\n\n                public String getRoom_url() {\n                    return room_url;\n                }\n\n                public void setRoom_url(String room_url) {\n                    this.room_url = room_url;\n                }\n\n                public String getSex() {\n                    return sex;\n                }\n\n                public void setSex(String sex) {\n                    this.sex = sex;\n                }\n\n                public String getRoom_name() {\n                    return room_name;\n                }\n\n                public void setRoom_name(String room_name) {\n                    this.room_name = room_name;\n                }\n\n                public String getRoom_role() {\n                    return room_role;\n                }\n\n                public void setRoom_role(String room_role) {\n                    this.room_role = room_role;\n                }\n\n                public String getRoom_icon() {\n                    return room_icon;\n                }\n\n                public void setRoom_icon(String room_icon) {\n                    this.room_icon = room_icon;\n                }\n\n                public String getName() {\n                    return name;\n                }\n\n                public void setName(String name) {\n                    this.name = name;\n                }\n\n                public List<String> getHeader() {\n                    return header;\n                }\n\n                public void setHeader(List<String> header) {\n                    this.header = header;\n                }\n            }\n        }\n\n        public static class ImageEntity {\n            /**\n             * medium : []\n             * big : [\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_1.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_1.jpg\"]\n             * download_url : [\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_d.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08_d.jpg\",\"http://wimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08.jpg\",\"http://dimg.spriteapp.cn/ugc/2017/01/17/587dc4f94bc08.jpg\"]\n             * height : 9364\n             * width : 710\n             * small : []\n             * thumbnail_small : [\"http://wimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587dc4f94bc08.jpg\",\"http://dimg.spriteapp.cn/crop/150x150/ugc/2017/01/17/587dc4f94bc08.jpg\"]\n             */\n\n            private int height;\n            private int width;\n            private List<?> medium;\n            private List<String> big;\n            private List<String> download_url;\n            private List<?> small;\n            private List<String> thumbnail_small;\n\n            public int getHeight() {\n                return height;\n            }\n\n            public void setHeight(int height) {\n                this.height = height;\n            }\n\n            public int getWidth() {\n                return width;\n            }\n\n            public void setWidth(int width) {\n                this.width = width;\n            }\n\n            public List<?> getMedium() {\n                return medium;\n            }\n\n            public void setMedium(List<?> medium) {\n                this.medium = medium;\n            }\n\n            public List<String> getBig() {\n                return big;\n            }\n\n            public void setBig(List<String> big) {\n                this.big = big;\n            }\n\n            public List<String> getDownload_url() {\n                return download_url;\n            }\n\n            public void setDownload_url(List<String> download_url) {\n                this.download_url = download_url;\n            }\n\n            public List<?> getSmall() {\n                return small;\n            }\n\n            public void setSmall(List<?> small) {\n                this.small = small;\n            }\n\n            public List<String> getThumbnail_small() {\n                return thumbnail_small;\n            }\n\n            public void setThumbnail_small(List<String> thumbnail_small) {\n                this.thumbnail_small = thumbnail_small;\n            }\n        }\n\n        public static class TagsBean {\n            /**\n             * id : 30157\n             * name : 百思原创\n             */\n\n            private int id;\n            private String name;\n\n            public int getId() {\n                return id;\n            }\n\n            public void setId(int id) {\n                this.id = id;\n            }\n\n            public String getName() {\n                return name;\n            }\n\n            public void setName(String name) {\n                this.name = name;\n            }\n        }\n\n        public static class TopCommentsBean {\n            /**\n             * voicetime : 0\n             * status : 2\n             * hate_count : 0\n             * cmt_type : text\n             * precid : 0\n             * content : 9个赞 10个踩也能上精华？你特么在逗我？\n             * like_count : 1\n             * u : {\"header\":[\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\"],\"uid\":\"15512124\",\"is_vip\":false,\"room_url\":\"\",\"sex\":\"m\",\"room_name\":\"\",\"room_role\":\"\",\"room_icon\":\"\",\"name\":\"Lgg丶\"}\n             * preuid : 0\n             * passtime : 2017-01-18 09:10:19\n             * voiceuri :\n             * id : 72883349\n             */\n\n            private int voicetime;\n            private int status;\n            private int hate_count;\n            private String cmt_type;\n            private int precid;\n            private String content;\n            private int like_count;\n            private UEntityXX u;\n            private int preuid;\n            private String passtime;\n            private String voiceuri;\n            private int id;\n\n            public int getVoicetime() {\n                return voicetime;\n            }\n\n            public void setVoicetime(int voicetime) {\n                this.voicetime = voicetime;\n            }\n\n            public int getStatus() {\n                return status;\n            }\n\n            public void setStatus(int status) {\n                this.status = status;\n            }\n\n            public int getHate_count() {\n                return hate_count;\n            }\n\n            public void setHate_count(int hate_count) {\n                this.hate_count = hate_count;\n            }\n\n            public String getCmt_type() {\n                return cmt_type;\n            }\n\n            public void setCmt_type(String cmt_type) {\n                this.cmt_type = cmt_type;\n            }\n\n            public int getPrecid() {\n                return precid;\n            }\n\n            public void setPrecid(int precid) {\n                this.precid = precid;\n            }\n\n            public String getContent() {\n                return content;\n            }\n\n            public void setContent(String content) {\n                this.content = content;\n            }\n\n            public int getLike_count() {\n                return like_count;\n            }\n\n            public void setLike_count(int like_count) {\n                this.like_count = like_count;\n            }\n\n            public UEntityXX getU() {\n                return u;\n            }\n\n            public void setU(UEntityXX u) {\n                this.u = u;\n            }\n\n            public int getPreuid() {\n                return preuid;\n            }\n\n            public void setPreuid(int preuid) {\n                this.preuid = preuid;\n            }\n\n            public String getPasstime() {\n                return passtime;\n            }\n\n            public void setPasstime(String passtime) {\n                this.passtime = passtime;\n            }\n\n            public String getVoiceuri() {\n                return voiceuri;\n            }\n\n            public void setVoiceuri(String voiceuri) {\n                this.voiceuri = voiceuri;\n            }\n\n            public int getId() {\n                return id;\n            }\n\n            public void setId(int id) {\n                this.id = id;\n            }\n\n            public static class UEntityXX {\n                /**\n                 * header : [\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\",\"http://qzapp.qlogo.cn/qzapp/100336987/633341F6CD428C870B21026C1361BF42/100\"]\n                 * uid : 15512124\n                 * is_vip : false\n                 * room_url :\n                 * sex : m\n                 * room_name :\n                 * room_role :\n                 * room_icon :\n                 * name : Lgg丶\n                 */\n\n                private String uid;\n                private boolean is_vip;\n                private String room_url;\n                private String sex;\n                private String room_name;\n                private String room_role;\n                private String room_icon;\n                private String name;\n                private List<String> header;\n\n                public String getUid() {\n                    return uid;\n                }\n\n                public void setUid(String uid) {\n                    this.uid = uid;\n                }\n\n                public boolean isIs_vip() {\n                    return is_vip;\n                }\n\n                public void setIs_vip(boolean is_vip) {\n                    this.is_vip = is_vip;\n                }\n\n                public String getRoom_url() {\n                    return room_url;\n                }\n\n                public void setRoom_url(String room_url) {\n                    this.room_url = room_url;\n                }\n\n                public String getSex() {\n                    return sex;\n                }\n\n                public void setSex(String sex) {\n                    this.sex = sex;\n                }\n\n                public String getRoom_name() {\n                    return room_name;\n                }\n\n                public void setRoom_name(String room_name) {\n                    this.room_name = room_name;\n                }\n\n                public String getRoom_role() {\n                    return room_role;\n                }\n\n                public void setRoom_role(String room_role) {\n                    this.room_role = room_role;\n                }\n\n                public String getRoom_icon() {\n                    return room_icon;\n                }\n\n                public void setRoom_icon(String room_icon) {\n                    this.room_icon = room_icon;\n                }\n\n                public String getName() {\n                    return name;\n                }\n\n                public void setName(String name) {\n                    this.name = name;\n                }\n\n                public List<String> getHeader() {\n                    return header;\n                }\n\n                public void setHeader(List<String> header) {\n                    this.header = header;\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/bean/SearchBean.java",
    "content": "package com.atguigu.mobileplayer1020.bean;\n\nimport java.util.List;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/14 15:23\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：搜索返回的数据\n */\npublic class SearchBean {\n\n\n    /**\n     * flag : ok\n     * pageNo : 1\n     * pageSize : 20\n     * wd : 东莞\n     * total : 1223\n     * items : [{\"itemID\":\"ARTIKElXTzJ6lsJqgXu2XQyf170114\",\"itemTitle\":\"2017春运第二天：全国铁路预计发送旅客865万人次\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIKElXTzJ6lsJqgXu2XQyf170114&amp;isfromapp=1\",\"pubTime\":\"2017-01-14 13:55:50\",\"keywords\":\"2017春运第二天：全国铁路预计发送旅客865万人次\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2017-01-14\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/14/2017011413234866215.jpg\"}},{\"itemID\":\"ARTI6FKArsahxNZOYIay6Zym170114\",\"itemTitle\":\"交通运输部：春运首日发送道路旅客5825万人次\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTI6FKArsahxNZOYIay6Zym170114&amp;isfromapp=1\",\"pubTime\":\"2017-01-14 09:54:19\",\"keywords\":\"交通运输部：春运首日发送道路旅客5825万人次\",\"category\":\"最新\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939846580285\",\"datecheck\":\"2017-01-14\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/14/2017011409540797302.jpg\"}},{\"itemID\":\"ARTI2U1NwEpXu2uzXW2U6fm0170113\",\"itemTitle\":\"\\u201c数\\u201d说春运旅客出行：探亲访友旅客占78%\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTI2U1NwEpXu2uzXW2U6fm0170113&amp;isfromapp=1\",\"pubTime\":\"2017-01-13 16:35:53\",\"keywords\":\"春运 旅客出行：探亲访友的旅客占78%\",\"category\":\"最新\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939846580285\",\"datecheck\":\"2017-01-13\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/13/2017011316092534788.jpg\"}},{\"itemID\":\"ARTIC8VKLxNm71w2jgSdYUNM170113\",\"itemTitle\":\"春运模式正式开启：\\u201c四流\\u201d叠加  启用信用机制\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIC8VKLxNm71w2jgSdYUNM170113&amp;isfromapp=1\",\"pubTime\":\"2017-01-13 15:22:48\",\"keywords\":\"春运 模式  四流 信用机制\",\"category\":\"最新\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939846580285\",\"datecheck\":\"2017-01-13\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/13/2017011314073535631.jpg\"}},{\"itemID\":\"ARTI1zb9QJRw7lnIOB8l5qYu170111\",\"itemTitle\":\"国台办：坚持\\u201c九二共识\\u201d才能行稳致远\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTI1zb9QJRw7lnIOB8l5qYu170111&amp;isfromapp=1\",\"pubTime\":\"2017-01-11 12:48:46\",\"keywords\":\"国台办：坚持\\u201c九二共识\\u201d才能行稳致远\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2017-01-11\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/11/2017011112405077418.jpg\"}},{\"itemID\":\"ARTIJjT8KfUOei7IZ8AMUJGb170110\",\"itemTitle\":\"女子深夜开车回家遭袭险丧命 嫌疑人是谁\\u2022如何藏在车内？\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIJjT8KfUOei7IZ8AMUJGb170110&amp;isfromapp=1\",\"pubTime\":\"2017-01-10 14:55:50\",\"keywords\":\"法治在线 婚外情 伤人案\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2017-01-10\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/10/2017011014551030316.jpg\"}},{\"itemID\":\"VIDE6L3q346aS2fHlCaBiHGy170109\",\"itemTitle\":\"《朝闻天下》 20170109 07:00\",\"itemType\":\"columnvideo_flag\",\"detailUrl\":\"http://tv.cntv.cn/video/C10598/592d364358e34e9a9abd691c7ef6548a\",\"pubTime\":\"2017-01-09 23:35:17\",\"keywords\":\"朝闻天下,郑丽,严於信,夏雯,中央纪律检查委员会,摩苏尔收复战,卡车冲撞人群事件,低温天气,职称制度改革\",\"category\":\"\",\"guid\":\"592d364358e34e9a9abd691c7ef6548a\",\"videoLength\":\"\",\"source\":\"\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"\",\"datecheck\":\"2017-01-09\",\"itemImage\":{\"imgUrl1\":\"http://p5.img.cctvpic.com/fmspic/2017/01/09/592d364358e34e9a9abd691c7ef6548a-1451.jpg\"}},{\"itemID\":\"VIDESfpipVJj4WMVV05HPkxR170109\",\"itemTitle\":\"[朝闻天下]广东东莞：高速狂煲\\u201c电话粥\\u201d司机单手开车\",\"itemType\":\"columnvideo_flag\",\"detailUrl\":\"http://tv.cntv.cn/video/C10598/bca72714e39e4600937afac54d767369\",\"pubTime\":\"2017-01-09 23:30:16\",\"keywords\":\"广东东莞，交通安全\",\"category\":\"\",\"guid\":\"bca72714e39e4600937afac54d767369\",\"videoLength\":\"\",\"source\":\"\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"\",\"datecheck\":\"2017-01-09\",\"itemImage\":{\"imgUrl1\":\"http://p2.img.cctvpic.com/fmspic/2017/01/09/bca72714e39e4600937afac54d767369-43.jpg\"}},{\"itemID\":\"VIDEvU4StMGmg9pYeLoV2jmy170109\",\"itemTitle\":\"《朝闻天下》 20170109 06:00\",\"itemType\":\"columnvideo_flag\",\"detailUrl\":\"http://tv.cntv.cn/video/C10598/d54d8291e83347199a0db92091b5d71e\",\"pubTime\":\"2017-01-09 21:25:17\",\"keywords\":\"朝闻天下,郑丽,严於信,夏雯,中国男篮职业联赛全明星赛,冰雕比赛,雪地摩托越野锦标赛,春节用工紧张,\\u201c雪鹰601\\u201d,科技奖励大会,职称制度改革,车站丢行李\",\"category\":\"\",\"guid\":\"d54d8291e83347199a0db92091b5d71e\",\"videoLength\":\"\",\"source\":\"\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"\",\"datecheck\":\"2017-01-09\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/fmspic/2017/01/09/d54d8291e83347199a0db92091b5d71e-2865.jpg\"}},{\"itemID\":\"VIDEQezfrFFJ7tEz7Uiynr1T170109\",\"itemTitle\":\"[朝闻天下]广东东莞：高速狂煲\\u201c电话粥\\u201d 司机单手开车\",\"itemType\":\"columnvideo_flag\",\"detailUrl\":\"http://tv.cntv.cn/video/C10598/1435830224f9413288cc427b1491b009\",\"pubTime\":\"2017-01-09 11:41:25\",\"keywords\":\"广东，东莞，煲\\u201c电话粥\\u201d\",\"category\":\"\",\"guid\":\"1435830224f9413288cc427b1491b009\",\"videoLength\":\"\",\"source\":\"\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"\",\"datecheck\":\"2017-01-09\",\"itemImage\":{\"imgUrl1\":\"http://p3.img.cctvpic.com/fmspic/2017/01/09/1435830224f9413288cc427b1491b009-40.jpg\"}},{\"itemID\":\"ARTI5LO568CgvGOuxOph75aW170108\",\"itemTitle\":\"广东拱北海关破获1.5亿元走私红油案\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTI5LO568CgvGOuxOph75aW170108&amp;isfromapp=1\",\"pubTime\":\"2017-01-08 16:41:14\",\"keywords\":\"广东拱北海关破获1.5亿元走私红油案\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2017-01-08\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/08/2017010816260349225.jpg\"}},{\"itemID\":\"ARTIVZyt15GbrNG62vabwDUX170107\",\"itemTitle\":\"长三角首趟春运临客开行 重点加开前往西部列车\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIVZyt15GbrNG62vabwDUX170107&amp;isfromapp=1\",\"pubTime\":\"2017-01-07 13:26:48\",\"keywords\":\"长三角首趟春运临客提前开行\",\"category\":\"最新\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939846580285\",\"datecheck\":\"2017-01-07\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/07/2017010713262412202.jpg\"}},{\"itemID\":\"ARTIiJG04R98NoWNk7KH6qlk170104\",\"itemTitle\":\"交通部首发大数据看春运\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIiJG04R98NoWNk7KH6qlk170104&amp;isfromapp=1\",\"pubTime\":\"2017-01-04 18:34:20\",\"keywords\":\"交通部首发大数据看春运\",\"category\":\"最新\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939846580285\",\"datecheck\":\"2017-01-04\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/04/2017010418322216793.jpg\"}},{\"itemID\":\"ARTIzdjlD7xWRbexvSOBHHk3161231\",\"itemTitle\":\"早啊！新闻来了〔2016.12.31〕\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIzdjlD7xWRbexvSOBHHk3161231&amp;isfromapp=1\",\"pubTime\":\"2016-12-31 06:26:22\",\"keywords\":\"早啊！新闻来了〔2016.12.31〕\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2016-12-31\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2016/12/31/2016123106250564289.jpg\"}},{\"itemID\":\"ARTIyRXmJUdiQKb2FVR1HuPx161230\",\"itemTitle\":\"检察机关依法对国台办原副主任龚清概、广东省原副省长刘志庚两案提起公诉\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIyRXmJUdiQKb2FVR1HuPx161230&amp;isfromapp=1\",\"pubTime\":\"2016-12-30 10:15:34\",\"keywords\":\"检察机关依法对龚清概、刘志庚两案提起公诉\",\"category\":\"最新\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939846580285\",\"datecheck\":\"2016-12-30\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2016/12/30/2016123010435374996.jpg\"}},{\"itemID\":\"ARTIiGtwlyQLzWMbT71oncEy161230\",\"itemTitle\":\"央视新闻邀你当编辑 你的2016十大新闻是什么？\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIiGtwlyQLzWMbT71oncEy161230&amp;isfromapp=1\",\"pubTime\":\"2016-12-30 01:00:26\",\"keywords\":\"央视新闻邀你当编辑 你的2016十大新闻是什么？\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2016-12-30\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2016/12/30/2016123000492718934.jpg\"}},{\"itemID\":\"ARTIerivnSnUH1ZQmq5PQSo9161228\",\"itemTitle\":\"深中通道开工建设 珠三角与港澳将形成1小时生活圈\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIerivnSnUH1ZQmq5PQSo9161228&amp;isfromapp=1\",\"pubTime\":\"2016-12-28 09:28:21\",\"keywords\":\"深中通道开工建设 珠三角与港澳将形成1小时生活圈\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2016-12-28\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2016/12/28/2016122809264128440.png\"}},{\"itemID\":\"VIDEeZcgzeo4SSy4eHG3a1to161227\",\"itemTitle\":\"《第一时间》 20161227 2/2\",\"itemType\":\"columnvideo_flag\",\"detailUrl\":\"http://tv.cntv.cn/video/C10375/3daad6ced51246faab09e3285ca20b4c\",\"pubTime\":\"2016-12-27 17:18:13\",\"keywords\":\"第一时间,陈蓓蓓,周运,孔德俏,楼市新观察,\\u201c网络黄金\\u201d骗局,盗刷银行卡,有毒减肥药,查干湖冬捕\",\"category\":\"\",\"guid\":\"3daad6ced51246faab09e3285ca20b4c\",\"videoLength\":\"\",\"source\":\"\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"\",\"datecheck\":\"2016-12-27\",\"itemImage\":{\"imgUrl1\":\"http://p4.img.cctvpic.com/fmspic/2016/12/27/3daad6ced51246faab09e3285ca20b4c-3853.jpg\"}},{\"itemID\":\"VIDEm6rZW3htMc83Y8wheJ5J161227\",\"itemTitle\":\"[第一时间]关注出行安全·广东东莞：醉驾撞人逃逸 说谎掩盖真相被拆穿\",\"itemType\":\"columnvideo_flag\",\"detailUrl\":\"http://tv.cntv.cn/video/C10375/840b3f71907c49298588291a4a5fb40d\",\"pubTime\":\"2016-12-27 17:18:13\",\"keywords\":\"广东东莞，醉驾撞人\",\"category\":\"\",\"guid\":\"840b3f71907c49298588291a4a5fb40d\",\"videoLength\":\"\",\"source\":\"\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"\",\"datecheck\":\"2016-12-27\",\"itemImage\":{\"imgUrl1\":\"http://p3.img.cctvpic.com/fmspic/2016/12/27/840b3f71907c49298588291a4a5fb40d-50.jpg\"}},{\"itemID\":\"ARTIJs7jSYDh93XAYnmwhG4b161224\",\"itemTitle\":\"关注 | 罗一笑走了，请带走争议留下共识！\",\"itemType\":\"article_flag\",\"detailUrl\":\"http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIJs7jSYDh93XAYnmwhG4b161224&amp;isfromapp=1\",\"pubTime\":\"2016-12-24 16:33:46\",\"keywords\":\"关注 | 罗一笑走了，请带走争议留下共识！\",\"category\":\"要闻\",\"guid\":\"\",\"videoLength\":\"\",\"source\":\"央视新闻客户端\",\"brief\":\"\",\"photoCount\":\"0\",\"sub_column_id\":\"PAGE1373939356250140\",\"datecheck\":\"2016-12-24\",\"itemImage\":{\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2016/12/24/2016122416280866312.jpg\"}}]\n     */\n\n    private String flag;\n    private String pageNo;\n    private String pageSize;\n    private String wd;\n    private String total;\n    private List<ItemsBean> items;\n\n    public String getFlag() {\n        return flag;\n    }\n\n    public void setFlag(String flag) {\n        this.flag = flag;\n    }\n\n    public String getPageNo() {\n        return pageNo;\n    }\n\n    public void setPageNo(String pageNo) {\n        this.pageNo = pageNo;\n    }\n\n    public String getPageSize() {\n        return pageSize;\n    }\n\n    public void setPageSize(String pageSize) {\n        this.pageSize = pageSize;\n    }\n\n    public String getWd() {\n        return wd;\n    }\n\n    public void setWd(String wd) {\n        this.wd = wd;\n    }\n\n    public String getTotal() {\n        return total;\n    }\n\n    public void setTotal(String total) {\n        this.total = total;\n    }\n\n    public List<ItemsBean> getItems() {\n        return items;\n    }\n\n    public void setItems(List<ItemsBean> items) {\n        this.items = items;\n    }\n\n    public static class ItemsBean {\n        /**\n         * itemID : ARTIKElXTzJ6lsJqgXu2XQyf170114\n         * itemTitle : 2017春运第二天：全国铁路预计发送旅客865万人次\n         * itemType : article_flag\n         * detailUrl : http://app.cntv.cn/special/news/detail/arti/index.html?id=ARTIKElXTzJ6lsJqgXu2XQyf170114&amp;isfromapp=1\n         * pubTime : 2017-01-14 13:55:50\n         * keywords : 2017春运第二天：全国铁路预计发送旅客865万人次\n         * category : 要闻\n         * guid :\n         * videoLength :\n         * source : 央视新闻客户端\n         * brief :\n         * photoCount : 0\n         * sub_column_id : PAGE1373939356250140\n         * datecheck : 2017-01-14\n         * itemImage : {\"imgUrl1\":\"http://p1.img.cctvpic.com/photoworkspace/2017/01/14/2017011413234866215.jpg\"}\n         */\n\n        private String itemID;\n        private String itemTitle;\n        private String itemType;\n        private String detailUrl;\n        private String pubTime;\n        private String keywords;\n        private String category;\n        private String guid;\n        private String videoLength;\n        private String source;\n        private String brief;\n        private String photoCount;\n        private String sub_column_id;\n        private String datecheck;\n        private ItemImageEntity itemImage;\n\n        public String getItemID() {\n            return itemID;\n        }\n\n        public void setItemID(String itemID) {\n            this.itemID = itemID;\n        }\n\n        public String getItemTitle() {\n            return itemTitle;\n        }\n\n        public void setItemTitle(String itemTitle) {\n            this.itemTitle = itemTitle;\n        }\n\n        public String getItemType() {\n            return itemType;\n        }\n\n        public void setItemType(String itemType) {\n            this.itemType = itemType;\n        }\n\n        public String getDetailUrl() {\n            return detailUrl;\n        }\n\n        public void setDetailUrl(String detailUrl) {\n            this.detailUrl = detailUrl;\n        }\n\n        public String getPubTime() {\n            return pubTime;\n        }\n\n        public void setPubTime(String pubTime) {\n            this.pubTime = pubTime;\n        }\n\n        public String getKeywords() {\n            return keywords;\n        }\n\n        public void setKeywords(String keywords) {\n            this.keywords = keywords;\n        }\n\n        public String getCategory() {\n            return category;\n        }\n\n        public void setCategory(String category) {\n            this.category = category;\n        }\n\n        public String getGuid() {\n            return guid;\n        }\n\n        public void setGuid(String guid) {\n            this.guid = guid;\n        }\n\n        public String getVideoLength() {\n            return videoLength;\n        }\n\n        public void setVideoLength(String videoLength) {\n            this.videoLength = videoLength;\n        }\n\n        public String getSource() {\n            return source;\n        }\n\n        public void setSource(String source) {\n            this.source = source;\n        }\n\n        public String getBrief() {\n            return brief;\n        }\n\n        public void setBrief(String brief) {\n            this.brief = brief;\n        }\n\n        public String getPhotoCount() {\n            return photoCount;\n        }\n\n        public void setPhotoCount(String photoCount) {\n            this.photoCount = photoCount;\n        }\n\n        public String getSub_column_id() {\n            return sub_column_id;\n        }\n\n        public void setSub_column_id(String sub_column_id) {\n            this.sub_column_id = sub_column_id;\n        }\n\n        public String getDatecheck() {\n            return datecheck;\n        }\n\n        public void setDatecheck(String datecheck) {\n            this.datecheck = datecheck;\n        }\n\n        public ItemImageEntity getItemImage() {\n            return itemImage;\n        }\n\n        public void setItemImage(ItemImageEntity itemImage) {\n            this.itemImage = itemImage;\n        }\n\n        public static class ItemImageEntity {\n            /**\n             * imgUrl1 : http://p1.img.cctvpic.com/photoworkspace/2017/01/14/2017011413234866215.jpg\n             */\n\n            private String imgUrl1;\n\n            public String getImgUrl1() {\n                return imgUrl1;\n            }\n\n            public void setImgUrl1(String imgUrl1) {\n                this.imgUrl1 = imgUrl1;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/fragment/LocalAudioFragment.java",
    "content": "package com.atguigu.mobileplayer1020.fragment;\n\nimport android.content.ContentResolver;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.provider.MediaStore;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.activity.SystemAudioPlayerActivity;\nimport com.atguigu.mobileplayer1020.adapter.LocalVideoAdapter;\nimport com.atguigu.mobileplayer1020.base.BaseFragment;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\n\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/6 16:46\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：本地音频\n */\npublic class LocalAudioFragment extends BaseFragment {\n    private ListView listview;\n    private TextView tv_no_media;\n    private LocalVideoAdapter adapter;\n\n    /**\n     * 数据集合\n     */\n    private ArrayList<MediaItem> mediaItems;\n\n    private Handler handler = new Handler(){\n        @Override\n        public void handleMessage(Message msg) {\n            super.handleMessage(msg);\n            //设置适配器\n            if(mediaItems != null && mediaItems.size() >0){\n\n                //有数据\n                //文本隐藏\n                tv_no_media.setVisibility(View.GONE);\n                adapter = new LocalVideoAdapter(mContext,mediaItems,false);\n                //设置适配器\n                listview.setAdapter(adapter);\n\n            }else{\n                //没有数据\n                //文本显示\n                tv_no_media.setVisibility(View.VISIBLE);\n            }\n        }\n    };\n\n    @Override\n    public View initView() {\n        Log.e(\"TAG\", \"本地视频ui初始化了。。\");\n        View view = View.inflate(mContext, R.layout.fragment_local_video, null);\n        listview = (ListView) view.findViewById(R.id.listview);\n        tv_no_media = (TextView) view.findViewById(R.id.tv_no_media);\n\n        //设置item的监听\n        listview.setOnItemClickListener(new MyOnItemClickListener());\n        return view;\n    }\n\n    class MyOnItemClickListener implements AdapterView.OnItemClickListener{\n\n        @Override\n        public void onItemClick(AdapterView<?> parent,View view, int position, long id) {\n            //3.传递列表数据\n            Intent intent = new Intent(mContext,SystemAudioPlayerActivity.class);\n\n            //传递点击的位置\n            intent.putExtra(\"position\",position);\n            startActivity(intent);\n\n        }\n    }\n\n    @Override\n    public void initData() {\n        super.initData();\n        Log.e(\"TAG\", \"本地视频数据初始化了。。\");\n        //在子线程中加载视频\n        getDataFromLocal();\n    }\n\n    /**\n     * 子线程中得到视频\n     */\n    private void getDataFromLocal() {\n        new Thread() {\n            @Override\n            public void run() {\n                super.run();\n\n                //初始化集合\n                mediaItems = new ArrayList<MediaItem>();\n                ContentResolver resolver = mContext.getContentResolver();\n                //sdcard 的视频路径\n                Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n                String[] objs = {\n                        MediaStore.Audio.Media.DISPLAY_NAME,//在sdcard显示的视频名称\n                        MediaStore.Audio.Media.DURATION,//视频的时长,毫秒\n                        MediaStore.Audio.Media.SIZE,//文件大小-byte\n                        MediaStore.Audio.Media.DATA,//在sdcard的路径-播放地址\n                        MediaStore.Audio.Media.ARTIST//艺术家\n                };\n                Cursor cusor = resolver.query(uri, objs, null, null, null);\n                if (cusor != null) {\n\n                    while (cusor.moveToNext()) {\n\n                        MediaItem mediaItem = new MediaItem();\n\n                        //添加到集合中\n                        mediaItems.add(mediaItem);//可以\n\n                        String name = cusor.getString(0);\n                        mediaItem.setName(name);\n                        long duration = cusor.getLong(1);\n                        mediaItem.setDuration(duration);\n                        long size = cusor.getLong(2);\n                        mediaItem.setSize(size);\n                        String data = cusor.getString(3);//播放地址\n                        mediaItem.setData(data);\n                        String artist = cusor.getString(4);//艺术家\n                        mediaItem.setArtist(artist);\n\n                    }\n\n                    cusor.close();\n                }\n\n\n                //发消息-切换到主线程\n                handler.sendEmptyMessage(2);\n\n\n            }\n        }.start();\n\n    }\n\n\n    @Override\n    public void onRefrshData() {\n        super.onRefrshData();\n//        Log.e(\"TAG\",\"onHiddenChanged。。\"+this.toString());\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/fragment/LocalVideoFragment.java",
    "content": "package com.atguigu.mobileplayer1020.fragment;\n\nimport android.content.ContentResolver;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.provider.MediaStore;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.activity.SystemVideoPlayerActivity;\nimport com.atguigu.mobileplayer1020.adapter.LocalVideoAdapter;\nimport com.atguigu.mobileplayer1020.base.BaseFragment;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\n\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/6 16:46\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：本地视频\n */\npublic class LocalVideoFragment extends BaseFragment {\n    private ListView listview;\n    private TextView tv_no_media;\n    private LocalVideoAdapter adapter;\n\n    /**\n     * 数据集合\n     */\n    private ArrayList<MediaItem> mediaItems;\n\n    private Handler handler = new Handler(){\n        @Override\n        public void handleMessage(Message msg) {\n            super.handleMessage(msg);\n            //设置适配器\n            if(mediaItems != null && mediaItems.size() >0){\n\n                //有数据\n                //文本隐藏\n                tv_no_media.setVisibility(View.GONE);\n                adapter = new LocalVideoAdapter(mContext,mediaItems, true);\n                //设置适配器\n                listview.setAdapter(adapter);\n\n            }else{\n                //没有数据\n                //文本显示\n                tv_no_media.setVisibility(View.VISIBLE);\n            }\n        }\n    };\n\n    @Override\n    public View initView() {\n        Log.e(\"TAG\", \"本地视频ui初始化了。。\");\n        View view = View.inflate(mContext, R.layout.fragment_local_video, null);\n        listview = (ListView) view.findViewById(R.id.listview);\n        tv_no_media = (TextView) view.findViewById(R.id.tv_no_media);\n\n        //设置item的监听\n        listview.setOnItemClickListener(new MyOnItemClickListener());\n        return view;\n    }\n\n    class MyOnItemClickListener implements AdapterView.OnItemClickListener{\n\n        @Override\n        public void onItemClick(AdapterView<?> parent,View view, int position, long id) {\n            MediaItem mediaItem = mediaItems.get(position);\n//            Toast.makeText(mContext, \"mediaItem==\"+mediaItem.toString(), Toast.LENGTH_SHORT).show();\n            //\n            //1.调起系统的播放器播放视频--隐式意图\n//            Intent intent = new Intent();\n//            //第一参数：播放路径\n//            //第二参数：路径对应的类型\n//            intent.setDataAndType(Uri.parse(mediaItem.getData()),\"video/*\");\n//            startActivity(intent);\n            //2.调起自定义播放器\n//            Intent intent = new Intent(mContext,SystemVideoPlayerActivity.class);\n//            //第一参数：播放路径\n//            //第二参数：路径对应的类型\n//            intent.setDataAndType(Uri.parse(mediaItem.getData()),\"video/*\");//一个播放地址\n//            startActivity(intent);\n            //3.传递列表数据\n            Intent intent = new Intent(mContext,SystemVideoPlayerActivity.class);\n\n            Bundle bundle = new Bundle();\n            //列表数据\n            bundle.putSerializable(\"videolist\",mediaItems);\n            intent.putExtras(bundle);\n            //传递点击的位置\n            intent.putExtra(\"position\",position);\n            startActivity(intent);\n\n        }\n    }\n\n    @Override\n    public void initData() {\n        super.initData();\n        Log.e(\"TAG\", \"本地视频数据初始化了。。\");\n        //在子线程中加载视频\n        getDataFromLocal();\n    }\n\n    /**\n     * 子线程中得到视频\n     */\n    private void getDataFromLocal() {\n        new Thread() {\n            @Override\n            public void run() {\n                super.run();\n\n                //初始化集合\n                mediaItems = new ArrayList<MediaItem>();\n                ContentResolver resolver = mContext.getContentResolver();\n                //sdcard 的视频路径\n                Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n                String[] objs = {\n                        MediaStore.Video.Media.DISPLAY_NAME,//在sdcard显示的视频名称\n                        MediaStore.Video.Media.DURATION,//视频的时长,毫秒\n                        MediaStore.Video.Media.SIZE,//文件大小-byte\n                        MediaStore.Video.Media.DATA,//在sdcard的路径-播放地址\n                        MediaStore.Video.Media.ARTIST//艺术家\n                };\n                Cursor cusor = resolver.query(uri, objs, null, null, null);\n                if (cusor != null) {\n\n                    while (cusor.moveToNext()) {\n\n                        MediaItem mediaItem = new MediaItem();\n\n                        //添加到集合中\n                        mediaItems.add(mediaItem);//可以\n\n                        String name = cusor.getString(0);\n                        mediaItem.setName(name);\n                        long duration = cusor.getLong(1);\n                        mediaItem.setDuration(duration);\n                        long size = cusor.getLong(2);\n                        mediaItem.setSize(size);\n                        String data = cusor.getString(3);//播放地址\n                        mediaItem.setData(data);\n                        String artist = cusor.getString(4);//艺术家\n                        mediaItem.setArtist(artist);\n\n                    }\n\n                    cusor.close();\n                }\n\n\n                //发消息-切换到主线程\n                handler.sendEmptyMessage(2);\n\n\n            }\n        }.start();\n\n    }\n\n\n    @Override\n    public void onRefrshData() {\n        super.onRefrshData();\n//        Log.e(\"TAG\",\"onHiddenChanged。。\"+this.toString());\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/fragment/NetAudioFragment.java",
    "content": "package com.atguigu.mobileplayer1020.fragment;\n\nimport android.content.Intent;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.activity.PicassoSampleActivity;\nimport com.atguigu.mobileplayer1020.adapter.NetAudioFragmentAdapter;\nimport com.atguigu.mobileplayer1020.base.BaseFragment;\nimport com.atguigu.mobileplayer1020.bean.NetAudioBean;\nimport com.atguigu.mobileplayer1020.utils.Constant;\nimport com.google.gson.Gson;\n\nimport org.xutils.common.Callback;\nimport org.xutils.http.RequestParams;\nimport org.xutils.x;\n\nimport java.util.List;\n\nimport butterknife.ButterKnife;\nimport butterknife.InjectView;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/6 16:46\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：网络音频\n */\npublic class NetAudioFragment extends BaseFragment {\n\n    @InjectView(R.id.listview)\n    ListView listview;\n    @InjectView(R.id.progressbar)\n    ProgressBar progressbar;\n    @InjectView(R.id.tv_nomedia)\n    TextView tvNomedia;\n    /**\n     * 数据集合\n     */\n    private List<NetAudioBean.ListBean> listDatas;\n    private NetAudioFragmentAdapter myAdapter;\n\n    @Override\n    public View initView() {\n        Log.e(\"TAG\", \"网络音频ui初始化了。。\");\n        View view = View.inflate(mContext, R.layout.fragment_net_audio, null);\n        ButterKnife.inject(this, view);\n        //设置ListView的item的点击事件\n        //设置点击事件\n        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n\n\n                NetAudioBean.ListBean listEntity = listDatas.get(position);\n                if(listEntity !=null ){\n                    //3.传递视频列表\n                    Intent intent = new Intent(mContext,PicassoSampleActivity.class);\n                    if(listEntity.getType().equals(\"gif\")){\n                        String url = listEntity.getGif().getImages().get(0);\n                        intent.putExtra(\"url\",url);\n                        mContext.startActivity(intent);\n                    }else if(listEntity.getType().equals(\"image\")){\n                        String url = listEntity.getImage().getThumbnail_small().get(0);\n                        intent.putExtra(\"url\",url);\n                        mContext.startActivity(intent);\n                    }\n                }\n\n\n            }\n        });\n\n        return view;\n    }\n\n    @Override\n    public void initData() {\n        super.initData();\n        Log.e(\"TAG\", \"网络音频数据初始化了。。\");\n//        tvNomedia.setVisibility(View.VISIBLE);\n//        tvNomedia.setText(\"hh\");\n        getDataFromNet();\n    }\n\n\n    @Override\n    public void onRefrshData() {\n        super.onRefrshData();\n//        Log.e(\"TAG\",\"onHiddenChanged。。\"+this.toString());\n\n    }\n\n    private void getDataFromNet() {\n        RequestParams params = new RequestParams(Constant.NET_AUDIO);\n        x.http().get(params, new Callback.CommonCallback<String>() {\n            @Override\n            public void onSuccess(String result) {\n\n                Log.e(\"TAG\", \"网络音乐请求成功\" + result);\n                processData(result);\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n                Log.e(\"TAG\", \"网络音乐请求失败\" + ex.getMessage());\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n\n            }\n\n            @Override\n            public void onFinished() {\n\n            }\n        });\n    }\n\n    private void processData(String json) {\n        NetAudioBean netAudioBean = new Gson().fromJson(json, NetAudioBean.class);\n        listDatas = netAudioBean.getList();\n\n\n        Log.e(\"TAG\",\"解决成功==\"+listDatas.get(0).getText());\n        if(listDatas != null && listDatas.size() >0){\n            //有视频\n            tvNomedia.setVisibility(View.GONE);\n            //设置适配器\n            myAdapter = new NetAudioFragmentAdapter(mContext,listDatas);\n            listview.setAdapter(myAdapter);\n        }else{\n            //没有视频\n            tvNomedia.setVisibility(View.VISIBLE);\n        }\n\n        progressbar.setVisibility(View.GONE);\n\n    }\n\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        ButterKnife.reset(this);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/fragment/NetVideoFragment.java",
    "content": "package com.atguigu.mobileplayer1020.fragment;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.AdapterView;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.activity.SystemVideoPlayerActivity;\nimport com.atguigu.mobileplayer1020.adapter.NetVideoAdapter;\nimport com.atguigu.mobileplayer1020.base.BaseFragment;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\nimport com.atguigu.mobileplayer1020.utils.CacheUtils;\nimport com.atguigu.mobileplayer1020.utils.Constant;\nimport com.cjj.MaterialRefreshLayout;\nimport com.cjj.MaterialRefreshListener;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.xutils.common.Callback;\nimport org.xutils.http.RequestParams;\nimport org.xutils.view.annotation.ViewInject;\nimport org.xutils.x;\n\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/6 16:46\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：网络视频\n */\npublic class NetVideoFragment extends BaseFragment {\n\n    /**\n     * 数据集合\n     */\n    private ArrayList<MediaItem> mediaItems;\n    private NetVideoAdapter adapter;\n\n    @ViewInject(R.id.listview)\n    private ListView listview;\n\n    @ViewInject(R.id.tv_no_media)\n    private  TextView tv_no_media;\n\n    @ViewInject(R.id.refresh)\n    MaterialRefreshLayout refreshLayout;\n    @Override\n    public View initView() {\n        Log.e(\"TAG\",\"网络视频ui初始化了。。\");\n        View view  = View.inflate(mContext, R.layout.fragment_net_video,null);\n        //把view注入到xUtils3框中\n        x.view().inject(NetVideoFragment.this,view);\n        //才初始化好的\n\n        listview.setOnItemClickListener(new MyOnItemClickListener());\n        //监听下拉和上拉刷新\n        refreshLayout.setMaterialRefreshListener(new MyMaterialRefreshListener());\n        return view;\n    }\n\n    /**\n     * 是否加载更多\n     */\n    private boolean isLoadMore = false;\n\n    class MyMaterialRefreshListener extends MaterialRefreshListener {\n\n        @Override\n        public void onRefresh(MaterialRefreshLayout materialRefreshLayout) {\n//            Toast.makeText(mContext, \"下拉刷新\", Toast.LENGTH_SHORT).show();\n            isLoadMore = false;\n            getDataFromNet();\n        }\n\n        /**\n         * 加载更多的回调\n         * @param materialRefreshLayout\n         */\n        @Override\n        public void onRefreshLoadMore(MaterialRefreshLayout materialRefreshLayout) {\n            super.onRefreshLoadMore(materialRefreshLayout);\n            isLoadMore = true;\n//            Toast.makeText(mContext, \"加载更多\", Toast.LENGTH_SHORT).show();\n            getDataFromNet();\n        }\n    }\n\n    class MyOnItemClickListener implements AdapterView.OnItemClickListener{\n\n        @Override\n        public void onItemClick(AdapterView<?> parent,View view, int position, long id) {\n            //传递列表数据\n            Intent intent = new Intent(mContext,SystemVideoPlayerActivity.class);\n\n            Bundle bundle = new Bundle();\n            //列表数据\n            bundle.putSerializable(\"videolist\",mediaItems);\n            intent.putExtras(bundle);\n            //传递点击的位置\n            intent.putExtra(\"position\",position);\n            startActivity(intent);\n\n        }\n    }\n\n    @Override\n    public void initData() {\n        super.initData();\n        Log.e(\"TAG\",\"网络视频数据初始化了。。\");\n//        tv_no_media.setText(\"呵呵\");\n        String json = CacheUtils.getString(mContext,Constant.NET_URL);\n        if(!TextUtils.isEmpty(json)){\n            processData(json);\n        }\n        getDataFromNet();\n    }\n\n    /**\n     * 使用xutils3联网请求数据\n     */\n    private void getDataFromNet() {\n        //网络的路径\n        RequestParams params  = new RequestParams(Constant.NET_URL);\n        x.http().get(params, new Callback.CommonCallback<String>() {\n            @Override\n           public void onSuccess(String result) {\n\n                Log.e(\"TA\",\"xUtils3联网请求成功==\");\n                Log.e(\"TAG\",\"线程名称==\"+Thread.currentThread().getName());\n                CacheUtils.putString(mContext,Constant.NET_URL,result);\n\n                processData(result);\n\n                if(!isLoadMore){\n                    //完成刷新\n                    refreshLayout.finishRefresh();\n                }else{\n                    //把上拉的隐藏\n                    refreshLayout.finishRefreshLoadMore();\n                }\n\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n                Log.e(\"TAG\",\"xUtils3请求失败了==\"+ex.getMessage());\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n\n            }\n\n            @Override\n            public void onFinished() {\n\n            }\n        });\n\n    }\n\n    /**\n     * 解析json数据：gson解析，fastjson解析和手动解析（原生的api）\n     * 显示数据-设置适配器\n     * @param json\n     */\n    private void processData(String json) {\n        if(!isLoadMore){\n            mediaItems = parsedJson(json);\n\n            Log.e(\"TAG\",\"mediaItems.get(0).getName()==\"+mediaItems.get(0).getName());\n            if(mediaItems != null && mediaItems.size() >0){\n                //有数据\n                tv_no_media.setVisibility(View.GONE);\n                adapter = new NetVideoAdapter(mContext,mediaItems);\n                //设置适配器\n                listview.setAdapter(adapter);\n\n            }else{\n                tv_no_media.setVisibility(View.VISIBLE);\n            }\n        }else{\n\n            //加载更多\n            ArrayList<MediaItem> mediaItem  = parsedJson(json);\n            mediaItems.addAll(mediaItem);\n            //刷新适配器\n            adapter.notifyDataSetChanged();//getCount-->getView\n\n\n        }\n\n    }\n\n    /**\n     * 使用系统的接口解析json数据\n     * @param json\n     * @return\n     */\n    private ArrayList<MediaItem> parsedJson(String json) {\n        ArrayList<MediaItem> mediaItems = new ArrayList<>();\n        try {\n            JSONObject jsonObject = new JSONObject(json);\n            JSONArray jsonArray = jsonObject.getJSONArray(\"trailers\");\n\n\n            for (int i=0;i<jsonArray.length();i++){\n\n                MediaItem mediaItem = new MediaItem();\n\n                mediaItems.add(mediaItem);//添加到集合中\n\n                JSONObject jsonObjectItem = (JSONObject) jsonArray.get(i);\n                String name = jsonObjectItem.optString(\"movieName\");\n                mediaItem.setName(name);\n                String desc = jsonObjectItem.optString(\"videoTitle\");\n                mediaItem.setDesc(desc);\n                String url = jsonObjectItem.optString(\"url\");\n                mediaItem.setData(Constant.BASE_URL+url);\n                String hightUrl = jsonObjectItem.optString(\"hightUrl\");\n                mediaItem.setHeightUrl(hightUrl);\n                String coverImg = jsonObjectItem.optString(\"coverImg\");\n                mediaItem.setImageUrl(coverImg);\n                int videoLength = jsonObjectItem.optInt(\"videoLength\");\n                mediaItem.setDuration(videoLength);\n\n            }\n\n\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n\n\n        return mediaItems;\n    }\n\n    @Override\n    public void onRefrshData() {\n        super.onRefrshData();\n//        Log.e(\"TAG\",\"onHiddenChanged。。\"+this.toString());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/fragment/RecyclerViewFragment.java",
    "content": "package com.atguigu.mobileplayer1020.fragment;\n\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.adapter.RecyclerFragmentAdapter;\nimport com.atguigu.mobileplayer1020.base.BaseFragment;\nimport com.atguigu.mobileplayer1020.bean.NetAudioBean;\nimport com.atguigu.mobileplayer1020.utils.Constant;\nimport com.google.gson.Gson;\n\nimport org.xutils.common.Callback;\nimport org.xutils.http.RequestParams;\nimport org.xutils.x;\n\nimport java.util.List;\n\nimport butterknife.ButterKnife;\nimport butterknife.InjectView;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/6 16:46\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：网络音频\n */\npublic class RecyclerViewFragment extends BaseFragment {\n\n    @InjectView(R.id.recyclerview)\n    RecyclerView recyclerview;\n    @InjectView(R.id.progressbar)\n    ProgressBar progressbar;\n    @InjectView(R.id.tv_nomedia)\n    TextView tvNomedia;\n    /**\n     * 数据集合\n     */\n    private List<NetAudioBean.ListBean> listDatas;\n    private RecyclerFragmentAdapter myAdapter;\n\n    @Override\n    public View initView() {\n        Log.e(\"TAG\", \"网络音频ui初始化了。。\");\n        View view = View.inflate(mContext, R.layout.fragment_recyclerview, null);\n        ButterKnife.inject(this, view);\n        //设置ListView的item的点击事件\n        //设置点击事件\n//        listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n//            @Override\n//            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n//\n//\n//                NetAudioBean.ListBean listEntity = listDatas.get(position);\n//                if(listEntity !=null ){\n//                    //3.传递视频列表\n//                    Intent intent = new Intent(mContext,PicassoSampleActivity.class);\n//                    if(listEntity.getType().equals(\"gif\")){\n//                        String url = listEntity.getGif().getImages().get(0);\n//                        intent.putExtra(\"url\",url);\n//                        mContext.startActivity(intent);\n//                    }else if(listEntity.getType().equals(\"image\")){\n//                        String url = listEntity.getImage().getThumbnail_small().get(0);\n//                        intent.putExtra(\"url\",url);\n//                        mContext.startActivity(intent);\n//                    }\n//                }\n//\n//\n//            }\n//        });\n\n        return view;\n    }\n\n    @Override\n    public void initData() {\n        super.initData();\n        Log.e(\"TAG\", \"网络音频数据初始化了。。\");\n//        tvNomedia.setVisibility(View.VISIBLE);\n//        tvNomedia.setText(\"hh\");\n        getDataFromNet();\n    }\n\n\n    @Override\n    public void onRefrshData() {\n        super.onRefrshData();\n//        Log.e(\"TAG\",\"onHiddenChanged。。\"+this.toString());\n\n    }\n\n    private void getDataFromNet() {\n        RequestParams params = new RequestParams(Constant.NET_AUDIO);\n        x.http().get(params, new Callback.CommonCallback<String>() {\n            @Override\n            public void onSuccess(String result) {\n\n                Log.e(\"TAG\", \"网络音乐请求成功\" + result);\n                processData(result);\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n                Log.e(\"TAG\", \"网络音乐请求失败\" + ex.getMessage());\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n\n            }\n\n            @Override\n            public void onFinished() {\n\n            }\n        });\n    }\n\n    private void processData(String json) {\n        NetAudioBean netAudioBean = new Gson().fromJson(json, NetAudioBean.class);\n        listDatas = netAudioBean.getList();\n\n\n        Log.e(\"TAG\",\"解决成功==\"+listDatas.get(0).getText());\n        if(listDatas != null && listDatas.size() >0){\n            //有视频\n            tvNomedia.setVisibility(View.GONE);\n            //设置适配器\n            myAdapter = new RecyclerFragmentAdapter(mContext,listDatas);\n            recyclerview.setAdapter(myAdapter);\n\n            recyclerview.setLayoutManager(new LinearLayoutManager(mContext,LinearLayoutManager.VERTICAL,false));\n        }else{\n            //没有视频\n            tvNomedia.setVisibility(View.VISIBLE);\n        }\n\n        progressbar.setVisibility(View.GONE);\n\n    }\n\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        ButterKnife.reset(this);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/service/MusicPlayerService.java",
    "content": "package com.atguigu.mobileplayer1020.service;\n\nimport android.app.Notification;\nimport android.app.NotificationManager;\nimport android.app.PendingIntent;\nimport android.app.Service;\nimport android.content.ContentResolver;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.media.MediaPlayer;\nimport android.net.Uri;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.provider.MediaStore;\nimport android.support.annotation.Nullable;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport com.atguigu.mobileplayer1020.IMusicPlayerService;\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.activity.SystemAudioPlayerActivity;\nimport com.atguigu.mobileplayer1020.bean.MediaItem;\nimport com.atguigu.mobileplayer1020.utils.CacheUtils;\n\nimport org.greenrobot.eventbus.EventBus;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/11 15:41\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用： 播放音乐的服务\n */\npublic class MusicPlayerService extends Service {\n\n    public static final String OPEN_COMPLETE = \"open_complete\";\n    /**\n     * AIDL生成的类\n     */\n    IMusicPlayerService.Stub stub = new IMusicPlayerService.Stub() {\n        //把服务当成成员变量\n        MusicPlayerService service = MusicPlayerService.this;\n\n        @Override\n        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {\n\n        }\n\n        @Override\n        public void openAudio(int position) throws RemoteException {\n            service.openAudio(position);\n        }\n\n        @Override\n        public void start() throws RemoteException {\n            service.start();\n\n        }\n\n        @Override\n        public void pause() throws RemoteException {\n            service.pause();\n        }\n\n        @Override\n        public String getAudioName() throws RemoteException {\n            return service.getAudioName();\n        }\n\n        @Override\n        public String getArtistName() throws RemoteException {\n            return service.getArtistName();\n        }\n\n        @Override\n        public int getCurrentPosition() throws RemoteException {\n            return service.getCurrentPosition();\n        }\n\n        @Override\n        public int getDuration() throws RemoteException {\n            return service.getDuration();\n        }\n\n        @Override\n        public void next() throws RemoteException {\n            service.next();\n\n        }\n\n        @Override\n        public void pre() throws RemoteException {\n            service.pre();\n        }\n\n        @Override\n        public int getPlayMode() throws RemoteException {\n            return service.getPlayMode();\n        }\n\n        @Override\n        public void setPlayMode(int mode) throws RemoteException {\n            service.setPlayMode(mode);\n        }\n\n        @Override\n        public boolean isPlaying() throws RemoteException {\n            return mediaPlayer.isPlaying();\n        }\n\n        @Override\n        public void seekTo(int postion) throws RemoteException {\n            mediaPlayer.seekTo(postion);\n        }\n\n        @Override\n        public String getAudioPath() throws RemoteException {\n            return mediaItem.getData();\n        }\n\n        @Override\n        public int getAudioSessionId() throws RemoteException {\n            return mediaPlayer.getAudioSessionId();\n        }\n    };\n    private ArrayList<MediaItem> mediaItems;\n    /**\n     * 音频是否加载完成\n     */\n    private boolean isLoaded = false;\n    private MediaItem mediaItem;\n    private int position;\n    /**\n     * 播放器\n     */\n    private MediaPlayer mediaPlayer;\n\n    /**\n     * 顺序播放\n     */\n    public static final int REPEATE_NOMAL = 1;\n\n    /**\n     * 单曲播放\n     */\n    public static final int REPEATE_SINGLE = 2;\n\n\n    /**\n     * 全部循环\n     */\n    public static final int REPEATE_ALL = 3;\n\n    private int playmode = REPEATE_NOMAL;\n    private boolean isNext = false;\n\n\n    /**\n     * 返回代理类\n     *\n     * @param intent\n     * @return\n     */\n    @Nullable\n    @Override\n    public IBinder onBind(Intent intent) {\n        return stub;\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Log.e(\"TAG\",\"service==\"+this.toString());\n        playmode = CacheUtils.getPlaymode(this, \"playmode\");\n        getDataFromLocal();\n    }\n\n    /**\n     * 子线程中得到音频\n     */\n    private void getDataFromLocal() {\n        new Thread() {\n            @Override\n            public void run() {\n                super.run();\n\n                //初始化集合\n                mediaItems = new ArrayList<MediaItem>();\n                ContentResolver resolver = getContentResolver();\n                //sdcard 的视频路径\n                Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n                String[] objs = {\n                        MediaStore.Audio.Media.DISPLAY_NAME,//在sdcard显示的视频名称\n                        MediaStore.Audio.Media.DURATION,//视频的时长,毫秒\n                        MediaStore.Audio.Media.SIZE,//文件大小-byte\n                        MediaStore.Audio.Media.DATA,//在sdcard的路径-播放地址\n                        MediaStore.Audio.Media.ARTIST//艺术家\n                };\n                Cursor cusor = resolver.query(uri, objs, null, null, null);\n                if (cusor != null) {\n\n                    while (cusor.moveToNext()) {\n\n                        MediaItem mediaItem = new MediaItem();\n\n                        //添加到集合中\n                        mediaItems.add(mediaItem);//可以\n\n                        String name = cusor.getString(0);\n                        mediaItem.setName(name);\n                        long duration = cusor.getLong(1);\n                        mediaItem.setDuration(duration);\n                        long size = cusor.getLong(2);\n                        mediaItem.setSize(size);\n                        String data = cusor.getString(3);//播放地址\n                        mediaItem.setData(data);\n                        String artist = cusor.getString(4);//艺术家\n                        mediaItem.setArtist(artist);\n\n                    }\n\n                    cusor.close();\n                }\n\n\n                //音频加载完成\n                isLoaded = true;\n\n\n            }\n        }.start();\n\n    }\n\n    /**\n     * 根据位置打开一个音频并且播放\n     *\n     * @param position\n     */\n    void openAudio(int position) {\n        if (mediaItems != null && mediaItems.size() > 0) {\n            mediaItem = mediaItems.get(position);\n            this.position = position;\n\n            //MediaPlayer\n            if (mediaPlayer != null) {\n                mediaPlayer.reset();//上一曲重置\n                mediaPlayer = null;\n            }\n\n            mediaPlayer = new MediaPlayer();\n            //设置三个监听\n            mediaPlayer.setOnPreparedListener(new MyOnPreparedListener());\n            mediaPlayer.setOnCompletionListener(new MyOnCompletionListener());\n            mediaPlayer.setOnErrorListener(new MyOnErrorListener());\n\n            //设置播放地址\n            try {\n                mediaPlayer.setDataSource(mediaItem.getData());\n                mediaPlayer.prepareAsync();\n                isNext = false;\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n\n\n        } else if (!isLoaded) {\n            Toast.makeText(this, \"没有加载完成\", Toast.LENGTH_SHORT).show();\n        }\n\n    }\n\n    class MyOnErrorListener implements MediaPlayer.OnErrorListener {\n\n        @Override\n        public boolean onError(MediaPlayer mp, int what, int extra) {\n            next();\n            return true;\n        }\n    }\n\n    class MyOnCompletionListener implements MediaPlayer.OnCompletionListener {\n\n\n        @Override\n        public void onCompletion(MediaPlayer mp) {\n            isNext = true;\n            next();\n        }\n    }\n\n\n    class MyOnPreparedListener implements MediaPlayer.OnPreparedListener {\n\n        @Override\n        public void onPrepared(MediaPlayer mp) {\n//            notifyChange(OPEN_COMPLETE);\n            //4.发消息-传递数据\n            EventBus.getDefault().post(mediaItem);\n            start();\n        }\n    }\n\n    private void notifyChange(String action) {\n        Intent intent = new Intent();\n        intent.setAction(action);\n\n        //发广播\n        sendBroadcast(intent);\n    }\n\n    private NotificationManager nm;\n\n    /**\n     * 开始播放音频\n     */\n    void start() {\n        mediaPlayer.start();\n        //在状态栏创建通知\n        nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);\n\n        Intent intent = new Intent(this, SystemAudioPlayerActivity.class);\n        intent.putExtra(\"notification\", true);//标识来自状态栏\n        //包含意图\n        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);\n        Notification notificaton = null;\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {\n            notificaton = new Notification.Builder(this)\n                    //图片\n                    .setSmallIcon(R.drawable.notification_music_playing)\n                    //标题\n                    .setContentTitle(\"321音乐\")\n                    //内容\n                    .setContentText(\"正在播放:\" + getAudioName())\n                    //点击动作，延期意图\n                    .setContentIntent(pendingIntent)\n                    .build();\n\n            //点击后还存在属性\n            notificaton.flags = Notification.FLAG_ONGOING_EVENT;\n        }\n        nm.notify(1, notificaton);\n\n    }\n\n    /**\n     * 暂停\n     */\n    void pause() {\n        mediaPlayer.pause();\n        //移除状态栏的通知\n        nm.cancel(1);\n    }\n\n    /**\n     * 得到歌曲的名称\n     */\n    String getAudioName() {\n        if (mediaItem != null) {\n            return mediaItem.getName();\n        }\n        return \"\";\n    }\n\n    /**\n     * 得到歌曲演唱者的名字\n     */\n    String getArtistName() {\n        if (mediaItem != null) {\n            return mediaItem.getArtist();\n        }\n        return \"\";\n\n    }\n\n    /**\n     * 得到歌曲的当前播放进度\n     */\n    int getCurrentPosition() {\n        if (mediaPlayer != null) {\n            return mediaPlayer.getCurrentPosition();\n        }\n        return 0;\n    }\n\n    /**\n     * 得到歌曲的当前总进度\n     */\n    int getDuration() {\n        if (mediaPlayer != null) {\n            return mediaPlayer.getDuration();\n        }\n        return 0;\n    }\n\n    /**\n     * 播放下一首歌曲\n     */\n    void next() {\n\n        //设置下一曲对应的位置\n        setNextPostion();\n        //根据对应的位置去播放\n        openNextAudio();\n\n    }\n\n    private void openNextAudio() {\n        int playmode = getPlayMode();\n\n        if (playmode == MusicPlayerService.REPEATE_NOMAL) {\n\n            if (position <= mediaItems.size() - 1) {\n                openAudio(position);\n            } else {\n                position = mediaItems.size() - 1;\n            }\n\n        } else if (playmode == MusicPlayerService.REPEATE_SINGLE) {\n            openAudio(position);\n\n        } else if (playmode == MusicPlayerService.REPEATE_ALL) {\n            openAudio(position);\n        } else {\n            if (position <= mediaItems.size() - 1) {\n                openAudio(position);\n            } else {\n                position = mediaItems.size() - 1;\n            }\n        }\n    }\n\n    private void setNextPostion() {\n        int playmode = getPlayMode();\n\n        if (playmode == MusicPlayerService.REPEATE_NOMAL) {\n\n            position++;\n\n        } else if (playmode == MusicPlayerService.REPEATE_SINGLE) {\n            if(!isNext){\n                isNext = false;\n                position++;\n                if (position > mediaItems.size() - 1) {\n                    position = 0;\n                }\n\n            }\n\n        } else if (playmode == MusicPlayerService.REPEATE_ALL) {\n            position++;\n            if (position > mediaItems.size() - 1) {\n                position = 0;\n            }\n        } else {\n            position++;\n        }\n    }\n\n    /**\n     * 播放上一首歌曲\n     */\n    void pre() {\n        //设置下一曲对应的位置\n        setPrePostion();\n        //根据对应的位置去播放\n        openPreAudio();\n    }\n\n    private void setPrePostion() {\n        int playmode = getPlayMode();\n\n        if (playmode == MusicPlayerService.REPEATE_NOMAL) {\n            position--;\n        } else if (playmode == MusicPlayerService.REPEATE_SINGLE) {\n            if(!isNext){\n                isNext = false;\n                position--;\n                if (position < 0) {\n                    position = mediaItems.size()-1;\n                }\n\n            }\n\n        } else if (playmode == MusicPlayerService.REPEATE_ALL) {\n            position--;\n            if (position < 0) {\n                position = mediaItems.size()-1;\n            }\n        } else {\n            position--;\n        }\n    }\n\n    private void openPreAudio() {\n        int playmode = getPlayMode();\n\n        if (playmode == MusicPlayerService.REPEATE_NOMAL) {\n\n            if (position >= 0) {\n                openAudio(position);\n            } else {\n                position = 0;\n            }\n\n        } else if (playmode == MusicPlayerService.REPEATE_SINGLE) {\n            openAudio(position);\n\n        } else if (playmode == MusicPlayerService.REPEATE_ALL) {\n            openAudio(position);\n        } else {\n            if (position >= 0) {\n                openAudio(position);\n            } else {\n                position = 0;\n            }\n        }\n    }\n\n    /**\n     * 得到播放模式\n     */\n    int getPlayMode() {\n        return playmode;\n    }\n\n    /**\n     * 设置播放模式\n     */\n    void setPlayMode(int mode) {\n        this.playmode = mode;\n        CacheUtils.setPlaymode(this, \"playmode\", playmode);\n\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/utils/CacheUtils.java",
    "content": "package com.atguigu.mobileplayer1020.utils;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\nimport com.atguigu.mobileplayer1020.service.MusicPlayerService;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/11 11:35\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：缓存工具类\n */\npublic class CacheUtils {\n    /**\n     * 得到缓存的文本数据\n     * @param mContext\n     * @param key\n     * @return\n     */\n    public static String getString(Context mContext, String key) {\n        SharedPreferences sp = mContext.getSharedPreferences(\"atguigu\",Context.MODE_PRIVATE);\n        return sp.getString(key,\"\");\n    }\n\n    /**\n     * 保持数据\n     * @param mContext\n     * @param key\n     * @param value\n     */\n    public static void putString(Context mContext, String key, String value) {\n        SharedPreferences sp = mContext.getSharedPreferences(\"atguigu\",Context.MODE_PRIVATE);\n        sp.edit().putString(key,value).commit();\n    }\n\n    /**\n     * 保持播放模式\n     * @param context\n     * @param key\n     * @param value\n     */\n    public static void setPlaymode(Context context, String key, int value) {\n        SharedPreferences sp = context.getSharedPreferences(\"atguigu\",Context.MODE_PRIVATE);\n        sp.edit().putInt(key,value).commit();\n    }\n\n    /**\n     * 得到保存播放模式\n     * @param context\n     * @param key\n     * @return\n     */\n    public static int getPlaymode(Context context, String key) {\n        SharedPreferences sp = context.getSharedPreferences(\"atguigu\",Context.MODE_PRIVATE);\n        return sp.getInt(key, MusicPlayerService.REPEATE_NOMAL);\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/utils/Constant.java",
    "content": "package com.atguigu.mobileplayer1020.utils;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/10 16:20\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：联网的路径\n */\npublic class Constant {\n\n//    public static final String BASE_URL = \"http://192.168.11.218:8080/web_home\";\n    public static final String BASE_URL = \"http://182.92.5.3/mobileplayer\";\n    /**\n     * 网络视频路径\n     */\n//    public static final String NET_URL = \"http://api.m.mtime.cn/PageSubArea/TrailerList.api\";\n    public static final String NET_URL = BASE_URL+\"/json/video.json\";\n\n\n\n    /**\n     * 网络音乐地址\n     */\n//    public static final String NET_AUDIO = \"http://s.budejie.com/topic/list/jingxuan/1/budejie-android-6.2.8/0-20.json?market=baidu&udid=863425026599592&appname=baisibudejie&os=4.2.2&client=android&visiting=&mac=98%3A6c%3Af5%3A4b%3A72%3A6d&ver=6.2.8\";\n    public static final String NET_AUDIO = BASE_URL+\"/json/audio.json\";\n\n\n\n    /**\n     * 搜索地址\n     */\n    public static final String NET_SEARCH_URL = \"http://hot.news.cntv.cn/index.php?controller=list&action=searchList&sort=date&n=20&wd=\";\n\n\n}"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/utils/DensityUtil.java",
    "content": "package com.atguigu.mobileplayer1020.utils;\n\nimport android.content.Context;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/13 16:42\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：xxxx\n */\npublic class DensityUtil {\n\n    /**\n     * 根据手机的分辨率从 dip 的单位 转成为 px(像素)\n     */\n    public static int dip2px(Context context, float dpValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n    /**\n     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp\n     */\n    public static int px2dip(Context context, float pxValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (pxValue / scale + 0.5f);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/utils/JsonParser.java",
    "content": "package com.atguigu.mobileplayer1020.utils;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\nimport org.json.JSONTokener;\n\n/**\n * Json结果解析类\n */\npublic class JsonParser {\n\n\tpublic static String parseIatResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\t// 转写结果词，默认使用第一个结果\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tJSONObject obj = items.getJSONObject(0);\n\t\t\t\tret.append(obj.getString(\"w\"));\n//\t\t\t\t如果需要多候选结果，解析数组其他字段\n//\t\t\t\tfor(int j = 0; j < items.length(); j++)\n//\t\t\t\t{\n//\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n//\t\t\t\t\tret.append(obj.getString(\"w\"));\n//\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t} \n\t\treturn ret.toString();\n\t}\n\t\n\tpublic static String parseGrammarResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tfor(int j = 0; j < items.length(); j++)\n\t\t\t\t{\n\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n\t\t\t\t\tif(obj.getString(\"w\").contains(\"nomatch\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tret.append(\"没有匹配结果.\");\n\t\t\t\t\t\treturn ret.toString();\n\t\t\t\t\t}\n\t\t\t\t\tret.append(\"【结果】\" + obj.getString(\"w\"));\n\t\t\t\t\tret.append(\"【置信度】\" + obj.getInt(\"sc\"));\n\t\t\t\t\tret.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tret.append(\"没有匹配结果.\");\n\t\t} \n\t\treturn ret.toString();\n\t}\n\t\n\tpublic static String parseLocalGrammarResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tfor(int j = 0; j < items.length(); j++)\n\t\t\t\t{\n\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n\t\t\t\t\tif(obj.getString(\"w\").contains(\"nomatch\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tret.append(\"没有匹配结果.\");\n\t\t\t\t\t\treturn ret.toString();\n\t\t\t\t\t}\n\t\t\t\t\tret.append(\"【结果】\" + obj.getString(\"w\"));\n\t\t\t\t\tret.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tret.append(\"【置信度】\" + joResult.optInt(\"sc\"));\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tret.append(\"没有匹配结果.\");\n\t\t} \n\t\treturn ret.toString();\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/utils/LyricParaser.java",
    "content": "package com.atguigu.mobileplayer1020.utils;\n\nimport com.atguigu.mobileplayer1020.bean.LyricBean;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStreamReader;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/13 15:25\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：歌词解析工具类\n */\npublic class LyricParaser {\n    private ArrayList<LyricBean> lyricBeens;\n\n    /*\n     得到歌词列表\n     */\n    public ArrayList<LyricBean> getLyricBeens() {\n        return lyricBeens;\n    }\n\n    /**\n     * 是否歌词存在\n     * @return\n     */\n    public boolean isExistsLyric() {\n        return isExistsLyric;\n    }\n\n    private boolean isExistsLyric = false;\n\n    public void readFile(File file) {\n        if (file == null || !file.exists()) {\n            //歌词文件不存在\n            lyricBeens = null;\n            isExistsLyric = false;\n        } else {\n            lyricBeens = new ArrayList<>();\n            isExistsLyric = true;\n            //歌词文件存在\n            //解析歌词-一句一句\n            BufferedReader reader = null;\n            try {\n                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), getCharset(file)));\n\n                String line;\n                while ((line = reader.readLine()) != null) {\n                    line = analyzeLyrc(line);\n                }\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n\n            //排序\n            Collections.sort(lyricBeens, new MyComparator());\n\n\n            //计算每句的高亮时间\n            for (int i = 0; i < lyricBeens.size(); i++) {\n                LyricBean one = lyricBeens.get(i);\n                if (i + 1 < lyricBeens.size()) {\n                    LyricBean two = lyricBeens.get(i + 1);\n                    one.setSleepTime(two.getTimePoint() - one.getTimePoint());\n                }\n            }\n\n\n        }\n    }\n\n\n    /**\n     * 判断文件编码\n     * @param file 文件\n     * @return 编码：GBK,UTF-8,UTF-16LE\n     */\n    public String getCharset(File file) {\n        String charset = \"GBK\";\n        byte[] first3Bytes = new byte[3];\n        try {\n            boolean checked = false;\n            BufferedInputStream bis = new BufferedInputStream(\n                    new FileInputStream(file));\n            bis.mark(0);\n            int read = bis.read(first3Bytes, 0, 3);\n            if (read == -1)\n                return charset;\n            if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {\n                charset = \"UTF-16LE\";\n                checked = true;\n            } else if (first3Bytes[0] == (byte) 0xFE\n                    && first3Bytes[1] == (byte) 0xFF) {\n                charset = \"UTF-16BE\";\n                checked = true;\n            } else if (first3Bytes[0] == (byte) 0xEF\n                    && first3Bytes[1] == (byte) 0xBB\n                    && first3Bytes[2] == (byte) 0xBF) {\n                charset = \"UTF-8\";\n                checked = true;\n            }\n            bis.reset();\n            if (!checked) {\n                int loc = 0;\n                while ((read = bis.read()) != -1) {\n                    loc++;\n                    if (read >= 0xF0)\n                        break;\n                    if (0x80 <= read && read <= 0xBF)\n                        break;\n                    if (0xC0 <= read && read <= 0xDF) {\n                        read = bis.read();\n                        if (0x80 <= read && read <= 0xBF)\n                            continue;\n                        else\n                            break;\n                    } else if (0xE0 <= read && read <= 0xEF) {\n                        read = bis.read();\n                        if (0x80 <= read && read <= 0xBF) {\n                            read = bis.read();\n                            if (0x80 <= read && read <= 0xBF) {\n                                charset = \"UTF-8\";\n                                break;\n                            } else\n                                break;\n                        } else\n                            break;\n                    }\n                }\n            }\n            bis.close();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return charset;\n    }\n\n    class MyComparator implements Comparator<LyricBean> {\n\n\n        @Override\n        public int compare(LyricBean o1, LyricBean o2) {\n            if (o1.getTimePoint() < o2.getTimePoint()) {\n                return -1;\n            } else if (o1.getTimePoint() > o2.getTimePoint()) {\n                return 1;\n            } else {\n                return 0;\n            }\n\n        }\n    }\n\n    /**\n     * 解析每一句\n     *\n     * @param line [02:04.12][03:37.32][00:59.73]我在这里欢笑\n     * @return\n     */\n    private String analyzeLyrc(String line) {\n        int pos1 = line.indexOf(\"[\");//0\n        int pos2 = line.indexOf(\"]\");//9如果没有就返回-1\n        if (pos1 == 0 && pos2 != -1) {\n            //解析歌词\n            //装时间的集合\n            long[] timeLongs = new long[getCountTag(line)];\n            String timeStr = line.substring(pos1 + 1, pos2);//02:04.12\n            timeLongs[0] = stTimeToLong(timeStr);//02:04.12-->毫秒\n\n            if (timeLongs[0] == -1) {\n                return \"\";\n            }\n\n            int i = 1;\n            String content = line;//[02:04.12][03:37.32][00:59.73]我在这里欢笑\n            while (pos1 == 0 && pos2 != -1) {\n                content = content.substring(pos2 + 1);//[03:37.32][00:59.73]我在这里欢笑-->[00:59.73]我在这里欢笑-->我在这里欢笑\n                pos1 = content.indexOf(\"[\");//0-->-1\n                pos2 = content.indexOf(\"]\");//9如果没有就返回-1\n\n                if (pos2 != -1) {//还有下一句\n                    timeStr = content.substring(pos1 + 1, pos2);//03:37.32-->00:59.73\n                    timeLongs[i] = stTimeToLong(timeStr);//03:37.32-->毫秒\n\n                    if (timeLongs[i] == -1) {\n                        return \"\";\n                    }\n\n                    i++;\n\n                }\n\n\n\n            }\n\n            //把解析好的时间和歌词对应起来\n            LyricBean lyricBean = new LyricBean();\n            for (int j = 0; j < timeLongs.length; j++) {\n\n                if (timeLongs[j] != 0) {\n                    //设置歌词内容\n                    lyricBean.setContent(content);\n                    lyricBean.setTimePoint(timeLongs[j]);\n\n                    lyricBeens.add(lyricBean);\n                    lyricBean = new LyricBean();\n                }\n            }\n\n            return content;\n\n\n        }\n        return null;\n    }\n\n    /**\n     * 02:04.12-->毫秒\n     *\n     * @param timeStr\n     * @return\n     */\n    private long stTimeToLong(String timeStr) {\n\n        long time = -1;\n        try {\n            //1.根据\":\"切成02和04.12\n            String[] s1 = timeStr.split(\":\");\n            //2.根据“.”把04.12切成04和12\n            String[] s2 = s1[1].split(\"\\\\.\");\n            //3.转换成long类型的毫秒时间\n            //分\n            long min = Long.valueOf(s1[0]);//02\n\n            //秒\n            long second = Long.valueOf(s2[0]);//04\n\n            //毫秒\n            long mil = Long.valueOf(s2[1]);//12\n\n            time = min * 60 * 1000 + second * 1000 + mil * 10;\n\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        return time;\n    }\n\n    /**\n     * @param line [02:04.12][03:37.32][00:59.73]我在这里欢笑\n     * @return\n     */\n    private int getCountTag(String line) {\n        int count = 1;\n        String[] left = line.split(\"\\\\[\");\n        String[] right = line.split(\"\\\\]\");\n\n        if (left.length == 0 && right.length == 0) {\n            count = 1;\n        } else if (left.length > right.length) {\n            count = left.length;\n        } else {\n            count = right.length;\n        }\n        return count;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/utils/Utils.java",
    "content": "package com.atguigu.mobileplayer1020.utils;\n\nimport android.content.Context;\nimport android.net.TrafficStats;\n\nimport java.util.Formatter;\nimport java.util.Locale;\n\npublic class Utils {\n\n    private StringBuilder mFormatBuilder;\n    private Formatter mFormatter;\n\n    private long lastTotalRxBytes = 0;\n    private long lastTimeStamp = 0;\n\n    public Utils() {\n        // 转换成字符串的时间\n        mFormatBuilder = new StringBuilder();\n        mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());\n\n    }\n\n    /**\n     * 把毫秒转换成：1:20:30这里形式\n     *\n     * @param timeMs\n     * @return\n     */\n    public String stringForTime(int timeMs) {\n        int totalSeconds = timeMs / 1000;\n        int seconds = totalSeconds % 60;\n\n        int minutes = (totalSeconds / 60) % 60;\n\n        int hours = totalSeconds / 3600;\n\n        mFormatBuilder.setLength(0);\n        if (hours > 0) {\n            return mFormatter.format(\"%d:%02d:%02d\", hours, minutes, seconds)\n                    .toString();\n        } else {\n            return mFormatter.format(\"%02d:%02d\", minutes, seconds).toString();\n        }\n    }\n\n    /**\n     * 是否是网络资源\n     *\n     * @param url\n     * @return\n     */\n    public boolean isNetUrl(String url) {\n        boolean result = false;\n        if (url != null) {\n\n            if (url.toLowerCase().startsWith(\"http\")\n                    || url.toLowerCase().startsWith(\"rtsp\")\n                    || url.toLowerCase().startsWith(\"mms\")) {\n                result = true;\n            }\n        }\n        return result;\n    }\n\n\n    /**\n     * 显示网络速度\n     * @param context\n     * @return\n     */\n    public String showNetSpeed(Context context) {\n\n        long nowTotalRxBytes = TrafficStats.getUidRxBytes(context.getApplicationInfo().uid) == TrafficStats.UNSUPPORTED ? 0 : (TrafficStats.getTotalRxBytes() / 1024);//转为KB;\n        long nowTimeStamp = System.currentTimeMillis();\n        long speed = ((nowTotalRxBytes - lastTotalRxBytes) * 1000 / (nowTimeStamp - lastTimeStamp));//毫秒转换\n\n        lastTimeStamp = nowTimeStamp;\n        lastTotalRxBytes = nowTotalRxBytes;\n\n        String netSpeed = String.valueOf(speed) + \" kb/s\";\n        return netSpeed;\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/view/BaseVisualizerView.java",
    "content": "package com.atguigu.mobileplayer1020.view;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Paint.Cap;\nimport android.graphics.Paint.Join;\nimport android.media.audiofx.Visualizer;\nimport android.util.AttributeSet;\nimport android.view.View;\n\npublic class BaseVisualizerView extends View implements Visualizer.OnDataCaptureListener{\n\n    private static final int DN_W = 480;\n    private static final int DN_H = 160;\n    private static final int DN_SL =14;\n    private static final int DN_SW = 6;\n\n    private int hgap = 0;\n    private int vgap = 0;\n    private int levelStep = 0;\n    private float strokeWidth = 0;\n    private float strokeLength = 0;\n\n    /**\n     * It is the max level.\n     */\n    protected final static int MAX_LEVEL = 13;\n\n    /**\n     * It is the cylinder number.\n     */\n    protected final static int CYLINDER_NUM = 20;\n\n    /**\n     * It is the visualizer.\n     */\n    protected Visualizer mVisualizer = null;\n\n    /**\n     * It is the paint which is used to draw to visual effect. \n     */\n    protected Paint mPaint = null;\n\n    /**\n     * It is the buffer of fft.\n     */\n    protected byte[] mData = new byte[CYLINDER_NUM];\n\n    boolean mDataEn = true;\n    /**\n     * It constructs the base visualizer view.\n     * @param context It is the context of the view owner.\n     */\n    public BaseVisualizerView(Context context) {\n        super(context);\n\n        initView();\n    }\n\n    private void initView() {\n        mPaint = new Paint();\n        mPaint.setAntiAlias(true);\n        mPaint.setColor(Color.YELLOW);\n//        mPaint.setColor(0xFFd60d25);\n        mPaint.setStrokeJoin(Join.ROUND);\n        mPaint.setStrokeCap(Cap.ROUND);\n    }\n\n    public BaseVisualizerView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        initView();\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        super.onLayout(changed, left, top, right, bottom);\n\n        float w, h, xr, yr;\n        w = right - left;\n        h = bottom - top;\n        xr = w / (float)DN_W;\n        yr = h / (float)DN_H;\n\n        strokeWidth = DN_SW * yr;\n        strokeLength = DN_SL * xr;\n        hgap = (int)((w - strokeLength * CYLINDER_NUM) / (CYLINDER_NUM + 1) );\n        vgap = (int)(h / (MAX_LEVEL + 2));\n\n        mPaint.setStrokeWidth(strokeWidth);\n    }\n\n    protected void drawCylinder(Canvas canvas, float x, byte value) {\n        if (value < 0) value = 0;\n        for (int i = 0; i < value; i++) {\n            float y = getHeight() - i * vgap - vgap;\n            canvas.drawLine(x, y, x + strokeLength, y, mPaint);\n        }\n    }\n\n    @Override\n    public void onDraw(Canvas canvas) {\n        for (int i = 0; i < CYLINDER_NUM; i ++) {\n            drawCylinder(canvas, strokeWidth / 2 + hgap + i * (hgap + strokeLength), mData[i]);\n        }\n    }\n\n    /**\n     * It sets the visualizer of the view. DO set the viaulizer to null when exit the program.\n     * @parma visualizer It is the visualizer to set.\n     */\n    public void setVisualizer(Visualizer visualizer) {\n        if (visualizer != null) {\n            if (!visualizer.getEnabled()) {\n                visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[0]);\n            }\n            levelStep = 128 / MAX_LEVEL;\n            visualizer.setDataCaptureListener(this, Visualizer.getMaxCaptureRate() / 2, false, true);\n                \n        } else {\n            \n            if (mVisualizer != null) {\n                mVisualizer.setEnabled(false);\n                mVisualizer.release();\n            }\n        }\n        mVisualizer = visualizer;\n    }\n\n    @Override\n    public void onFftDataCapture(Visualizer visualizer, byte[] fft,\n            int samplingRate) {\n        byte[] model = new byte[fft.length / 2 + 1];\n        if (mDataEn) {\n\n            model[0] = (byte) Math.abs(fft[1]);  \n            int j = 1;  \n            for (int i = 2; i < fft.length;) {\n                model[j] = (byte) Math.hypot(fft[i], fft[i + 1]);  \n                i += 2;  \n                j++;  \n            }\n        } else {\n            for (int i = 0; i < CYLINDER_NUM; i ++) {\n                model[i] = 0;\n            }\n        }\n        for (int i = 0; i < CYLINDER_NUM; i ++) {\n            final byte a = (byte)(Math.abs(model[CYLINDER_NUM  - i]) / levelStep);\n\n            final byte b = mData[i];\n            if (a > b) {\n                mData[i] = a;\n            } else {\n                if (b > 0) {\n                    mData[i]--;\n                }\n            }\n        }\n        postInvalidate();\n    }\n\n    @Override\n    public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform,\n            int samplingRate) {\n        // Do nothing.\n    }\n\n    /**\n     * It enables or disables the data processs.\n     * @param en If this value is true it enables the data process..\n     */\n    public void enableDataProcess(boolean en) {\n        mDataEn = en;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/view/LyricShowView.java",
    "content": "package com.atguigu.mobileplayer1020.view;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\n\nimport com.atguigu.mobileplayer1020.bean.LyricBean;\nimport com.atguigu.mobileplayer1020.utils.DensityUtil;\n\nimport java.util.ArrayList;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/13 14:20\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：自定义显示歌词的控件\n */\npublic class LyricShowView extends TextView {\n    private final Context mContext;\n    private int width;\n    private int height;\n    private ArrayList<LyricBean> lyricBeen;\n    private Paint paint;\n    private Paint nopaint;\n    /**\n     * 歌词的索引\n     */\n    private int index = 0;\n    private float textHeight;\n    /**\n     * 歌曲当前播放的进程\n     */\n    private int currentPosition;\n    private float sleepTime;\n    private float timePoint;\n\n    public LyricShowView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        this.mContext = context;\n        textHeight = DensityUtil.dip2px(mContext,20);\n        initView();\n\n\n    }\n\n    private void initView() {\n        //创建画笔\n        paint = new Paint();\n        paint.setTextSize(DensityUtil.dip2px(mContext,16));\n        paint.setColor(Color.GREEN);\n        paint.setTextAlign(Paint.Align.CENTER);\n        paint.setAntiAlias(true);\n\n        nopaint = new Paint();\n        nopaint.setTextSize(DensityUtil.dip2px(mContext,16));\n        nopaint.setColor(Color.WHITE);\n        nopaint.setTextAlign(Paint.Align.CENTER);\n        nopaint.setAntiAlias(true);\n\n\n//        lyricBeen = new ArrayList<>();\n//        //添加歌词列表\n//        LyricBean lyricBean = new LyricBean();\n//        for (int i = 0; i < 1000; i++) {\n//            //歌词内容\n//            lyricBean.setContent(\"aaaaaaaaaaa\" + i);\n//            //休眠时间\n//            lyricBean.setSleepTime(i + 1000);\n//            //时间戳\n//            lyricBean.setTimePoint(i * 1000);\n//            //添加到集合中\n//            lyricBeen.add(lyricBean);\n//            //重新创建\n//            lyricBean = new LyricBean();\n//\n//        }\n    }\n\n\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        width = w;\n        height = h;\n    }\n\n\n    /**\n     * 绘制歌词\n     */\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n\n        if (lyricBeen != null && lyricBeen.size() > 0) {\n\n\n\n\n            if(index != lyricBeen.size()-1){\n                float plush = 0;\n\n                if(sleepTime==0){\n                    plush = 0;\n                }else{\n                    // 这一句花的时间： 这一句休眠时间  =  这一句要移动的距离：总距离(行高)\n                    //这一句要移动的距离 = （这一句花的时间/这一句休眠时间） * 总距离(行高)\n                    plush = ((currentPosition-timePoint)/sleepTime)*textHeight;\n                }\n\n\n                canvas.translate(0,-plush);\n\n            }\n\n            //绘制歌词\n            //当前句-绿色\n            String content = lyricBeen.get(index).getContent();\n            canvas.drawText(content, width / 2, height / 2, paint);\n            //绘制前面部分\n\n            float tempY = height / 2;\n            for (int i = index - 1; i >= 0; i--) {\n\n                tempY = tempY - textHeight;\n                if (tempY < 0) {\n                    break;\n                }\n\n                String preContent = lyricBeen.get(i).getContent();\n                canvas.drawText(preContent, width / 2, tempY, nopaint);\n            }\n\n\n            //绘制后面部分\n\n            tempY = height / 2;\n            for (int i = index + 1; i < lyricBeen.size(); i++) {\n                tempY = tempY + textHeight;\n                if (tempY > height) {\n                    break;\n                }\n                String nextContent = lyricBeen.get(i).getContent();\n                canvas.drawText(nextContent, width / 2, tempY, nopaint);\n            }\n\n        } else {\n\n\n            //没有歌词\n            canvas.drawText(\"没有找到歌词...\", width / 2, height / 2, paint);\n        }\n\n\n    }\n\n    /**\n     * 根据当前播放的位置，计算高亮哪句，并且与歌曲播放同步\n     *\n     * @param currentPosition\n     */\n    public void setNextShowLyric(int currentPosition) {\n        this.currentPosition = currentPosition;\n        if(lyricBeen ==null || lyricBeen.size()==0)\n            return;\n\n        for (int i=1;i<lyricBeen.size();i++){\n\n            if(currentPosition <lyricBeen.get(i).getTimePoint()){\n\n                int indexTemp = i - 1;\n\n                if(currentPosition >= lyricBeen.get(indexTemp).getTimePoint()){\n                    //就我们要找的高亮的哪句\n                    index = indexTemp;//某一句歌词的索引\n                    sleepTime = lyricBeen.get(indexTemp).getSleepTime();\n                    timePoint = lyricBeen.get(indexTemp).getTimePoint();\n\n                }\n\n            }else{\n                index = i;\n            }\n        }\n\n        invalidate();//强制绘制\n    }\n\n    /**\n     * 设置歌词列表\n     * @param lyricBeens\n     */\n    public void setLyrics(ArrayList<LyricBean> lyricBeens) {\n        this.lyricBeen = lyricBeens;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/view/TitleBarView.java",
    "content": "package com.atguigu.mobileplayer1020.view;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.atguigu.mobileplayer1020.R;\nimport com.atguigu.mobileplayer1020.activity.SearchActivity;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/6 15:49\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：自定义标题栏\n */\npublic class TitleBarView extends LinearLayout implements View.OnClickListener {\n\n    private final Context mContext;\n    private TextView tv_search;\n    private RelativeLayout rl_game;\n    private ImageView iv_record;\n\n    public TitleBarView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        this.mContext = context;\n    }\n\n    /**\n     * 当布局加载完成后回调该方法\n     * 得到孩子的实例\n     */\n    @Override\n    protected void onFinishInflate() {\n        super.onFinishInflate();\n        tv_search = (TextView) getChildAt(1);\n        rl_game = (RelativeLayout) getChildAt(2);\n        iv_record = (ImageView) getChildAt(3);\n\n        tv_search.setOnClickListener(this);\n        rl_game.setOnClickListener(this);\n        iv_record.setOnClickListener(this);\n\n    }\n\n    @Override\n    public void onClick(View v) {\n        switch (v.getId()) {\n            case R.id.tv_search:\n//                Toast.makeText(mContext, \"搜索\", Toast.LENGTH_SHORT).show();\n                Intent intent = new Intent(mContext, SearchActivity.class);\n                mContext.startActivity(intent);\n                break;\n            case R.id.rl_game:\n                Toast.makeText(mContext, \"游戏\", Toast.LENGTH_SHORT).show();\n                break;\n            case R.id.iv_record:\n                Toast.makeText(mContext, \"记录\", Toast.LENGTH_SHORT).show();\n                break;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/view/VideoView.java",
    "content": "package com.atguigu.mobileplayer1020.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.ViewGroup;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/9 14:41\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：自定义VideoView\n */\npublic class VideoView extends android.widget.VideoView {\n    public VideoView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    /**\n     * 测量\n     *\n     * @param widthMeasureSpec\n     * @param heightMeasureSpec\n     */\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);\n    }\n\n\n    /**\n     * 根据传入视频的大小设置，设置视频的画面大小\n     *\n     * @param screenWidth\n     * @param screeHeight\n     */\n    public void setViewSize(int screenWidth, int screeHeight) {\n        //视频画面的宽和高\n        ViewGroup.LayoutParams l = getLayoutParams();\n        l.width = screenWidth;\n        l.height = screeHeight;\n        setLayoutParams(l);\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/atguigu/mobileplayer1020/view/VitamioVideoView.java",
    "content": "package com.atguigu.mobileplayer1020.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.ViewGroup;\n\n/**\n * 作者：尚硅谷-杨光福 on 2017/1/9 14:41\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：自定义VideoView\n */\npublic class VitamioVideoView extends io.vov.vitamio.widget.VideoView {\n    public VitamioVideoView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    /**\n     * 测量\n     *\n     * @param widthMeasureSpec\n     * @param heightMeasureSpec\n     */\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);\n    }\n\n\n    /**\n     * 根据传入视频的大小设置，设置视频的画面大小\n     *\n     * @param screenWidth\n     * @param screeHeight\n     */\n    public void setViewSize(int screenWidth, int screeHeight) {\n        //视频画面的宽和高\n        ViewGroup.LayoutParams l = getLayoutParams();\n        l.width = screenWidth;\n        l.height = screeHeight;\n        setLayoutParams(l);\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/animation_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<animation-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/now_playing_matrix_01\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_02\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_03\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_04\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_05\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_06\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_07\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_08\" android:duration=\"150\"/>\n    <item android:drawable=\"@drawable/now_playing_matrix_09\" android:duration=\"150\"/>\n\n</animation-list>"
  },
  {
    "path": "app/src/main/res/drawable/audio_progress_horizontal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2008 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    \n    <item android:id=\"@android:id/background\" android:drawable=\"@drawable/seekbar_bg\">\n\n    </item>\n    \n    <item android:id=\"@android:id/secondaryProgress\" android:drawable=\"@drawable/seekbar_progress\">\n\n    </item>\n    \n    <item android:id=\"@android:id/progress\" android:drawable=\"@drawable/seekbar_progress\">\n\n    </item>\n    \n</layer-list>\n\n"
  },
  {
    "path": "app/src/main/res/drawable/btn_audio_next_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_now_playing_play_next\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_now_playing_play_next_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_audio_pause_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_now_playing_pause\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_now_playing_pause_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_audio_playmode_all_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_now_playing_play_all_repeat\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_now_playing_play_all_repeat_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_audio_playmode_normal_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_now_playing_normal_order\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_now_playing_normal_order_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_audio_playmode_single_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_now_playing_playmode_singlerepeat\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_now_playing_playmode_singlerepeat_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_audio_pre_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_now_playing_play_prevoius\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_now_playing_play_prevoius_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_audio_start_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_now_playing_real_play\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_now_playing_real_play_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_exit_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_exit\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_exit_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_next_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_next_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_next_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_pause_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_pause_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_pause_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_pre_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_pre_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_pre_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_screen_default_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_default_screen_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_default_screen_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_screen_full_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_full_screen_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_full_screen_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_start_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_play_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_play_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_swich_lyric_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_lyrics\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_lyrics_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_swiche_player_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_switch_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_switch_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/btn_voice_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/btn_voice_normal\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/btn_voice_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/iv_item_picture_popup_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/ic_item_right_more_normal\"></item>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_item_right_more_press\"></item>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/progress_horizontal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2008 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    \n    <item android:id=\"@android:id/background\" android:drawable=\"@drawable/progress_background\">\n\n    </item>\n    \n    <item android:id=\"@android:id/secondaryProgress\" android:drawable=\"@drawable/progress_secondaryprogress\">\n\n    </item>\n    \n    <item android:id=\"@android:id/progress\" android:drawable=\"@drawable/progress_normal\">\n\n    </item>\n    \n</layer-list>\n\n"
  },
  {
    "path": "app/src/main/res/drawable/rb_local_audio_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_checked=\"false\" android:drawable=\"@drawable/ic_tab_audio\"/>\n    <item android:state_checked=\"true\" android:drawable=\"@drawable/ic_tab_audio_press\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/rb_local_video_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_checked=\"false\" android:drawable=\"@drawable/ic_tab_video\"/>\n    <item android:state_checked=\"true\" android:drawable=\"@drawable/ic_tab_video_press\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/rb_net_audio_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_checked=\"false\" android:drawable=\"@drawable/ic_tab_netaudio\"/>\n    <item android:state_checked=\"true\" android:drawable=\"@drawable/ic_tab_netaudio_press\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/rb_net_video_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_checked=\"false\" android:drawable=\"@drawable/ic_tab_netvideo\"/>\n    <item android:state_checked=\"true\" android:drawable=\"@drawable/ic_tab_netvideo_press\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/rb_textcolor_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_checked=\"false\" android:color=\"#363636\"/>\n    <item android:state_checked=\"true\" android:color=\"#3097FD\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/shape_red.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\"oval\">\n\n    <solid android:color=\"#ff0000\"/>\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_sousuo_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"12dp\" />\n\n    <padding\n        android:bottom=\"5dp\"\n        android:left=\"8dp\"\n        android:right=\"8dp\"\n        android:top=\"5dp\" />\n\n    <solid android:color=\"#ff2679ca\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shape_sousuo_bg_pressed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"12dp\" />\n\n    <padding\n        android:bottom=\"5dp\"\n        android:left=\"8dp\"\n        android:right=\"8dp\"\n        android:top=\"5dp\" />\n\n    <solid android:color=\"#cc2679ca\" />\n\n</shape>"
  },
  {
    "path": "app/src/main/res/drawable/shenhe_cai_pic_night_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_enabled=\"true\" android:drawable=\"@drawable/shenhe_cai_pic_night\"></item>\n    <item android:state_enabled=\"false\" android:drawable=\"@drawable/shenhe_cai_pic_an\"></item>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/shenhe_ding_pic_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_enabled=\"false\" android:drawable=\"@drawable/shenhe_ding_pic_an\"></item>\n    <item android:state_enabled=\"true\" android:drawable=\"@drawable/shenhe_ding_pic\"></item>\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/text_color_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_enabled=\"false\" android:color=\"#ff0000\"></item>\n    <item android:state_enabled=\"true\" android:color=\"#44000000\"></item>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/tv_search_drawable.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/ic_sousuokuang\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/ic_sousuokuang_press\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/tv_search_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:drawable=\"@drawable/shape_sousuo_bg\"/>\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/shape_sousuo_bg_pressed\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/drawable/tv_search_textcolor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"false\" android:color=\"#99ffffff\"/>\n    <item android:state_pressed=\"true\" android:color=\"#ffffffff\"/>\n\n</selector>"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\"com.atguigu.mobileplayer1020.MainActivity\">\n\n    <!--标题栏-线性布局-->\n    <include layout=\"@layout/titlebar\" />\n\n\n    <!--中间内容-FrameLayout-->\n    <FrameLayout\n        android:id=\"@+id/fl_mainc_content\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" />\n\n    <!--RadioGroup嵌套RadioButton-->\n    <RadioGroup\n        android:id=\"@+id/rg_main\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#11000000\"\n        android:orientation=\"horizontal\"\n        android:padding=\"5dp\">\n\n        <RadioButton\n            android:id=\"@+id/rb_local_video\"\n            style=\"@style/bottom_button_style\"\n            android:drawableTop=\"@drawable/rb_local_video_selector\"\n            android:text=\"本地视频\" />\n\n        <RadioButton\n            android:id=\"@+id/rb_local_audio\"\n            style=\"@style/bottom_button_style\"\n            android:drawableTop=\"@drawable/rb_local_audio_selector\"\n            android:text=\"本地音频\" />\n\n\n        <RadioButton\n            android:id=\"@+id/rb_net_audio\"\n            style=\"@style/bottom_button_style\"\n            android:drawableTop=\"@drawable/rb_net_audio_selector\"\n            android:text=\"网络音乐\" />\n\n\n        <RadioButton\n            android:id=\"@+id/rb_net_video\"\n            style=\"@style/bottom_button_style\"\n            android:drawableTop=\"@drawable/rb_net_video_selector\"\n            android:text=\"网络视频\" />\n        <RadioButton\n            android:id=\"@+id/rb_recyclerview\"\n            style=\"@style/bottom_button_style\"\n            android:drawableTop=\"@drawable/rb_net_video_selector\"\n            android:text=\"recyclerview\" />\n    </RadioGroup>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_picasso_sample.xml",
    "content": "<uk.co.senab.photoview.PhotoView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/iv_photo\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"/>"
  },
  {
    "path": "app/src/main/res/layout/activity_search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_search\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\"com.atguigu.mobileplayer1020.activity.SearchActivity\">\n\n    <LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"50dp\"\n        android:background=\"#ff3097fd\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n\n        <EditText\n            android:id=\"@+id/et_search\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"8dp\"\n            android:layout_weight=\"1\"\n            android:background=\"@drawable/tv_search_selector\"\n            android:clickable=\"true\"\n            android:drawableLeft=\"@drawable/tv_search_drawable\"\n            android:drawablePadding=\"5dp\"\n            android:hint=\"全网搜索...\"\n            android:textColor=\"@drawable/tv_search_textcolor\" />\n\n\n        <ImageView\n            android:id=\"@+id/iv_voice\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"8dp\"\n            android:layout_marginRight=\"8dp\"\n            android:src=\"@drawable/voice_empty\" />\n\n        <TextView\n            android:id=\"@+id/tv_search\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"8dp\"\n            android:text=\"搜索\"\n            android:textColor=\"#000000\"\n            android:textSize=\"14sp\" />\n\n\n    </LinearLayout>\n\n    <ListView\n        android:id=\"@+id/listview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"></ListView>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_system_audio_player.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_system_audio_player\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/base_bg\"\n    tools:context=\"com.atguigu.mobileplayer1020.activity.SystemAudioPlayerActivity\">\n\n    <RelativeLayout\n        android:id=\"@+id/rl_top\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <com.atguigu.mobileplayer1020.view.BaseVisualizerView\n            android:layout_centerHorizontal=\"true\"\n            android:id=\"@+id/baseVisualizerView\"\n            android:layout_height=\"40dp\"\n            android:layout_width=\"80dp\"\n            />\n\n        <ImageView\n            android:visibility=\"gone\"\n            android:id=\"@+id/iv_icon\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerHorizontal=\"true\"\n            android:src=\"@drawable/now_playing_matrix_01\" />\n\n        <TextView\n            android:id=\"@+id/tv_artist\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"40dp\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"14sp\" />\n\n        <TextView\n            android:id=\"@+id/tv_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_marginTop=\"60dp\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"16sp\" />\n\n\n    </RelativeLayout>\n\n    <LinearLayout\n        android:id=\"@+id/ll_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_marginBottom=\"8dp\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/tv_time\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"8dp\"\n            android:gravity=\"right\"\n            android:text=\"00:00/00:00\"\n            android:textColor=\"@android:color/white\" />\n\n\n        <SeekBar\n            android:id=\"@+id/seekbar_audio\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"8dp\"\n            android:layout_marginRight=\"8dp\"\n            android:max=\"100\"\n            android:maxHeight=\"5dp\"\n            android:minHeight=\"5dp\"\n            android:progress=\"10\"\n            android:progressDrawable=\"@drawable/audio_progress_horizontal\"\n            android:thumb=\"@drawable/seek_thumb\" />\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n\n            <Button\n                android:id=\"@+id/btn_audio_playmode\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:background=\"@drawable/btn_audio_playmode_normal_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_audio_pre\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:background=\"@drawable/btn_audio_pre_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_audio_start_pause\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:background=\"@drawable/btn_audio_pause_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_audio_next\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:background=\"@drawable/btn_audio_next_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_swich_lyric\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:background=\"@drawable/btn_swich_lyric_selector\" />\n\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n    <com.atguigu.mobileplayer1020.view.LyricShowView\n        android:id=\"@+id/lyric_show_view\"\n        android:layout_height=\"match_parent\"\n        android:layout_above=\"@id/ll_bottom\"\n        android:layout_below=\"@id/rl_top\"\n        android:layout_width=\"match_parent\"\n        />\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_system_video_player.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_system_video_player\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#000000\"\n    android:gravity=\"center\"\n    tools:context=\"com.atguigu.mobileplayer1020.activity.SystemVideoPlayerActivity\">\n\n\n    <com.atguigu.mobileplayer1020.view.VideoView\n        android:layout_centerInParent=\"true\"\n        android:id=\"@+id/videoview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <include layout=\"@layout/mediacontroller\"/>\n    <include layout=\"@layout/loading\" android:id=\"@+id/ll_loading\"/>\n    <include layout=\"@layout/buffer\" android:id=\"@+id/ll_buffer\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_test_b.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_test_b\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#44000000\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.atguigu.mobileplayer1020.activity.TestB\">\n\n\n    <TextView\n        android:gravity=\"center\"\n        android:text=\"我是B页面\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_vitamio_video_player.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_system_video_player\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#000000\"\n    android:gravity=\"center\"\n    tools:context=\"com.atguigu.mobileplayer1020.activity.SystemVideoPlayerActivity\">\n\n\n    <com.atguigu.mobileplayer1020.view.VitamioVideoView\n        android:layout_centerInParent=\"true\"\n        android:id=\"@+id/videoview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <include layout=\"@layout/mediacontroller\"/>\n    <include layout=\"@layout/loading\" android:id=\"@+id/ll_loading\"/>\n    <include layout=\"@layout/buffer\" android:id=\"@+id/ll_buffer\"/>\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_welcome.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_welcome\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#ffffff\"\n    tools:context=\"com.atguigu.mobileplayer1020.WelcomeActivity\">\n\n    <ImageView\n        android:id=\"@+id/iv_icon\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:src=\"@drawable/login_icon\" />\n\n    <LinearLayout\n        android:layout_centerHorizontal=\"true\"\n        android:layout_below=\"@id/iv_icon\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <ProgressBar\n            android:layout_width=\"30dp\"\n            android:layout_height=\"30dp\" />\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"10dp\"\n            android:text=\"正在启动.....\"\n            android:textColor=\"@android:color/black\"\n            android:textSize=\"18sp\" />\n    </LinearLayout>\n\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/ad_middle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n\n    <TextView\n        android:id=\"@+id/tv_context\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:text=\"这哪里是在斗牛，分明就是玩命嘛！\"\n        android:textColor=\"@android:color/black\"\n        android:textSize=\"18sp\" />\n\n    <ImageView\n        android:id=\"@+id/iv_image_icon\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\"\n        android:layout_marginTop=\"5dp\" />\n\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center_vertical\">\n\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginLeft=\"10dp\"\n            android:text=\"推广\"\n            android:textSize=\"18sp\" />\n\n        <Button\n            android:id=\"@+id/btn_install\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_marginRight=\"10dp\"\n            android:text=\"安装\"\n            android:textSize=\"18sp\" />\n    </RelativeLayout>\n\n\n</LinearLayout>\n\n\n\n"
  },
  {
    "path": "app/src/main/res/layout/all_ad_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"10dp\"\n        android:background=\"#55888888\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"20dp\"\n        android:background=\"@drawable/bg_activities_item_end_transparent\"\n        android:orientation=\"vertical\"\n        android:paddingLeft=\"10dp\"\n        android:paddingRight=\"10dp\">\n\n\n        <include layout=\"@layout/ad_middle\" />\n\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/all_gif_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"10dp\"\n        android:background=\"#55888888\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"20dp\"\n        android:background=\"@drawable/bg_activities_item_end_transparent\"\n        android:orientation=\"vertical\"\n        android:paddingLeft=\"10dp\"\n        android:paddingRight=\"10dp\">\n\n        <include layout=\"@layout/common_user_info\" />\n\n        <include layout=\"@layout/gif_middle\" />\n\n        <include layout=\"@layout/common_bottom\" />\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/all_image_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"10dp\"\n        android:background=\"#55888888\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"20dp\"\n        android:background=\"@drawable/bg_activities_item_end_transparent\"\n        android:orientation=\"vertical\"\n        android:paddingLeft=\"10dp\"\n        android:paddingRight=\"10dp\">\n\n        <include layout=\"@layout/common_user_info\" />\n\n        <include layout=\"@layout/image_middle\" />\n\n        <include layout=\"@layout/common_bottom\" />\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/all_text_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"10dp\"\n        android:background=\"#55888888\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"20dp\"\n        android:background=\"@drawable/bg_activities_item_end_transparent\"\n        android:orientation=\"vertical\"\n        android:paddingLeft=\"10dp\"\n        android:paddingRight=\"10dp\">\n\n        <include layout=\"@layout/common_user_info\" />\n\n        <include layout=\"@layout/text_middle\" />\n\n        <include layout=\"@layout/common_bottom\" />\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/all_video_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"10dp\"\n        android:background=\"#55888888\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"20dp\"\n        android:background=\"@drawable/bg_activities_item_end_transparent\"\n        android:orientation=\"vertical\"\n        android:paddingLeft=\"10dp\"\n        android:paddingRight=\"10dp\">\n\n        <include layout=\"@layout/common_user_info\" />\n\n        <include layout=\"@layout/video_middle\" />\n\n        <include layout=\"@layout/common_bottom\" />\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/buffer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_centerInParent=\"true\"\n    android:gravity=\"center\"\n    android:visibility=\"gone\"\n    android:orientation=\"vertical\">\n\n\n    <ProgressBar\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <TextView\n        android:id=\"@+id/tv_buffer\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"缓冲中...10kb/s\"\n        android:textColor=\"@android:color/white\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/common_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"25dp\"\n        android:layout_marginTop=\"3dp\"\n        android:gravity=\"center_vertical\">\n\n        <ImageView\n            android:id=\"@+id/iv_video_kind\"\n            android:layout_width=\"22dp\"\n            android:layout_height=\"22dp\"\n            android:layout_marginRight=\"4dp\"\n            android:src=\"@drawable/label_icon\" />\n\n        <TextView\n            android:id=\"@+id/tv_video_kind_text\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center_vertical\"\n            android:text=\"分类，例如：搞笑\"\n            android:textColor=\"#77000000\"\n            android:textSize=\"12sp\" />\n    </LinearLayout>\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:background=\"#44000000\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"30dp\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n        <LinearLayout\n            android:id=\"@+id/ll_ding\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:clickable=\"true\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_ding\"\n                android:layout_width=\"20dp\"\n                android:layout_height=\"20dp\"\n                android:layout_marginRight=\"4dp\"\n                android:background=\"@drawable/shenhe_ding_pic_selector\" />\n\n            <TextView\n                android:id=\"@+id/tv_shenhe_ding_number\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"20dp\"\n                android:gravity=\"center\"\n                android:paddingTop=\"2dp\"\n                android:text=\"1\"\n                android:textColor=\"@drawable/text_color_selector\" />\n\n        </LinearLayout>\n\n        <View\n            android:layout_width=\"1dip\"\n            android:layout_height=\"20dp\"\n            android:background=\"#44000000\" />\n\n        <LinearLayout\n            android:id=\"@+id/ll_cai\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:clickable=\"true\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/iv_cai\"\n                android:layout_width=\"20dp\"\n                android:layout_height=\"20dp\"\n                android:layout_marginRight=\"4dp\"\n                android:background=\"@drawable/shenhe_cai_pic_night_selector\" />\n\n            <TextView\n                android:id=\"@+id/tv_shenhe_cai_number\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"20dp\"\n                android:gravity=\"center\"\n                android:paddingTop=\"2dp\"\n                android:text=\"2\"\n                android:textColor=\"@drawable/text_color_selector\" />\n\n        </LinearLayout>\n\n        <View\n            android:layout_width=\"1dip\"\n            android:layout_height=\"20dp\"\n            android:background=\"#44000000\" />\n\n        <LinearLayout\n            android:id=\"@+id/ll_share\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:clickable=\"true\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:layout_width=\"20dp\"\n                android:layout_height=\"20dp\"\n                android:layout_marginRight=\"4dp\"\n                android:src=\"@drawable/posts_detail_forward_night\" />\n\n            <TextView\n                android:id=\"@+id/tv_posts_number\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"20dp\"\n                android:gravity=\"center\"\n                android:paddingTop=\"2dp\"\n                android:text=\"3\"\n                android:textColor=\"#44000000\" />\n\n        </LinearLayout>\n\n        <TextView\n            android:layout_width=\"1dip\"\n            android:layout_height=\"20dp\"\n            android:background=\"#44000000\"\n            android:gravity=\"center_vertical\" />\n\n        <LinearLayout\n            android:id=\"@+id/ll_download\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:clickable=\"true\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"下载(原评论)\" />\n\n            <TextView\n                android:id=\"@+id/tv_download_number\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"20dp\"\n                android:gravity=\"center\"\n                android:paddingTop=\"2dp\"\n                android:text=\"下载\"\n                android:textColor=\"#44000000\" />\n\n        </LinearLayout>\n    </LinearLayout>\n</LinearLayout>\n\n"
  },
  {
    "path": "app/src/main/res/layout/common_user_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"40dp\">\n\n    <LinearLayout\n        android:id=\"@+id/ll_video_user_info\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:clickable=\"true\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n        <ImageView\n            android:id=\"@+id/iv_headpic\"\n            android:layout_width=\"35dp\"\n            android:layout_height=\"35dp\"\n            android:layout_marginRight=\"10dp\"\n            android:src=\"@drawable/user\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/tv_name\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"20dp\"\n                android:gravity=\"center_vertical\"\n                android:paddingTop=\"5dp\"\n                android:text=\"Zealer\"\n                android:textColor=\"#88000000\"\n                android:textSize=\"10dp\" />\n\n            <TextView\n                android:id=\"@+id/tv_time_refresh\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"20dp\"\n                android:gravity=\"center_vertical\"\n                android:paddingBottom=\"5dp\"\n                android:text=\"2016-03-01 08:19\"\n                android:textColor=\"#88000000\"\n                android:textSize=\"10dp\" />\n\n        </LinearLayout>\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/iv_right_more\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:background=\"@android:color/transparent\"\n        android:clickable=\"true\"\n        android:src=\"@drawable/iv_item_picture_popup_selector\" />\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_local_video.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:background=\"#ffffff\"\n    android:layout_height=\"match_parent\">\n\n\n    <ListView\n        android:id=\"@+id/listview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"></ListView>\n\n    <TextView\n        android:id=\"@+id/tv_no_media\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:text=\"没有发现视频...\"\n        android:textColor=\"#000000\"\n        android:textSize=\"18sp\" />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_net_audio.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ListView\n        android:id=\"@+id/listview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n\n    <ProgressBar\n        android:id=\"@+id/progressbar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n\n    <TextView\n        android:id=\"@+id/tv_nomedia\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:text=\"请求失败...\"\n        android:visibility=\"gone\" />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_net_video.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.cjj.MaterialRefreshLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:id=\"@+id/refresh\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:isLoadMore=\"true\"\n        app:overlay=\"true\"\n        app:progress_colors=\"@array/material_colors\"\n        app:progress_size_type=\"normal\"\n        app:wave_color=\"#90ffffff\"\n        app:wave_height_type=\"normal\"\n        app:wave_show=\"true\">\n\n\n        <ListView\n            android:id=\"@+id/listview\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n\n\n    </com.cjj.MaterialRefreshLayout>\n\n\n    <TextView\n        android:id=\"@+id/tv_no_media\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:text=\"没有发现视频...\" />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_recyclerview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <android.support.v7.widget.RecyclerView\n        android:id=\"@+id/recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n\n    <ProgressBar\n        android:id=\"@+id/progressbar\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\" />\n\n    <TextView\n        android:id=\"@+id/tv_nomedia\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:text=\"请求失败...\"\n        android:visibility=\"gone\" />\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/gif_middle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n\n    <TextView\n        android:id=\"@+id/tv_context\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:text=\"这哪里是在斗牛，分明就是玩命嘛！\"\n        android:textColor=\"@android:color/black\"\n        android:textSize=\"18sp\" />\n\n\n    <ImageView\n        android:id=\"@+id/iv_image_gif\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\"\n        android:layout_margin=\"10dp\"\n        android:layout_marginTop=\"5dp\">\n\n\n    </ImageView>\n\n\n</LinearLayout>\n\n\n\n"
  },
  {
    "path": "app/src/main/res/layout/image_middle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n\n    <TextView\n        android:id=\"@+id/tv_context\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:text=\"这哪里是在斗牛，分明就是玩命嘛！\"\n        android:textColor=\"@android:color/black\"\n        android:textSize=\"18sp\" />\n\n    <ImageView\n        android:scaleType=\"center\"\n        android:id=\"@+id/iv_image_icon\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\"\n        android:layout_marginTop=\"5dp\" />\n\n\n\n\n\n</LinearLayout>\n\n\n\n"
  },
  {
    "path": "app/src/main/res/layout/item_local_video.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"5dp\">\n\n\n    <ImageView\n        android:id=\"@+id/iv_icon\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:src=\"@drawable/video_default_icon\" />\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_toRightOf=\"@id/iv_icon\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/tv_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"视频的名称\"\n            android:textColor=\"@android:color/black\"\n            android:textSize=\"18sp\" />\n\n        <TextView\n\n            android:id=\"@+id/tv_duration\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"10:00\"\n            android:textColor=\"@android:color/darker_gray\"\n            android:textSize=\"15sp\" />\n\n    </LinearLayout>\n\n    <TextView\n\n        android:id=\"@+id/tv_size\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:text=\"10MB\"\n        android:textColor=\"@android:color/darker_gray\"\n        android:textSize=\"15sp\" />\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/item_net_video.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"5dp\">\n\n\n    <FrameLayout\n        android:id=\"@+id/fl\"\n        android:layout_width=\"120dp\"\n        android:layout_height=\"90dp\">\n\n        <ImageView\n            android:id=\"@+id/iv_icon\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_marginLeft=\"8dp\"\n            android:scaleType=\"fitXY\"\n            android:src=\"@drawable/video_default\" />\n\n        <ImageView\n            android:src=\"@drawable/center_collect_play\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"bottom|right\"\n            android:layout_marginBottom=\"8dp\"\n            android:layout_marginRight=\"8dp\" />\n    </FrameLayout>\n\n\n    <LinearLayout\n        android:layout_centerVertical=\"true\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_toRightOf=\"@id/fl\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/tv_name\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"视频的名称\"\n            android:textColor=\"@android:color/black\"\n            android:textSize=\"16sp\" />\n\n        <TextView\n\n            android:id=\"@+id/tv_duration\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"10:00\"\n            android:textColor=\"@android:color/darker_gray\"\n            android:textSize=\"14sp\" />\n\n    </LinearLayout>\n\n    <TextView\n\n        android:id=\"@+id/tv_size\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_marginBottom=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:text=\"10MB\"\n        android:textColor=\"@android:color/darker_gray\"\n        android:textSize=\"15sp\" />\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/bg_player_loading_background\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\">\n\n    <ProgressBar\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <TextView\n        android:id=\"@+id/tv_loading\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"正在加载...\"\n        android:textColor=\"@android:color/white\" />\n\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/mediacontroller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:id=\"@+id/ll_top\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/bg_player_status\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_name\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"8dp\"\n                android:layout_weight=\"1\"\n                android:text=\"视频的名称\"\n                android:textColor=\"@android:color/white\" />\n\n            <ImageView\n                android:id=\"@+id/iv_battery\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"8dp\"\n                android:src=\"@drawable/ic_battery_0\" />\n\n            <TextView\n                android:id=\"@+id/tv_systetime\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginLeft=\"8dp\"\n                android:layout_marginRight=\"8dp\"\n                android:text=\"10:10\"\n                android:textColor=\"@android:color/white\" />\n\n\n        </LinearLayout>\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/bg_player_top_control\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\">\n\n            <Button\n                android:id=\"@+id/btn_voice\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/btn_voice_selector\" />\n\n            <SeekBar\n\n                android:id=\"@+id/seekbar_voice\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:maxHeight=\"6dp\"\n                android:minHeight=\"6dp\"\n                android:progressDrawable=\"@drawable/progress_horizontal\"\n                android:thumb=\"@drawable/progress_thumb\" />\n\n            <Button\n                android:id=\"@+id/btn_swiche_player\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/btn_swiche_player_selector\" />\n\n        </LinearLayout>\n\n\n    </LinearLayout>\n\n\n    <LinearLayout\n        android:id=\"@+id/ll_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:orientation=\"vertical\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/bg_player_bottom_seekbar\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:layout_marginLeft=\"8dp\"\n                android:id=\"@+id/tv_currenttime\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"00:00\"\n                android:textColor=\"@android:color/white\" />\n\n            <SeekBar\n\n                android:layout_marginLeft=\"8dp\"\n                android:id=\"@+id/seekbar_video\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:maxHeight=\"6dp\"\n                android:minHeight=\"6dp\"\n                android:progressDrawable=\"@drawable/progress_horizontal\"\n                android:thumb=\"@drawable/progress_thumb\" />\n\n            <TextView\n                android:layout_marginLeft=\"8dp\"\n                android:layout_marginRight=\"8dp\"\n                android:id=\"@+id/tv_duration\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"00:00\"\n                android:textColor=\"@android:color/white\" />\n\n        </LinearLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:background=\"@drawable/bg_player_bottom_control\"\n            android:gravity=\"center\"\n            android:orientation=\"horizontal\">\n\n\n            <Button\n                android:id=\"@+id/btn_exit\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/btn_exit_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_pre\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/btn_pre_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_start_pause\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/btn_pause_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_next\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/btn_next_selector\" />\n\n            <Button\n                android:id=\"@+id/btn_swich_screen\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@drawable/btn_screen_full_selector\" />\n\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/layout/text_middle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n\n    <TextView\n        android:id=\"@+id/tv_context\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:text=\"这哪里是在斗牛，分明就是玩命嘛！\"\n        android:textColor=\"@android:color/black\"\n        android:textSize=\"18sp\" />\n\n\n\n\n</LinearLayout>\n\n\n\n"
  },
  {
    "path": "app/src/main/res/layout/titlebar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.atguigu.mobileplayer1020.view.TitleBarView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"50dp\"\n    android:background=\"#ff3097fd\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n\n    <ImageView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:src=\"@drawable/ic_topbanner_logo\" />\n\n    <TextView\n        android:id=\"@+id/tv_search\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_weight=\"1\"\n        android:background=\"@drawable/tv_search_selector\"\n        android:clickable=\"true\"\n        android:drawableLeft=\"@drawable/tv_search_drawable\"\n        android:drawablePadding=\"5dp\"\n        android:text=\"全网搜索...\"\n        android:textColor=\"@drawable/tv_search_textcolor\" />\n\n    <RelativeLayout\n        android:id=\"@+id/rl_game\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:gravity=\"center\">\n\n        <TextView\n            android:id=\"@+id/tv_game\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:drawableLeft=\"@drawable/ic_topbanner_game\" />\n\n        <ImageView\n            android:layout_width=\"6dp\"\n            android:layout_height=\"6dp\"\n            android:layout_alignRight=\"@id/tv_game\"\n            android:src=\"@drawable/shape_red\" />\n    </RelativeLayout>\n\n    <ImageView\n        android:id=\"@+id/iv_record\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:src=\"@drawable/ic_topbanner_record\" />\n\n\n</com.atguigu.mobileplayer1020.view.TitleBarView>"
  },
  {
    "path": "app/src/main/res/layout/video_middle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n\n    <TextView\n        android:id=\"@+id/tv_context\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center_vertical\"\n        android:text=\"这哪里是在斗牛，分明就是玩命嘛！\"\n        android:textColor=\"@android:color/black\"\n        android:textSize=\"18sp\" />\n\n    <RelativeLayout\n        android:id=\"@+id/rl_holder\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"230dp\"\n        android:layout_marginTop=\"5dp\">\n\n        <fm.jiecao.jcvideoplayer_lib.JCVideoPlayerStandard\n            android:id=\"@+id/jcv_videoplayer\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"220dp\"\n            android:orientation=\"vertical\" />\n\n        <TextView\n           android:layout_margin=\"5dp\"\n            android:id=\"@+id/tv_play_nums\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:background=\"#88000000\"\n            android:paddingLeft=\"5dp\"\n            android:paddingRight=\"5dp\"\n            android:text=\"1000次播放\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"14dp\" />\n\n        <TextView\n            android:layout_margin=\"5dp\"\n            android:padding=\"5dp\"\n            android:id=\"@+id/tv_video_duration\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_alignParentRight=\"true\"\n            android:background=\"#88000000\"\n            android:paddingLeft=\"5dp\"\n            android:paddingRight=\"5dp\"\n            android:text=\"12:23\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"14dp\" />\n\n\n    </RelativeLayout>\n\n\n    <ImageView\n        android:id=\"@+id/iv_commant\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"5dp\"\n        android:src=\"@drawable/top_comment\" />\n\n    <TextView\n        android:id=\"@+id/tv_commant_context\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"#11000000\"\n        android:gravity=\"center_vertical\"\n        android:paddingLeft=\"5dp\"\n        android:text=\"楼主，要不是我玩过98，我差点就信了\"\n        android:textSize=\"14sp\" />\n</LinearLayout>\n\n\n\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n\n    <color name=\"material_red\">#ffF44336</color>\n    <color name=\"material_green\">#ff4CAF50</color>\n    <color name=\"material_blue\">#ff03A9F4</color>\n    <color name=\"material_yellow\">#ffFFEB3B</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">321手机影音</string>\n    <string name=\"player_name\">321手机影音(系统推荐)</string>\n\n\n    <integer-array name=\"material_colors\">\n        <item>@color/material_red</item>\n        <item>@color/material_blue</item>\n        <item>@color/material_yellow</item>\n    </integer-array>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n\n    <style name=\"bottom_button_style\" >\n        <item name=\"android:layout_weight\">1</item>\n        <item name=\"android:layout_width\">wrap_content</item>\n        <item name=\"android:layout_height\">wrap_content</item>\n        <item name=\"android:button\">@null</item>\n        <item name=\"android:gravity\">center</item>\n        <item name=\"android:drawablePadding\">5dp</item>\n        <item name=\"android:textColor\">@drawable/rb_textcolor_selector</item>\n        <item name=\"android:textSize\">10sp</item>\n    </style>\n\n\n    <style name=\"noAnimation_Theme\" parent=\"android:Theme\">\n            <item name=\"android:windowAnimationStyle\">@style/noAnimation</item>\n            <item name=\"android:windowNoTitle\">true</item>\n            <item name=\"android:windowFullscreen\">true</item>\n            <item name=\"android:windowContentOverlay\">@null</item>\n    </style>\n\n    <style name=\"noAnimation\">\n            <item name=\"android:activityOpenEnterAnimation\">@null</item>\n            <item name=\"android:activityOpenExitAnimation\">@null</item>\n            <item name=\"android:activityCloseEnterAnimation\">@null</item>\n            <item name=\"android:activityCloseExitAnimation\">@null</item>\n            <item name=\"android:taskOpenEnterAnimation\">@null</item>\n            <item name=\"android:taskOpenExitAnimation\">@null</item>\n            <item name=\"android:taskCloseEnterAnimation\">@null</item>\n            <item name=\"android:taskCloseExitAnimation\">@null</item>\n            <item name=\"android:taskToFrontEnterAnimation\">@null</item>\n            <item name=\"android:taskToFrontExitAnimation\">@null</item>\n            <item name=\"android:taskToBackEnterAnimation\">@null</item>\n            <item name=\"android:taskToBackExitAnimation\">@null</item>\n\n    </style>\n\n\n\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/atguigu/mobileplayer1020/ExampleUnitTest.java",
    "content": "package com.atguigu.mobileplayer1020;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "binderService/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.3\"\n\n    defaultConfig {\n        applicationId \"com.yanguangfu.binder\"\n        minSdkVersion 14\n        targetSdkVersion 23\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n        }\n    }\n}\n"
  },
  {
    "path": "binderService/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.yanguangfu.binder\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\" >\n\n    <uses-sdk android:minSdkVersion=\"8\" android:targetSdkVersion=\"16\" />\n\n    <application\n        android:icon=\"@drawable/icon\"\n        android:label=\"@string/app_name\" >\n        <activity android:name=\"com.yanguangfu.binder.MainActivity\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <service android:name=\"com.yanguangfu.binder.MyService\" >\n           <!-- <intent-filter>\n                <action android:name=\"com.yanguangfu.binder.action.AIDLService\" />\n            </intent-filter>-->\n        </service>\n    </application>\n\n</manifest>"
  },
  {
    "path": "binderService/src/main/aidl/com/yanguangfu/binder/aidl/AIDLActivity.aidl",
    "content": "package com.yanguangfu.binder.aidl;  \nimport com.yanguangfu.binder.aidl.Rect1;\n\ninterface AIDLActivity {   \n    void performAction(in Rect1 rect);   \n}  \n"
  },
  {
    "path": "binderService/src/main/aidl/com/yanguangfu/binder/aidl/AIDLService.aidl",
    "content": "package com.yanguangfu.binder.aidl;  \nimport com.yanguangfu.binder.aidl.AIDLActivity;\ninterface AIDLService {   \n    void registerTestCall(AIDLActivity cb);   \n    void invokCallBack();\n    \n    String getName();\n    int getAge();\n}  \n"
  },
  {
    "path": "binderService/src/main/aidl/com/yanguangfu/binder/aidl/Rect1.aidl",
    "content": "package com.yanguangfu.binder.aidl; \nparcelable Rect1;"
  },
  {
    "path": "binderService/src/main/java/com/yanguangfu/binder/MainActivity.java",
    "content": "package com.yanguangfu.binder;\n\nimport java.text.MessageFormat;\n\nimport android.app.Activity;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.Bundle;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.Toast;\n\nimport com.yanguangfu.binder.aidl.AIDLActivity;\nimport com.yanguangfu.binder.aidl.AIDLService;\nimport com.yanguangfu.binder.aidl.Rect1;\n\n/**\n *\n * @author 杨光福\n *\n */\npublic class MainActivity extends Activity implements OnClickListener {\n\tprivate Button btn_bindService;\n\tprivate Button btn_unbindService;\n\tprivate Button btnCallBack;\n\tprivate Button btn_get_data;\n\n\n\n\t@Override\n\tpublic void onCreate(Bundle icicle) {\n\t\tLog.e(\"yangguangfu\", \"MainActivity.onCreate\");\n\t\tsuper.onCreate(icicle);\n\t\tsetContentView(R.layout.activity_main);\n\t\t//初始化布局文件\n\t\tinitView();\n\t\t//设置点击事件\n\t\tsetClickListener();\n\t}\n\n\t/**\n\t * 设置点击事件\n\t */\n\tprivate void setClickListener() {\n\t\tbtn_bindService.setOnClickListener(this);\n\t\tbtn_unbindService.setOnClickListener(this);\n\t\tbtnCallBack.setOnClickListener(this);\n\t\tbtn_get_data.setOnClickListener(this);\n\t}\n\n\t/**\n\t * 初始化布局文件\n\t */\n\tprivate void initView() {\n\t\tbtn_bindService = (Button) findViewById(R.id.btn_bindService);\n\t\tbtn_unbindService = (Button) findViewById(R.id.btn_unbindService);\n\t\tbtnCallBack = (Button) findViewById(R.id.btn_call_back);\n\t\tbtn_get_data = (Button) findViewById(R.id.btn_get_data);\n\t}\n\n\t@Override\n\tpublic void onClick(View v) {\n\t\tswitch (v.getId()) {\n\t\t\tcase R.id.btn_bindService:\n\t\t\t\tLog.e(\"yangguangfu\", \"MainActivity.点击了btn_bindService\");\n\n\t\t\t\tIntent intent = new Intent(this,MyService.class);\n\t\t\t\t//绑定服务\n\t\t\t\tbindService(intent, mConnection, Context.BIND_AUTO_CREATE);\n\t\t\t\t//启动服务\n\t\t\t    startService(intent);\n\t\t\t\tbreak;\n\t\t\tcase R.id.btn_unbindService:\n\t\t\t\tLog.e(\"yangguangfu\", \"MainActivity.点击了btn_unbindService\");\n\t\t\t\tunbindService(mConnection);\n\t\t\t\t// stopService(intent);\n\t\t\t\tbreak;\n\t\t\tcase R.id.btn_call_back:\n\n\t\t\t\tLog.e(\"yangguangfu\", \"MainActivity.点击了btn_unbindService\");\n\t\t\t\ttry {\n\t\t\t\t\tmService.invokCallBack();\n\t\t\t\t} catch (RemoteException e) {\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\tcase R.id.btn_get_data:\n\n\t\t\t\tLog.e(\"yangguangfu\", \"MainActivity.点击了btn_get_data\");\n\t\t\t\tif(mService==null){\n\t\t\t\t\tToast.makeText(getApplicationContext(), \"服务还没有绑定\", 0).show();\n\t\t\t\t\treturn ;\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tString dataFromService = mService.getName()+\"---\"+mService.getAge();\n\t\t\t\t\tLog.e(\"yangguangfu\", \"MainActivity.dataFromService==\"+dataFromService);\n\t\t\t\t\tToast.makeText(getApplicationContext(), dataFromService, 0).show();\n\t\t\t\t} catch (RemoteException e) {\n\t\t\t\t\t// TODO Auto-generated catch block\n\t\t\t\t\te.printStackTrace();\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t}\n\n\t}\n\n\n\n\tprivate AIDLActivity mCallback = new AIDLActivity.Stub() {\n\n\t\t@Override\n\t\tpublic void performAction(Rect1 rect) throws RemoteException {\n\t\t\tLog.e(\"yangguangfu\", \"MainActivity.performAction\");\n\t\t\tLog.e(\"yangguangfu\", MessageFormat.format(\n\t\t\t\t\t\"MainActivity.rect[bottom={0},top={1},left={2},right={3}]\", rect.bottom,\n\t\t\t\t\trect.top, rect.left, rect.right));\n\t\t\tToast.makeText(MainActivity.this,\n\t\t\t\t\t\"这个土司是由Service回调Activity弹出来的\", 1).show();\n\n\t\t}\n\t};\n\n\tprivate AIDLService mService;\n\tprivate ServiceConnection mConnection = new ServiceConnection() {\n\t\tpublic void onServiceConnected(ComponentName className, IBinder service) {\n\t\t\tLog.e(\"yangguangfu\", \"MainActivity.onServiceConnected\");\n\t\t\tmService = AIDLService.Stub.asInterface(service);\n\t\t\ttry {\n\t\t\t\tmService.registerTestCall(mCallback);\n\t\t\t} catch (RemoteException e) {\n\n\t\t\t}\n\t\t}\n\n\t\tpublic void onServiceDisconnected(ComponentName className) {\n\t\t\tLog.e(\"yangguangfu\", \"MainActivity.onServiceDisconnected\");\n\t\t\tmService = null;\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "binderService/src/main/java/com/yanguangfu/binder/MyService.java",
    "content": "package com.yanguangfu.binder;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinder;\nimport android.os.RemoteException;\nimport android.util.Log;\n\nimport com.yanguangfu.binder.aidl.AIDLActivity;\nimport com.yanguangfu.binder.aidl.AIDLService;\nimport com.yanguangfu.binder.aidl.Rect1;\n\n/**\n *\n * @author 杨光福\n *\n */\npublic class MyService extends Service {\n\n\tprivate AIDLActivity callback;\n\n\n\n\t@Override\n\tpublic void onCreate() {\n\t\tLog.e(\"yangguangfu\", \"MyService.onCreate\");\n\t}\n\n\n\t@Override\n\tpublic void onStart(Intent intent, int startId) {\n\t\tLog.e(\"yangguangfu\", \"MyService.onStart startId=\"+startId);\n\t}\n\n\n\t@Override\n\tpublic IBinder onBind(Intent t) {\n\t\tLog.e(\"yangguangfu\", \"MyService.onBind\");\n\t\treturn mBinder;\n\t}\n\n\n\t@Override\n\tpublic void onDestroy() {\n\t\tLog.e(\"yangguangfu\", \"MyService.onDestroy\");\n\t\tsuper.onDestroy();\n\t}\n\n\n\t@Override\n\tpublic boolean onUnbind(Intent intent) {\n\t\tLog.e(\"yangguangfu\", \"MyService.onUnbind\");\n\t\treturn super.onUnbind(intent);\n\t}\n\n\n\tpublic void onRebind(Intent intent) {\n\t\tLog.e(\"yangguangfu\", \"MyService.onRebind\");\n\t\tsuper.onRebind(intent);\n\t}\n\n\tprivate String getNames(){\n\t\tLog.e(\"yangguangfu\", \"MyService.getName\");\n\t\treturn \"name from service\";\n\t}\n\n\tprivate int getAges(){\n\t\tLog.e(\"yangguangfu\", \"MyService.getAge\");\n\t\treturn 24;\n\t}\n\n\tprivate final AIDLService.Stub mBinder = new AIDLService.Stub() {\n\n\t\t@Override\n\t\tpublic void invokCallBack() throws RemoteException {\n\t\t\tLog.e(\"yangguangfu\", \"MyService.AIDLService.invokCallBack\");\n\t\t\tRect1 rect = new Rect1();\n\t\t\trect.bottom=-1;\n\t\t\trect.left=-1;\n\t\t\trect.right=1;\n\t\t\trect.top=1;\n\t\t\tcallback.performAction(rect);\n\t\t}\n\n\n\t\t@Override\n\t\tpublic void registerTestCall(AIDLActivity cb) throws RemoteException {\n\t\t\tLog.e(\"yangguangfu\", \"MyService.AIDLService.registerTestCall\");\n\t\t\tcallback = cb;\n\t\t}\n\n\n\t\t@Override\n\t\tpublic String getName() throws RemoteException {\n\t\t\tLog.e(\"yangguangfu\", \"MyService.AIDLService.getName\");\n\t\t\treturn getNames();\n\t\t}\n\n\n\t\t@Override\n\t\tpublic int getAge() throws RemoteException {\n\t\t\tLog.e(\"yangguangfu\", \"MyService.AIDLService.getAge\");\n\t\t\treturn getAges();\n\t\t}\n\t};\n}\n"
  },
  {
    "path": "binderService/src/main/java/com/yanguangfu/binder/aidl/Rect1.java",
    "content": "package com.yanguangfu.binder.aidl;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\npublic class Rect1 implements Parcelable {\n\tpublic int left;\n\tpublic int top;\n\tpublic int right;\n\tpublic int bottom;\n\tpublic static final Parcelable.Creator<Rect1> CREATOR = new Parcelable.Creator<Rect1>() {\n\t\tpublic Rect1 createFromParcel(Parcel in) {\n\t\t\treturn new Rect1(in);\n\t\t}\n\n\t\tpublic Rect1[] newArray(int size) {\n\t\t\treturn new Rect1[size];\n\t\t}\n\t};\n\n\tpublic Rect1() {\n\t}\n\n\tprivate Rect1(Parcel in) {\n\t\treadFromParcel(in);\n\t}\n\n\tpublic void readFromParcel(Parcel in) {\n\t\tleft = in.readInt();\n\t\ttop = in.readInt();\n\t\tright = in.readInt();\n\t\tbottom = in.readInt();\n\t}\n\n\t@Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel out, int flags) {\n\t\tout.writeInt(left);\n\t\tout.writeInt(top);\n\t\tout.writeInt(right);\n\t\tout.writeInt(bottom);\n\t}\n\n}\n"
  },
  {
    "path": "binderService/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\" >\n\n    <Button\n        android:id=\"@+id/btn_bindService\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"绑定方式开启服务\" >\n    </Button>\n\n    <Button\n        android:id=\"@+id/btn_unbindService\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"取消绑定服务\" >\n    </Button>\n\n    <Button\n        android:id=\"@+id/btn_call_back\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"让Service回调Activity方法\" >\n    </Button>\n    \n    <Button\n        android:id=\"@+id/btn_get_data\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"得到服务中的数据\" >\n    </Button>\n\n</LinearLayout>"
  },
  {
    "path": "binderService/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"hello\">Hello World!</string>\n    <string name=\"app_name\">BinderService</string>\n</resources>\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.2.0'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Dec 28 10:00:20 PST 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.14.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "jcvideoplayer-lib/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "jcvideoplayer-lib/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion '24.0.3'\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 24\n        versionCode 47\n        versionName \"4.8.3\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    testCompile 'junit:junit:4.12'\n    compile 'com.android.support:appcompat-v7:24.2.1'\n\n    //required, enough for most devices.\n    compile 'tv.danmaku.ijk.media:ijkplayer-java:0.7.4'\n    compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.7.4'\n    //Other ABIs: optional\n//    compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.7.4'\n    compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.7.4'\n    compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.7.4'\n    compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.7.4'\n    //ExoPlayer as IMediaPlayer: optional, experimental\n    //compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.7.4'\n}\n\n//apply from: '../gradle/maven_push.gradle'\n"
  },
  {
    "path": "jcvideoplayer-lib/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/Nathen/WorkEnv/android-sdk-macosx/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/androidTest/java/fm/jiecao/jcvideoplayer_lib/ApplicationTest.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"fm.jiecao.jcvideoplayer_lib\">\n\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n    <uses-sdk tools:overrideLibrary=\"tv.danmaku.ijk.media.player_arm64,tv.danmaku.ijk.media.player_x86_64\" />\n    <application\n        android:allowBackup=\"true\"\n        android:supportsRtl=\"true\"></application>\n\n</manifest>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCMediaManager.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.graphics.Point;\nimport android.media.AudioManager;\nimport android.os.Handler;\nimport android.os.HandlerThread;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.Surface;\n\nimport java.util.Map;\n\nimport tv.danmaku.ijk.media.player.IMediaPlayer;\nimport tv.danmaku.ijk.media.player.IjkMediaPlayer;\n\n\n/**\n * <p>统一管理MediaPlayer的地方,只有一个mediaPlayer实例，那么不会有多个视频同时播放，也节省资源。</p>\n * <p>Unified management MediaPlayer place, there is only one MediaPlayer instance, then there will be no more video broadcast at the same time, also save resources.</p>\n * Created by Nathen\n * On 2015/11/30 15:39\n */\npublic class JCMediaManager implements IMediaPlayer.OnPreparedListener, IMediaPlayer.OnCompletionListener,\n        IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnSeekCompleteListener, IMediaPlayer.OnErrorListener,\n        IMediaPlayer.OnVideoSizeChangedListener, IMediaPlayer.OnInfoListener {\n    public static String TAG = \"JieCaoVideoPlayer\";\n\n    private static JCMediaManager JCMediaManager;\n    public IjkMediaPlayer mediaPlayer;\n    public static JCResizeTextureView textureView;\n\n    public int currentVideoWidth = 0;\n    public int currentVideoHeight = 0;\n    public int lastState;\n    public int bufferPercent;\n    public int backUpBufferState = -1;\n    public int videoRotation;\n\n    public static final int HANDLER_PREPARE = 0;\n    public static final int HANDLER_SETDISPLAY = 1;\n    public static final int HANDLER_RELEASE = 2;\n    HandlerThread mMediaHandlerThread;\n    MediaHandler mMediaHandler;\n    Handler mainThreadHandler;\n\n    public static JCMediaManager instance() {\n        if (JCMediaManager == null) {\n            JCMediaManager = new JCMediaManager();\n        }\n        return JCMediaManager;\n    }\n\n    public JCMediaManager() {\n        mediaPlayer = new IjkMediaPlayer();\n        mMediaHandlerThread = new HandlerThread(TAG);\n        mMediaHandlerThread.start();\n        mMediaHandler = new MediaHandler((mMediaHandlerThread.getLooper()));\n        mainThreadHandler = new Handler();\n    }\n\n    public Point getVideoSize() {\n        if (currentVideoWidth != 0 && currentVideoHeight != 0) {\n            return new Point(currentVideoWidth, currentVideoHeight);\n        } else {\n            return null;\n        }\n    }\n\n    public class MediaHandler extends Handler {\n        public MediaHandler(Looper looper) {\n            super(looper);\n        }\n\n        @Override\n        public void handleMessage(Message msg) {\n            super.handleMessage(msg);\n            switch (msg.what) {\n                case HANDLER_PREPARE:\n                    try {\n                        currentVideoWidth = 0;\n                        currentVideoHeight = 0;\n                        mediaPlayer.release();\n                        mediaPlayer = new IjkMediaPlayer();\n                        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);\n                        mediaPlayer.setDataSource(((FuckBean) msg.obj).url, ((FuckBean) msg.obj).mapHeadData);\n                        mediaPlayer.setLooping(((FuckBean) msg.obj).looping);\n                        mediaPlayer.setOnPreparedListener(JCMediaManager.this);\n                        mediaPlayer.setOnCompletionListener(JCMediaManager.this);\n                        mediaPlayer.setOnBufferingUpdateListener(JCMediaManager.this);\n                        mediaPlayer.setScreenOnWhilePlaying(true);\n                        mediaPlayer.setOnSeekCompleteListener(JCMediaManager.this);\n                        mediaPlayer.setOnErrorListener(JCMediaManager.this);\n                        mediaPlayer.setOnInfoListener(JCMediaManager.this);\n                        mediaPlayer.setOnVideoSizeChangedListener(JCMediaManager.this);\n                        mediaPlayer.prepareAsync();\n                        mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, \"reconnect\", 1);\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                    break;\n                case HANDLER_SETDISPLAY:\n                    if (msg.obj == null) {\n                        instance().mediaPlayer.setSurface(null);\n                    } else {\n                        Surface holder = (Surface) msg.obj;\n                        if (holder.isValid()) {\n                            Log.i(TAG, \"set surface\");\n                            instance().mediaPlayer.setSurface(holder);\n                            mainThreadHandler.post(new Runnable() {\n                                @Override\n                                public void run() {\n                                    textureView.requestLayout();\n                                }\n                            });\n                        }\n                    }\n                    break;\n                case HANDLER_RELEASE:\n                    mediaPlayer.reset();\n                    mediaPlayer.release();\n                    break;\n            }\n        }\n    }\n\n\n    public void prepare(final String url, final Map<String, String> mapHeadData, boolean loop) {\n        if (TextUtils.isEmpty(url)) return;\n        Message msg = new Message();\n        msg.what = HANDLER_PREPARE;\n        FuckBean fb = new FuckBean(url, mapHeadData, loop);\n        msg.obj = fb;\n        mMediaHandler.sendMessage(msg);\n    }\n\n    public void releaseMediaPlayer() {\n        Message msg = new Message();\n        msg.what = HANDLER_RELEASE;\n        mMediaHandler.sendMessage(msg);\n    }\n\n    public void setDisplay(Surface holder) {\n        Message msg = new Message();\n        msg.what = HANDLER_SETDISPLAY;\n        msg.obj = holder;\n        mMediaHandler.sendMessage(msg);\n    }\n\n    @Override\n    public void onPrepared(IMediaPlayer mp) {\n        mainThreadHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayerManager.getFirst().onPrepared();\n                }\n            }\n        });\n    }\n\n    @Override\n    public void onCompletion(IMediaPlayer mp) {\n        mainThreadHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayerManager.getFirst().onAutoCompletion();\n                }\n            }\n        });\n    }\n\n    @Override\n    public void onBufferingUpdate(IMediaPlayer mp, final int percent) {\n        mainThreadHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayerManager.getFirst().onBufferingUpdate(percent);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void onSeekComplete(IMediaPlayer mp) {\n        mainThreadHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayerManager.getFirst().onSeekComplete();\n                }\n            }\n        });\n    }\n\n    @Override\n    public boolean onError(IMediaPlayer mp, final int what, final int extra) {\n        mainThreadHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayerManager.getFirst().onError(what, extra);\n                }\n            }\n        });\n        return true;\n    }\n\n    @Override\n    public boolean onInfo(IMediaPlayer mp, final int what, final int extra) {\n        mainThreadHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayerManager.getFirst().onInfo(what, extra);\n                }\n            }\n        });\n        return false;\n    }\n\n    @Override\n    public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) {\n        currentVideoWidth = mp.getVideoWidth();\n        currentVideoHeight = mp.getVideoHeight();\n        mainThreadHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                if (JCVideoPlayerManager.getFirst() != null) {\n                    JCVideoPlayerManager.getFirst().onVideoSizeChanged();\n                }\n            }\n        });\n    }\n\n\n    private class FuckBean {\n        String url;\n        Map<String, String> mapHeadData;\n        boolean looping;\n\n        FuckBean(String url, Map<String, String> mapHeadData, boolean loop) {\n            this.url = url;\n            this.mapHeadData = mapHeadData;\n            this.looping = loop;\n        }\n    }\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCMediaPlayerListener.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\n/**\n * Created by Nathen on 16/7/26.\n */\npublic interface JCMediaPlayerListener {\n    void onPrepared();\n\n    void onCompletion();\n\n    void onAutoCompletion();\n\n    void onBufferingUpdate(int percent);\n\n    void onSeekComplete();\n\n    void onError(int what, int extra);\n\n    void onInfo(int what, int extra);\n\n    void onVideoSizeChanged();\n\n    void goBackThisListener();\n\n    boolean backToOtherListener();\n\n    void onScrollChange();\n\n    int getScreenType();\n\n    String getUrl();\n\n    int getState();\n\n    void autoFullscreen(float x);\n\n    void autoQuitFullscreen();\n\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCResizeImageView.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.content.Context;\nimport android.graphics.Point;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.widget.ImageView;\n\n/**\n * <p>参照Android系统的VideoView的onMeasure方法\n * <br>注意!relativelayout中无法全屏，要嵌套一个linearlayout</p>\n * <p>Referring Android system Video View of onMeasure method\n * <br>NOTE! Can not fullscreen relativelayout, to nest a linearlayout</p>\n * Created by Nathen\n * On 2016/06/02 00:01\n */\npublic class JCResizeImageView extends ImageView {\n    protected static final String TAG = \"JCResizeImageView\";\n    protected static final boolean DEBUG = false;\n\n    // x as width, y as height\n    protected Point mVideoSize;\n\n    public JCResizeImageView(Context context) {\n        super(context);\n        init();\n    }\n\n    public JCResizeImageView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    protected void init() {\n        mVideoSize = new Point(0, 0);\n    }\n\n    public void setVideoSize(Point videoSize) {\n        if (videoSize != null && !mVideoSize.equals(videoSize)) {\n            this.mVideoSize = videoSize;\n            requestLayout();\n        }\n    }\n\n    @Override\n    public void setRotation(float rotation) {\n        if (rotation != getRotation()) {\n            super.setRotation(rotation);\n            requestLayout();\n        }\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int viewRotation = (int) getRotation();\n        // 如果判断成立，则说明显示的ImageView和本身的位置是有90度的旋转的，所以需要交换宽高参数。\n        if (viewRotation == 90 || viewRotation == 270) {\n            int tempMeasureSpec = widthMeasureSpec;\n            widthMeasureSpec = heightMeasureSpec;\n            heightMeasureSpec = tempMeasureSpec;\n        }\n\n        if (DEBUG) {\n            Log.i(TAG, \"onMeasure \" + \" [\" + this.hashCode() + \"] \");\n            Log.i(TAG, \"viewRotation = \" + viewRotation);\n        }\n\n        int videoWidth = mVideoSize.x;\n        int videoHeight = mVideoSize.y;\n\n        if (DEBUG) {\n            Log.i(TAG, \"videoWidth = \" + videoWidth + \", \" + \"videoHeight = \" + videoHeight);\n            if (videoWidth > 0 && videoHeight > 0) {\n                Log.i(TAG, \"videoWidth / videoHeight = \" + videoWidth / videoHeight);\n            }\n        }\n\n        int width = getDefaultSize(videoWidth, widthMeasureSpec);\n        int height = getDefaultSize(videoHeight, heightMeasureSpec);\n        if (videoWidth > 0 && videoHeight > 0) {\n\n            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);\n            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);\n            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);\n            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);\n\n            if (DEBUG) {\n                Log.i(TAG, \"widthMeasureSpec  [\" + MeasureSpec.toString(widthMeasureSpec) + \"]\");\n                Log.i(TAG, \"heightMeasureSpec [\" + MeasureSpec.toString(heightMeasureSpec) + \"]\");\n            }\n\n            if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {\n                // the size is fixed\n                width = widthSpecSize;\n                height = heightSpecSize;\n\n                // for compatibility, we adjust size based on aspect ratio\n                if (videoWidth * height < width * videoHeight) {\n                    width = height * videoWidth / videoHeight;\n                } else if (videoWidth * height > width * videoHeight) {\n                    height = width * videoHeight / videoWidth;\n                }\n            } else if (widthSpecMode == MeasureSpec.EXACTLY) {\n                // only the width is fixed, adjust the height to match aspect ratio if possible\n                width = widthSpecSize;\n                height = width * videoHeight / videoWidth;\n                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {\n                    // couldn't match aspect ratio within the constraints\n                    height = heightSpecSize;\n                    width = height * videoWidth / videoHeight;\n                }\n            } else if (heightSpecMode == MeasureSpec.EXACTLY) {\n                // only the height is fixed, adjust the width to match aspect ratio if possible\n                height = heightSpecSize;\n                width = height * videoWidth / videoHeight;\n                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {\n                    // couldn't match aspect ratio within the constraints\n                    width = widthSpecSize;\n                    height = width * videoHeight / videoWidth;\n                }\n            } else {\n                // neither the width nor the height are fixed, try to use actual video size\n                width = videoWidth;\n                height = videoHeight;\n                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {\n                    // too tall, decrease both width and height\n                    height = heightSpecSize;\n                    width = height * videoWidth / videoHeight;\n                }\n                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {\n                    // too wide, decrease both width and height\n                    width = widthSpecSize;\n                    height = width * videoHeight / videoWidth;\n                }\n            }\n        } else {\n            // no size yet, just adopt the given spec sizes\n        }\n        if (DEBUG) {\n            Log.i(TAG, \"viewWidth = \" + width + \", \" + \"viewHeight = \" + height);\n            Log.i(TAG, \"viewWidth / viewHeight = \" + width / height);\n        }\n        setMeasuredDimension(width, height);\n    }\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCResizeTextureView.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Point;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.TextureView;\n\n/**\n * <p>参照Android系统的VideoView的onMeasure方法\n * <br>注意!relativelayout中无法全屏，要嵌套一个linearlayout</p>\n * <p>Referring Android system Video View of onMeasure method\n * <br>NOTE! Can not fullscreen relativelayout, to nest a linearlayout</p>\n * Created by Nathen\n * On 2016/06/02 00:01\n */\npublic class JCResizeTextureView extends TextureView {\n    protected static final String TAG = \"JCResizeTextureView\";\n    protected static final boolean DEBUG = false;\n\n    // x as width, y as height\n    protected Point mVideoSize;\n    protected boolean hasUpdated;\n\n    public JCResizeTextureView(Context context) {\n        super(context);\n        init();\n    }\n\n    public JCResizeTextureView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    @Override\n    public Bitmap getBitmap() {\n        if (hasUpdated) {\n            return super.getBitmap();\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public Bitmap getBitmap(int width, int height) {\n        if (hasUpdated) {\n            return super.getBitmap(width, height);\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public Bitmap getBitmap(Bitmap bitmap) {\n        if (hasUpdated) {\n            return super.getBitmap(bitmap);\n        } else {\n            return null;\n        }\n    }\n\n    private void init() {\n        mVideoSize = new Point(0, 0);\n    }\n\n    public void setVideoSize(Point videoSize) {\n        if (videoSize != null && !mVideoSize.equals(videoSize)) {\n            this.mVideoSize = videoSize;\n            requestLayout();\n        }\n    }\n\n    @Override\n    public void setRotation(float rotation) {\n        if (rotation != getRotation()) {\n            super.setRotation(rotation);\n            requestLayout();\n        }\n    }\n\n    /*\n        在明确TextureView已经被填充Image数据的情况下调用\n     */\n    public void setHasUpdated() {\n        hasUpdated = true;\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int viewRotation = (int) getRotation();\n        // 如果判断成立，则说明显示的TextureView和本身的位置是有90度的旋转的，所以需要交换宽高参数。\n        if (viewRotation == 90 || viewRotation == 270) {\n            int tempMeasureSpec = widthMeasureSpec;\n            widthMeasureSpec = heightMeasureSpec;\n            heightMeasureSpec = tempMeasureSpec;\n        }\n\n        if (DEBUG) {\n            Log.i(TAG, \"onMeasure \" + \" [\" + this.hashCode() + \"] \");\n            Log.i(TAG, \"viewRotation = \" + viewRotation);\n        }\n\n        int videoWidth = mVideoSize.x;\n        int videoHeight = mVideoSize.y;\n\n        if (DEBUG) {\n            Log.i(TAG, \"videoWidth = \" + videoWidth + \", \" + \"videoHeight = \" + videoHeight);\n            if (videoWidth > 0 && videoHeight > 0) {\n                Log.i(TAG, \"videoWidth / videoHeight = \" + videoWidth / videoHeight);\n            }\n        }\n\n        int width = getDefaultSize(videoWidth, widthMeasureSpec);\n        int height = getDefaultSize(videoHeight, heightMeasureSpec);\n        if (videoWidth > 0 && videoHeight > 0) {\n\n            int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);\n            int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);\n            int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);\n            int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);\n\n            if (DEBUG) {\n                Log.i(TAG, \"widthMeasureSpec  [\" + MeasureSpec.toString(widthMeasureSpec) + \"]\");\n                Log.i(TAG, \"heightMeasureSpec [\" + MeasureSpec.toString(heightMeasureSpec) + \"]\");\n            }\n\n            if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {\n                // the size is fixed\n                width = widthSpecSize;\n                height = heightSpecSize;\n\n                // for compatibility, we adjust size based on aspect ratio\n                if (videoWidth * height < width * videoHeight) {\n                    width = height * videoWidth / videoHeight;\n                } else if (videoWidth * height > width * videoHeight) {\n                    height = width * videoHeight / videoWidth;\n                }\n            } else if (widthSpecMode == MeasureSpec.EXACTLY) {\n                // only the width is fixed, adjust the height to match aspect ratio if possible\n                width = widthSpecSize;\n                height = width * videoHeight / videoWidth;\n                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {\n                    // couldn't match aspect ratio within the constraints\n                    height = heightSpecSize;\n                    width = height * videoWidth / videoHeight;\n                }\n            } else if (heightSpecMode == MeasureSpec.EXACTLY) {\n                // only the height is fixed, adjust the width to match aspect ratio if possible\n                height = heightSpecSize;\n                width = height * videoWidth / videoHeight;\n                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {\n                    // couldn't match aspect ratio within the constraints\n                    width = widthSpecSize;\n                    height = width * videoHeight / videoWidth;\n                }\n            } else {\n                // neither the width nor the height are fixed, try to use actual video size\n                width = videoWidth;\n                height = videoHeight;\n                if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {\n                    // too tall, decrease both width and height\n                    height = heightSpecSize;\n                    width = height * videoWidth / videoHeight;\n                }\n                if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {\n                    // too wide, decrease both width and height\n                    width = widthSpecSize;\n                    height = width * videoHeight / videoWidth;\n                }\n            }\n        } else {\n            // no size yet, just adopt the given spec sizes\n        }\n        if (DEBUG) {\n            Log.i(TAG, \"viewWidth = \" + width + \", \" + \"viewHeight = \" + height);\n            Log.i(TAG, \"viewWidth / viewHeight = \" + width / height);\n        }\n        setMeasuredDimension(width, height);\n    }\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCUserAction.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\n/**\n * Created by Nathen\n * On 2016/04/04 22:13\n */\npublic interface JCUserAction {\n\n    int ON_CLICK_START_ICON = 0;\n    int ON_CLICK_START_ERROR = 1;\n    int ON_CLICK_START_AUTO_COMPLETE = 2;\n\n    int ON_CLICK_PAUSE = 3;\n    int ON_CLICK_RESUME = 4;\n    int ON_SEEK_POSITION = 5;\n    int ON_AUTO_COMPLETE = 6;\n\n    int ON_ENTER_FULLSCREEN = 7;\n    int ON_QUIT_FULLSCREEN = 8;\n    int ON_ENTER_TINYSCREEN = 9;\n    int ON_QUIT_TINYSCREEN = 10;\n\n\n    int ON_TOUCH_SCREEN_SEEK_VOLUME = 11;\n    int ON_TOUCH_SCREEN_SEEK_POSITION = 12;\n\n    void onEvent(int type, String url, int screen, Object... objects);\n\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCUserActionStandard.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\n/**\n * Created by Nathen\n * On 2016/04/26 20:53\n */\npublic interface JCUserActionStandard extends JCUserAction {\n\n    int ON_CLICK_START_THUMB = 101;\n    int ON_CLICK_BLANK = 102;\n\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCUtils.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.view.ContextThemeWrapper;\n\nimport java.util.Formatter;\nimport java.util.Locale;\n\n/**\n * Created by Nathen\n * On 2016/02/21 12:25\n */\npublic class JCUtils {\n\n    public static String stringForTime(int timeMs) {\n        if (timeMs <= 0 || timeMs >= 24 * 60 * 60 * 1000) {\n            return \"00:00\";\n        }\n        int totalSeconds = timeMs / 1000;\n        int seconds = totalSeconds % 60;\n        int minutes = (totalSeconds / 60) % 60;\n        int hours = totalSeconds / 3600;\n        StringBuilder stringBuilder = new StringBuilder();\n        Formatter mFormatter = new Formatter(stringBuilder, Locale.getDefault());\n        if (hours > 0) {\n            return mFormatter.format(\"%d:%02d:%02d\", hours, minutes, seconds).toString();\n        } else {\n            return mFormatter.format(\"%02d:%02d\", minutes, seconds).toString();\n        }\n    }\n\n    /**\n     * This method requires the caller to hold the permission ACCESS_NETWORK_STATE.\n     *\n     * @param context a application context\n     * @return if wifi is connected,return true\n     */\n    public static boolean isWifiConnected(Context context) {\n        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);\n        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();\n        return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;\n    }\n\n    /**\n     * Get activity from context object\n     *\n     * @param context something\n     * @return object of Activity or null if it is not Activity\n     */\n    public static Activity scanForActivity(Context context) {\n        if (context == null) return null;\n\n        if (context instanceof Activity) {\n            return (Activity) context;\n        } else if (context instanceof ContextWrapper) {\n            return scanForActivity(((ContextWrapper) context).getBaseContext());\n        }\n\n        return null;\n    }\n\n    /**\n     * Get AppCompatActivity from context\n     *\n     * @param context\n     * @return AppCompatActivity if it's not null\n     */\n    public static AppCompatActivity getAppCompActivity(Context context) {\n        if (context == null) return null;\n        if (context instanceof AppCompatActivity) {\n            return (AppCompatActivity) context;\n        } else if (context instanceof ContextThemeWrapper) {\n            return getAppCompActivity(((ContextThemeWrapper) context).getBaseContext());\n        }\n        return null;\n    }\n\n    public static int dip2px(Context context, float dpValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCVideoPlayer.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.content.Context;\nimport android.content.pm.ActivityInfo;\nimport android.graphics.Bitmap;\nimport android.graphics.Point;\nimport android.graphics.SurfaceTexture;\nimport android.hardware.Sensor;\nimport android.hardware.SensorEvent;\nimport android.hardware.SensorEventListener;\nimport android.hardware.SensorManager;\nimport android.media.AudioManager;\nimport android.os.Handler;\nimport android.support.v7.app.ActionBar;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.Surface;\nimport android.view.TextureView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Constructor;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Timer;\nimport java.util.TimerTask;\n\nimport tv.danmaku.ijk.media.player.IMediaPlayer;\n\n/**\n * Created by Nathen on 16/7/30.\n */\npublic abstract class JCVideoPlayer extends FrameLayout implements JCMediaPlayerListener, View.OnClickListener, SeekBar.OnSeekBarChangeListener, View.OnTouchListener, TextureView.SurfaceTextureListener {\n\n    public static final String TAG = \"JieCaoVideoPlayer\";\n\n    public static final int FULLSCREEN_ID = 33797;\n    public static final int TINY_ID = 33798;\n    public static final int THRESHOLD = 80;\n    public static final int FULL_SCREEN_NORMAL_DELAY = 500;\n\n    public static boolean ACTION_BAR_EXIST = true;\n    public static boolean TOOL_BAR_EXIST = true;\n    public static boolean WIFI_TIP_DIALOG_SHOWED = false;\n    public static int FULLSCREEN_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_SENSOR;\n    public static int NORMAL_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;\n\n    public static long CLICK_QUIT_FULLSCREEN_TIME = 0;\n\n    public static final int SCREEN_LAYOUT_NORMAL = 0;\n    public static final int SCREEN_LAYOUT_LIST = 1;\n    public static final int SCREEN_WINDOW_FULLSCREEN = 2;\n    public static final int SCREEN_WINDOW_TINY = 3;\n\n    public static final int CURRENT_STATE_NORMAL = 0;\n    public static final int CURRENT_STATE_PREPARING = 1;\n    public static final int CURRENT_STATE_PLAYING = 2;\n    public static final int CURRENT_STATE_PLAYING_BUFFERING_START = 3;\n    public static final int CURRENT_STATE_PAUSE = 5;\n    public static final int CURRENT_STATE_AUTO_COMPLETE = 6;\n    public static final int CURRENT_STATE_ERROR = 7;\n\n    public int currentState = -1;\n    public int currentScreen = -1;\n\n\n    public String url = \"\";\n    public Object[] objects = null;\n    public boolean looping = false;\n    public Map<String, String> mapHeadData = new HashMap<>();\n    public int seekToInAdvance = -1;\n    protected static Bitmap pauseSwitchCoverBitmap = null;\n    private boolean textureUpdated;\n    private boolean textureSizeChanged;\n\n    public ImageView startButton;\n    public JCResizeImageView cacheImageView;\n\n    public SeekBar progressBar;\n    public ImageView fullscreenButton;\n    public TextView currentTimeTextView, totalTimeTextView;\n    public ViewGroup textureViewContainer;\n    public ViewGroup topContainer, bottomContainer;\n    public Surface surface;\n\n    protected static WeakReference<JCUserAction> JC_USER_EVENT;\n    protected static Timer UPDATE_PROGRESS_TIMER;\n\n    protected int mScreenWidth;\n    protected int mScreenHeight;\n    protected AudioManager mAudioManager;\n    protected Handler mHandler;\n    protected ProgressTimerTask mProgressTimerTask;\n\n    protected boolean mTouchingProgressBar;\n    protected float mDownX;\n    protected float mDownY;\n    protected boolean mChangeVolume;\n    protected boolean mChangePosition;\n    protected int mDownPosition;\n    protected int mGestureDownVolume;\n    protected int mSeekTimePosition;\n\n    public JCVideoPlayer(Context context) {\n        super(context);\n        init(context);\n    }\n\n    public JCVideoPlayer(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context);\n    }\n\n    public void init(Context context) {\n        View.inflate(context, getLayoutId(), this);\n        startButton = (ImageView) findViewById(R.id.start);\n        fullscreenButton = (ImageView) findViewById(R.id.fullscreen);\n        progressBar = (SeekBar) findViewById(R.id.progress);\n        currentTimeTextView = (TextView) findViewById(R.id.current);\n        totalTimeTextView = (TextView) findViewById(R.id.total);\n        bottomContainer = (ViewGroup) findViewById(R.id.layout_bottom);\n        textureViewContainer = (ViewGroup) findViewById(R.id.surface_container);\n        topContainer = (ViewGroup) findViewById(R.id.layout_top);\n        cacheImageView = (JCResizeImageView) findViewById(R.id.cache);\n\n        startButton.setOnClickListener(this);\n        fullscreenButton.setOnClickListener(this);\n        progressBar.setOnSeekBarChangeListener(this);\n        bottomContainer.setOnClickListener(this);\n        textureViewContainer.setOnClickListener(this);\n\n        textureViewContainer.setOnTouchListener(this);\n        mScreenWidth = getContext().getResources().getDisplayMetrics().widthPixels;\n        mScreenHeight = getContext().getResources().getDisplayMetrics().heightPixels;\n        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);\n        mHandler = new Handler();\n    }\n\n    public boolean setUp(String url, int screen, Object... objects) {\n        if (!TextUtils.isEmpty(this.url) && TextUtils.equals(this.url, url)) {\n            return false;\n        }\n        JCVideoPlayerManager.checkAndPutListener(this);\n        if (JCVideoPlayerManager.CURRENT_SCROLL_LISTENER != null && JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get() != null) {\n            if (this == JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get()) {\n                if (((JCVideoPlayer) JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get()).currentState == CURRENT_STATE_PLAYING) {\n                    if (url.equals(JCMediaManager.instance().mediaPlayer.getDataSource())) {\n                        ((JCVideoPlayer) JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get()).startWindowTiny();//如果列表中,滑动过快,在还没来得及onScroll的时候自己已经被复用了\n                    }\n                }\n            }\n        }\n        this.url = url;\n        this.objects = objects;\n        this.currentScreen = screen;\n        setUiWitStateAndScreen(CURRENT_STATE_NORMAL);\n        if (url.equals(JCMediaManager.instance().mediaPlayer.getDataSource())) {//如果初始化了一个正在tinyWindow的前身,就应该监听它的滑动,如果显示就在这个listener中播放\n            JCVideoPlayerManager.putScrollListener(this);\n        }\n        return true;\n    }\n\n    @Override\n    public int getScreenType() {\n        return currentScreen;\n    }\n\n    @Override\n    public String getUrl() {\n        return url;\n    }\n\n    @Override\n    public int getState() {\n        return currentState;\n    }\n\n    public boolean setUp(String url, int screen, Map<String, String> mapHeadData, Object... objects) {\n        if (setUp(url, screen, objects)) {\n            this.mapHeadData.clear();\n            this.mapHeadData.putAll(mapHeadData);\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public void onClick(View v) {\n        int i = v.getId();\n        if (i == R.id.start) {\n            Log.i(TAG, \"onClick start [\" + this.hashCode() + \"] \");\n            if (TextUtils.isEmpty(url)) {\n                Toast.makeText(getContext(), getResources().getString(R.string.no_url), Toast.LENGTH_SHORT).show();\n                return;\n            }\n            if (currentState == CURRENT_STATE_NORMAL || currentState == CURRENT_STATE_ERROR) {\n                if (!url.startsWith(\"file\") && !JCUtils.isWifiConnected(getContext()) && !WIFI_TIP_DIALOG_SHOWED) {\n                    showWifiDialog();\n                    return;\n                }\n                prepareVideo();\n                onEvent(currentState != CURRENT_STATE_ERROR ? JCUserAction.ON_CLICK_START_ICON : JCUserAction.ON_CLICK_START_ERROR);\n            } else if (currentState == CURRENT_STATE_PLAYING) {\n                obtainCache();\n                onEvent(JCUserAction.ON_CLICK_PAUSE);\n                Log.d(TAG, \"pauseVideo [\" + this.hashCode() + \"] \");\n                JCMediaManager.instance().mediaPlayer.pause();\n                setUiWitStateAndScreen(CURRENT_STATE_PAUSE);\n                refreshCache();\n            } else if (currentState == CURRENT_STATE_PAUSE) {\n                onEvent(JCUserAction.ON_CLICK_RESUME);\n                JCMediaManager.instance().mediaPlayer.start();\n                setUiWitStateAndScreen(CURRENT_STATE_PLAYING);\n            } else if (currentState == CURRENT_STATE_AUTO_COMPLETE) {\n                onEvent(JCUserAction.ON_CLICK_START_AUTO_COMPLETE);\n                prepareVideo();\n            }\n        } else if (i == R.id.fullscreen) {\n            Log.i(TAG, \"onClick fullscreen [\" + this.hashCode() + \"] \");\n            if (currentState == CURRENT_STATE_AUTO_COMPLETE) return;\n            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n                //quit fullscreen\n                backPress();\n            } else {\n                Log.d(TAG, \"toFullscreenActivity [\" + this.hashCode() + \"] \");\n                onEvent(JCUserAction.ON_ENTER_FULLSCREEN);\n                startWindowFullscreen();\n            }\n        } else if (i == R.id.surface_container && currentState == CURRENT_STATE_ERROR) {\n            Log.i(TAG, \"onClick surfaceContainer State=Error [\" + this.hashCode() + \"] \");\n            prepareVideo();\n        }\n    }\n\n    public void prepareVideo() {\n        Log.d(TAG, \"prepareVideo [\" + this.hashCode() + \"] \");\n        JCVideoPlayerManager.completeAll();\n        JCVideoPlayerManager.putListener(this);\n        addTextureView();\n\n        AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);\n        mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);\n        JCUtils.scanForActivity(getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n\n        JCVideoPlayerManager.putScrollListener(this);\n        JCMediaManager.instance().prepare(url, mapHeadData, looping);\n        setUiWitStateAndScreen(CURRENT_STATE_PREPARING);\n    }\n\n    @Override\n    public boolean onTouch(View v, MotionEvent event) {\n        float x = event.getX();\n        float y = event.getY();\n        int id = v.getId();\n        if (id == R.id.surface_container) {\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    Log.i(TAG, \"onTouch surfaceContainer actionDown [\" + this.hashCode() + \"] \");\n                    mTouchingProgressBar = true;\n\n                    mDownX = x;\n                    mDownY = y;\n                    mChangeVolume = false;\n                    mChangePosition = false;\n                    /////////////////////\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    Log.i(TAG, \"onTouch surfaceContainer actionMove [\" + this.hashCode() + \"] \");\n                    float deltaX = x - mDownX;\n                    float deltaY = y - mDownY;\n                    float absDeltaX = Math.abs(deltaX);\n                    float absDeltaY = Math.abs(deltaY);\n                    if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n                        if (!mChangePosition && !mChangeVolume) {\n                            if (absDeltaX > THRESHOLD || absDeltaY > THRESHOLD) {\n                                cancelProgressTimer();\n                                if (absDeltaX >= THRESHOLD) {\n                                    // 全屏模式下的CURRENT_STATE_ERROR状态下,不响应进度拖动事件.\n                                    // 否则会因为mediaplayer的状态非法导致App Crash\n                                    if (currentState != CURRENT_STATE_ERROR) {\n                                        mChangePosition = true;\n                                        mDownPosition = getCurrentPositionWhenPlaying();\n                                    }\n                                } else {\n                                    mChangeVolume = true;\n                                    mGestureDownVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);\n                                }\n                            }\n                        }\n                    }\n                    if (mChangePosition) {\n                        int totalTimeDuration = getDuration();\n                        mSeekTimePosition = (int) (mDownPosition + deltaX * totalTimeDuration / mScreenWidth);\n                        if (mSeekTimePosition > totalTimeDuration)\n                            mSeekTimePosition = totalTimeDuration;\n                        String seekTime = JCUtils.stringForTime(mSeekTimePosition);\n                        String totalTime = JCUtils.stringForTime(totalTimeDuration);\n\n                        showProgressDialog(deltaX, seekTime, mSeekTimePosition, totalTime, totalTimeDuration);\n                    }\n                    if (mChangeVolume) {\n                        deltaY = -deltaY;\n                        int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);\n                        int deltaV = (int) (max * deltaY * 3 / mScreenHeight);\n                        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mGestureDownVolume + deltaV, 0);\n                        int volumePercent = (int) (mGestureDownVolume * 100 / max + deltaY * 3 * 100 / mScreenHeight);\n\n                        showVolumeDialog(-deltaY, volumePercent);\n                    }\n\n                    break;\n                case MotionEvent.ACTION_UP:\n                    Log.i(TAG, \"onTouch surfaceContainer actionUp [\" + this.hashCode() + \"] \");\n                    mTouchingProgressBar = false;\n                    dismissProgressDialog();\n                    dismissVolumeDialog();\n                    if (mChangePosition) {\n                        onEvent(JCUserAction.ON_TOUCH_SCREEN_SEEK_POSITION);\n                        JCMediaManager.instance().mediaPlayer.seekTo(mSeekTimePosition);\n                        int duration = getDuration();\n                        int progress = mSeekTimePosition * 100 / (duration == 0 ? 1 : duration);\n                        progressBar.setProgress(progress);\n                    }\n                    if (mChangeVolume) {\n                        onEvent(JCUserAction.ON_TOUCH_SCREEN_SEEK_VOLUME);\n                    }\n                    startProgressTimer();\n                    break;\n            }\n        }\n        return false;\n    }\n\n    public void addTextureView() {\n        Log.d(TAG, \"addTextureView [\" + this.hashCode() + \"] \");\n        if (textureViewContainer.getChildCount() > 0) {\n            textureViewContainer.removeAllViews();\n        }\n        JCMediaManager.textureView = null;\n        JCMediaManager.textureView = new JCResizeTextureView(getContext());\n        JCMediaManager.textureView.setVideoSize(JCMediaManager.instance().getVideoSize());\n        JCMediaManager.textureView.setRotation(JCMediaManager.instance().videoRotation);\n        JCMediaManager.textureView.setSurfaceTextureListener(this);\n\n        FrameLayout.LayoutParams layoutParams =\n                new FrameLayout.LayoutParams(\n                        ViewGroup.LayoutParams.MATCH_PARENT,\n                        ViewGroup.LayoutParams.MATCH_PARENT,\n                        Gravity.CENTER);\n        textureViewContainer.addView(JCMediaManager.textureView, layoutParams);\n\n        cacheImageView.setVideoSize(JCMediaManager.instance().getVideoSize());\n        cacheImageView.setRotation(JCMediaManager.instance().videoRotation);\n\n    }\n\n    public void setUiWitStateAndScreen(int state) {\n        currentState = state;\n        switch (currentState) {\n            case CURRENT_STATE_NORMAL:\n                if (isCurrentMediaListener()) {\n                    cancelProgressTimer();\n                    JCMediaManager.instance().releaseMediaPlayer();\n                }\n                break;\n            case CURRENT_STATE_PREPARING:\n                resetProgressAndTime();\n                break;\n            case CURRENT_STATE_PLAYING:\n            case CURRENT_STATE_PAUSE:\n            case CURRENT_STATE_PLAYING_BUFFERING_START:\n                startProgressTimer();\n                break;\n            case CURRENT_STATE_ERROR:\n                cancelProgressTimer();\n                if (isCurrentMediaListener()) {\n                    JCMediaManager.instance().releaseMediaPlayer();\n                }\n                break;\n            case CURRENT_STATE_AUTO_COMPLETE:\n                cancelProgressTimer();\n                progressBar.setProgress(100);\n                currentTimeTextView.setText(totalTimeTextView.getText());\n                break;\n        }\n    }\n\n    public void startProgressTimer() {\n        cancelProgressTimer();\n        UPDATE_PROGRESS_TIMER = new Timer();\n        mProgressTimerTask = new ProgressTimerTask();\n        UPDATE_PROGRESS_TIMER.schedule(mProgressTimerTask, 0, 300);\n    }\n\n    public void cancelProgressTimer() {\n        if (UPDATE_PROGRESS_TIMER != null) {\n            UPDATE_PROGRESS_TIMER.cancel();\n        }\n        if (mProgressTimerTask != null) {\n            mProgressTimerTask.cancel();\n        }\n    }\n\n    @Override\n    public void onPrepared() {\n        Log.i(TAG, \"onPrepared \" + \" [\" + this.hashCode() + \"] \");\n\n        if (currentState != CURRENT_STATE_PREPARING) return;\n        JCMediaManager.instance().mediaPlayer.start();\n        if (seekToInAdvance != -1) {\n            JCMediaManager.instance().mediaPlayer.seekTo(seekToInAdvance);\n            seekToInAdvance = -1;\n        }\n        startProgressTimer();\n        setUiWitStateAndScreen(CURRENT_STATE_PLAYING);\n    }\n\n    public void clearFullscreenLayout() {\n        ViewGroup vp = (ViewGroup) (JCUtils.scanForActivity(getContext()))//.getWindow().getDecorView();\n                .findViewById(Window.ID_ANDROID_CONTENT);\n        View oldF = vp.findViewById(FULLSCREEN_ID);\n        View oldT = vp.findViewById(TINY_ID);\n        if (oldF != null) {\n            vp.removeView(oldF);\n        }\n        if (oldT != null) {\n            vp.removeView(oldT);\n        }\n        showSupportActionBar(getContext());\n    }\n\n    @Override\n    public void onAutoCompletion() {\n        Log.i(TAG, \"onAutoCompletion \" + \" [\" + this.hashCode() + \"] \");\n        onEvent(JCUserAction.ON_AUTO_COMPLETE);\n        dismissVolumeDialog();\n        dismissProgressDialog();\n        setUiWitStateAndScreen(CURRENT_STATE_AUTO_COMPLETE);\n        JCVideoPlayerManager.popListener();//自己进入autoComplete状态，其他的进入complete状态\n        JCVideoPlayerManager.completeAll();\n    }\n\n    @Override\n    public void onCompletion() {\n        Log.i(TAG, \"onCompletion \" + \" [\" + this.hashCode() + \"] \");\n        setUiWitStateAndScreen(CURRENT_STATE_NORMAL);\n        if (textureViewContainer.getChildCount() > 0) {\n            textureViewContainer.removeAllViews();\n        }\n\n        JCMediaManager.instance().currentVideoWidth = 0;\n        JCMediaManager.instance().currentVideoHeight = 0;\n\n        // 清理缓存变量\n        JCMediaManager.instance().bufferPercent = 0;\n        JCMediaManager.instance().videoRotation = 0;\n\n        AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);\n        mAudioManager.abandonAudioFocus(onAudioFocusChangeListener);\n        JCUtils.scanForActivity(getContext()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n        clearFullscreenLayout();\n        JCUtils.getAppCompActivity(getContext()).setRequestedOrientation(NORMAL_ORIENTATION);\n\n        // 清理cover image,回收bitmap内存\n        clearCacheImage();\n\n    }\n\n    @Override\n    public boolean backToOtherListener() {//这里这个名字这么写并不对,这是在回退的时候gotoother,如果直接gotoother就不叫这个名字\n\n        obtainCache();\n\n        Log.i(TAG, \"backToOtherListener \" + \" [\" + this.hashCode() + \"] \");\n        JCUtils.getAppCompActivity(getContext()).setRequestedOrientation(NORMAL_ORIENTATION);\n        if (currentScreen == JCVideoPlayerStandard.SCREEN_WINDOW_FULLSCREEN\n                || currentScreen == JCVideoPlayerStandard.SCREEN_WINDOW_TINY) {\n//            if (currentScreen == JCVideoPlayerStandard.SCREEN_WINDOW_FULLSCREEN) {\n//                final Animation ra = AnimationUtils.loadAnimation(getContext(), R.anim.quit_fullscreen);\n//                startAnimation(ra);\n//            }\n            onEvent(currentScreen == JCVideoPlayerStandard.SCREEN_WINDOW_FULLSCREEN ?\n                    JCUserAction.ON_QUIT_FULLSCREEN :\n                    JCUserAction.ON_QUIT_TINYSCREEN);\n            if (JCVideoPlayerManager.LISTENERLIST.size() == 1) {//directly fullscreen\n                JCVideoPlayerManager.popListener().onCompletion();\n                JCMediaManager.instance().releaseMediaPlayer();\n                showSupportActionBar(getContext());\n                return true;\n            }\n            ViewGroup vp = (ViewGroup) (JCUtils.scanForActivity(getContext()))//.getWindow().getDecorView();\n                    .findViewById(Window.ID_ANDROID_CONTENT);\n            vp.removeView(this);\n            JCMediaManager.instance().lastState = currentState;//save state\n            JCVideoPlayerManager.popListener();\n            if (JCVideoPlayerManager.getFirst() != null) {\n                JCVideoPlayerManager.getFirst().goBackThisListener();\n                CLICK_QUIT_FULLSCREEN_TIME = System.currentTimeMillis();\n\n                refreshCache();\n            } else {\n                JCVideoPlayerManager.completeAll();\n            }\n            return true;\n        }\n\n        return false;\n    }\n\n    public static long lastAutoFullscreenTime = 0;\n\n    @Override\n    public void autoFullscreen(float x) {\n        if (isCurrentMediaListener()\n                && currentState == CURRENT_STATE_PLAYING\n                && currentScreen != SCREEN_WINDOW_FULLSCREEN\n                && currentScreen != SCREEN_WINDOW_TINY) {\n            if (x > 0) {\n                JCUtils.getAppCompActivity(getContext()).setRequestedOrientation(\n                        ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n            } else {\n                JCUtils.getAppCompActivity(getContext()).setRequestedOrientation(\n                        ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);\n            }\n            startWindowFullscreen();\n        }\n    }\n\n    @Override\n    public void autoQuitFullscreen() {\n        if ((System.currentTimeMillis() - lastAutoFullscreenTime) > 2000\n                && isCurrentMediaListener()\n                && currentState == CURRENT_STATE_PLAYING\n                && currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n            lastAutoFullscreenTime = System.currentTimeMillis();\n            backPress();\n        }\n    }\n\n    @Override\n    public void goBackThisListener() {\n        Log.i(TAG, \"goBackThisListener \" + \" [\" + this.hashCode() + \"] \");\n\n        currentState = JCMediaManager.instance().lastState;\n        setUiWitStateAndScreen(currentState);\n        addTextureView();\n\n        showSupportActionBar(getContext());\n    }\n\n    @Override\n    public void onBufferingUpdate(int percent) {\n        if (currentState != CURRENT_STATE_NORMAL && currentState != CURRENT_STATE_PREPARING) {\n            Log.v(TAG, \"onBufferingUpdate \" + percent + \" [\" + this.hashCode() + \"] \");\n            JCMediaManager.instance().bufferPercent = percent;\n            setTextAndProgress(percent);\n        }\n    }\n\n    @Override\n    public void onSeekComplete() {\n    }\n\n    @Override\n    public void onError(int what, int extra) {\n        Log.e(TAG, \"onError \" + what + \" - \" + extra + \" [\" + this.hashCode() + \"] \");\n        if (what != 38 && what != -38) {\n            setUiWitStateAndScreen(CURRENT_STATE_ERROR);\n        }\n    }\n\n\n    @Override\n    public void onInfo(int what, int extra) {\n        Log.d(TAG, \"onInfo what - \" + what + \" extra - \" + extra);\n        if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_START) {\n            JCMediaManager.instance().backUpBufferState = currentState;\n            setUiWitStateAndScreen(CURRENT_STATE_PLAYING_BUFFERING_START);\n            Log.d(TAG, \"MEDIA_INFO_BUFFERING_START\");\n        } else if (what == IMediaPlayer.MEDIA_INFO_BUFFERING_END) {\n            if (JCMediaManager.instance().backUpBufferState != -1) {\n                setUiWitStateAndScreen(JCMediaManager.instance().backUpBufferState);\n                JCMediaManager.instance().backUpBufferState = -1;\n            }\n            Log.d(TAG, \"MEDIA_INFO_BUFFERING_END\");\n        } else if (what == IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED) {\n            JCMediaManager.instance().videoRotation = extra;\n            JCMediaManager.textureView.setRotation(extra);\n            cacheImageView.setRotation(JCMediaManager.instance().videoRotation);\n            Log.d(TAG, \"MEDIA_INFO_VIDEO_ROTATION_CHANGED\");\n\n\n        }\n    }\n\n    @Override\n    public void onVideoSizeChanged() {\n        Log.i(TAG, \"onVideoSizeChanged \" + \" [\" + this.hashCode() + \"] \");\n        JCMediaManager.textureView.setVideoSize(JCMediaManager.instance().getVideoSize());\n        cacheImageView.setVideoSize(JCMediaManager.instance().getVideoSize());\n    }\n\n    @Override\n    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {\n        Log.i(TAG, \"onSurfaceTextureAvailable [\" + this.hashCode() + \"] \");\n        this.surface = new Surface(surface);\n        JCMediaManager.instance().setDisplay(this.surface);\n    }\n\n    @Override\n    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {\n\n        // 如果SurfaceTexture还没有更新Image，则记录SizeChanged事件，否则忽略\n        textureSizeChanged = true;\n        Log.i(TAG, \"onSurfaceTextureSizeChanged [\" + this.hashCode() + \"] \");\n    }\n\n    @Override\n    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {\n        surface.release();\n        return true;\n    }\n\n    @Override\n    public void onSurfaceTextureUpdated(SurfaceTexture surface) {\n        // 如果textureSizeChanged=true，则说明此次Updated事件不是Image更新引起的   应该是TextureSizeChanged引起的 所以不需要更新 cacheImageView\n        Log.i(TAG, \"onSurfaceTextureUpdated [\" + this.hashCode() + \"] textureSizeChanged=\" + textureSizeChanged);\n        if (!textureSizeChanged) {\n            cacheImageView.setVisibility(INVISIBLE);\n            JCMediaManager.textureView.setHasUpdated();\n        } else {\n            textureSizeChanged = false;\n        }\n    }\n\n    @Override\n    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n    }\n\n    @Override\n    public void onStartTrackingTouch(SeekBar seekBar) {\n        Log.i(TAG, \"bottomProgress onStartTrackingTouch [\" + this.hashCode() + \"] \");\n        cancelProgressTimer();\n        ViewParent vpdown = getParent();\n        while (vpdown != null) {\n            vpdown.requestDisallowInterceptTouchEvent(true);\n            vpdown = vpdown.getParent();\n        }\n    }\n\n    @Override\n    public void onStopTrackingTouch(SeekBar seekBar) {\n        Log.i(TAG, \"bottomProgress onStopTrackingTouch [\" + this.hashCode() + \"] \");\n        onEvent(JCUserAction.ON_SEEK_POSITION);\n        startProgressTimer();\n        ViewParent vpup = getParent();\n        while (vpup != null) {\n            vpup.requestDisallowInterceptTouchEvent(false);\n            vpup = vpup.getParent();\n        }\n        if (currentState != CURRENT_STATE_PLAYING &&\n                currentState != CURRENT_STATE_PAUSE) return;\n        int time = seekBar.getProgress() * getDuration() / 100;\n        JCMediaManager.instance().mediaPlayer.seekTo(time);\n        Log.i(TAG, \"seekTo \" + time + \" [\" + this.hashCode() + \"] \");\n    }\n\n    public static boolean backPress() {\n        Log.i(TAG, \"backPress\");\n        if (JCVideoPlayerManager.getFirst() != null) {\n            return JCVideoPlayerManager.getFirst().backToOtherListener();\n        }\n        return false;\n    }\n\n    public void startWindowFullscreen() {\n\n        obtainCache();\n\n        Log.i(TAG, \"startWindowFullscreen \" + \" [\" + this.hashCode() + \"] \");\n        CLICK_QUIT_FULLSCREEN_TIME = System.currentTimeMillis();\n        hideSupportActionBar(getContext());\n        JCUtils.getAppCompActivity(getContext()).setRequestedOrientation(FULLSCREEN_ORIENTATION);\n\n        ViewGroup vp = (ViewGroup) (JCUtils.scanForActivity(getContext()))//.getWindow().getDecorView();\n                .findViewById(Window.ID_ANDROID_CONTENT);\n        View old = vp.findViewById(FULLSCREEN_ID);\n        if (old != null) {\n            vp.removeView(old);\n        }\n        if (textureViewContainer.getChildCount() > 0) {\n            textureViewContainer.removeAllViews();\n        }\n        try {\n            Constructor<JCVideoPlayer> constructor = (Constructor<JCVideoPlayer>) JCVideoPlayer.this.getClass().getConstructor(Context.class);\n            JCVideoPlayer jcVideoPlayer = constructor.newInstance(getContext());\n            jcVideoPlayer.setId(FULLSCREEN_ID);\n            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n            vp.addView(jcVideoPlayer, lp);\n            jcVideoPlayer.setUp(url, JCVideoPlayerStandard.SCREEN_WINDOW_FULLSCREEN, objects);\n            jcVideoPlayer.setUiWitStateAndScreen(currentState);\n            jcVideoPlayer.addTextureView();\n\n//            final Animation ra = AnimationUtils.loadAnimation(getContext(), R.anim.start_fullscreen);\n//            jcVideoPlayer.setAnimation(ra);\n\n            JCVideoPlayerManager.putListener(jcVideoPlayer);\n\n\n        } catch (InstantiationException e) {\n            e.printStackTrace();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n        refreshCache();\n\n    }\n\n    public void startWindowTiny() {\n        Log.i(TAG, \"startWindowTiny \" + \" [\" + this.hashCode() + \"] \");\n        onEvent(JCUserAction.ON_ENTER_TINYSCREEN);\n\n        ViewGroup vp = (ViewGroup) (JCUtils.scanForActivity(getContext()))//.getWindow().getDecorView();\n                .findViewById(Window.ID_ANDROID_CONTENT);\n        View old = vp.findViewById(TINY_ID);\n        if (old != null) {\n            vp.removeView(old);\n        }\n        if (textureViewContainer.getChildCount() > 0) {\n            textureViewContainer.removeAllViews();\n        }\n        try {\n            Constructor<JCVideoPlayer> constructor = (Constructor<JCVideoPlayer>) JCVideoPlayer.this.getClass().getConstructor(Context.class);\n            JCVideoPlayer mJcVideoPlayer = constructor.newInstance(getContext());\n            mJcVideoPlayer.setId(TINY_ID);\n            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(400, 400);\n            lp.gravity = Gravity.RIGHT | Gravity.BOTTOM;\n            vp.addView(mJcVideoPlayer, lp);\n            mJcVideoPlayer.setUp(url, JCVideoPlayerStandard.SCREEN_WINDOW_TINY, objects);\n            mJcVideoPlayer.setUiWitStateAndScreen(currentState);\n            mJcVideoPlayer.addTextureView();\n            JCVideoPlayerManager.putListener(mJcVideoPlayer);\n\n        } catch (InstantiationException e) {\n            e.printStackTrace();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n\n    }\n\n    public class ProgressTimerTask extends TimerTask {\n        @Override\n        public void run() {\n            if (currentState == CURRENT_STATE_PLAYING || currentState == CURRENT_STATE_PAUSE || currentState == CURRENT_STATE_PLAYING_BUFFERING_START) {\n                int position = getCurrentPositionWhenPlaying();\n                int duration = getDuration();\n                Log.v(TAG, \"onProgressUpdate \" + position + \"/\" + duration + \" [\" + this.hashCode() + \"] \");\n                mHandler.post(new Runnable() {\n                    @Override\n                    public void run() {\n                        setTextAndProgress(JCMediaManager.instance().bufferPercent);\n                    }\n                });\n            }\n        }\n    }\n\n    public int getCurrentPositionWhenPlaying() {\n        int position = 0;\n        if (currentState == CURRENT_STATE_PLAYING || currentState == CURRENT_STATE_PAUSE || currentState == CURRENT_STATE_PLAYING_BUFFERING_START) {\n            try {\n                position = (int) JCMediaManager.instance().mediaPlayer.getCurrentPosition();\n            } catch (IllegalStateException e) {\n                e.printStackTrace();\n                return position;\n            }\n        }\n        return position;\n    }\n\n    public int getDuration() {\n        int duration = 0;\n        try {\n            duration = (int) JCMediaManager.instance().mediaPlayer.getDuration();\n        } catch (IllegalStateException e) {\n            e.printStackTrace();\n            return duration;\n        }\n        return duration;\n    }\n\n    public void setTextAndProgress(int secProgress) {\n        int position = getCurrentPositionWhenPlaying();\n        int duration = getDuration();\n        int progress = position * 100 / (duration == 0 ? 1 : duration);\n        setProgressAndTime(progress, secProgress, position, duration);\n    }\n\n    public void setProgressAndTime(int progress, int secProgress, int currentTime, int totalTime) {\n        if (!mTouchingProgressBar) {\n            if (progress != 0) progressBar.setProgress(progress);\n        }\n        if (secProgress > 95) secProgress = 100;\n        if (secProgress != 0) progressBar.setSecondaryProgress(secProgress);\n        if (currentTime != 0) currentTimeTextView.setText(JCUtils.stringForTime(currentTime));\n        totalTimeTextView.setText(JCUtils.stringForTime(totalTime));\n    }\n\n    public void resetProgressAndTime() {\n        progressBar.setProgress(0);\n        progressBar.setSecondaryProgress(0);\n        currentTimeTextView.setText(JCUtils.stringForTime(0));\n        totalTimeTextView.setText(JCUtils.stringForTime(0));\n    }\n\n    public static AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {\n        @Override\n        public void onAudioFocusChange(int focusChange) {\n            switch (focusChange) {\n                case AudioManager.AUDIOFOCUS_GAIN:\n                    break;\n                case AudioManager.AUDIOFOCUS_LOSS:\n                    releaseAllVideos();\n                    Log.d(TAG, \"AUDIOFOCUS_LOSS [\" + this.hashCode() + \"]\");\n                    break;\n                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:\n                    if (JCMediaManager.instance().mediaPlayer.isPlaying()) {\n                        JCMediaManager.instance().mediaPlayer.pause();\n                    }\n                    Log.d(TAG, \"AUDIOFOCUS_LOSS_TRANSIENT [\" + this.hashCode() + \"]\");\n                    break;\n                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:\n                    break;\n            }\n        }\n    };\n\n    public void release() {\n        if (url.equals(JCMediaManager.instance().mediaPlayer.getDataSource()) &&\n                (System.currentTimeMillis() - CLICK_QUIT_FULLSCREEN_TIME) > FULL_SCREEN_NORMAL_DELAY) {\n            //如果正在全屏播放就不能手动调用release\n            if (JCVideoPlayerManager.getFirst() != null &&\n                    JCVideoPlayerManager.getFirst().getScreenType() != SCREEN_WINDOW_FULLSCREEN) {\n                Log.d(TAG, \"release [\" + this.hashCode() + \"]\");\n                releaseAllVideos();\n            }\n        }\n    }\n\n    //isCurrentMediaListener and isCurrenPlayUrl should be two logic methods,isCurrentMediaListener is for different jcvd with same\n    //url when fullscreen or tiny screen. isCurrenPlayUrl is to find where is myself when back from tiny screen.\n    //Sometimes they are overlap.\n    public boolean isCurrentMediaListener() {\n        return JCVideoPlayerManager.getFirst() != null\n                && JCVideoPlayerManager.getFirst() == this;\n    }\n\n    public boolean isCurrenPlayingUrl() {\n        return url.equals(JCMediaManager.instance().mediaPlayer.getDataSource());\n    }\n\n    public static void releaseAllVideos() {\n        Log.d(TAG, \"releaseAllVideos\");\n        JCVideoPlayerManager.completeAll();\n        JCMediaManager.instance().releaseMediaPlayer();\n    }\n\n    public static void setJcUserAction(JCUserAction jcUserEvent) {\n        JC_USER_EVENT = new WeakReference<>(jcUserEvent);\n    }\n\n    public void onEvent(int type) {\n        if (JC_USER_EVENT != null && JC_USER_EVENT.get() != null && isCurrentMediaListener()) {\n            JC_USER_EVENT.get().onEvent(type, url, currentScreen, objects);\n        }\n    }\n\n    @Override\n    public void onScrollChange() {//这里需要自己判断自己是 进入小窗,退出小窗,暂停还是播放\n        if (url.equals(JCMediaManager.instance().mediaPlayer.getDataSource())) {\n            if (JCVideoPlayerManager.getFirst() == null) return;\n            if (JCVideoPlayerManager.getFirst().getScreenType() == SCREEN_WINDOW_TINY) {\n                //如果正在播放的是小窗,择机退出小窗\n                if (isShown()) {//已经显示,就退出小窗\n                    backPress();\n                }\n            } else {\n                //如果正在播放的不是小窗,择机进入小窗\n                if (!isShown()) {//已经隐藏\n                    if (currentState != CURRENT_STATE_PLAYING) {\n                        releaseAllVideos();\n                    } else {\n                        startWindowTiny();\n                    }\n                }\n            }\n        }\n    }\n\n    public static void onScroll() {//这里就应该保证,listener的正确的完整的赋值,调用非播放的控件\n        if (JCVideoPlayerManager.CURRENT_SCROLL_LISTENER != null && JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get() != null) {\n            JCMediaPlayerListener jcMediaPlayerListener = JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get();\n            if (//jcMediaPlayerListenerWeakReference.get().getState() != CURRENT_STATE_NORMAL &&\n                    jcMediaPlayerListener.getState() != CURRENT_STATE_ERROR &&\n                            jcMediaPlayerListener.getState() != CURRENT_STATE_AUTO_COMPLETE) {\n                jcMediaPlayerListener.onScrollChange();\n            }\n        }\n    }\n\n    public static void startFullscreen(Context context, Class _class, String url, Object... objects) {\n\n        hideSupportActionBar(context);\n        JCUtils.getAppCompActivity(context).setRequestedOrientation(FULLSCREEN_ORIENTATION);\n        ViewGroup vp = (ViewGroup) (JCUtils.scanForActivity(context))//.getWindow().getDecorView();\n                .findViewById(Window.ID_ANDROID_CONTENT);\n        View old = vp.findViewById(JCVideoPlayer.FULLSCREEN_ID);\n        if (old != null) {\n            vp.removeView(old);\n        }\n        try {\n            Constructor<JCVideoPlayer> constructor = _class.getConstructor(Context.class);\n            JCVideoPlayer jcVideoPlayer = constructor.newInstance(context);\n            jcVideoPlayer.setId(JCVideoPlayer.FULLSCREEN_ID);\n            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n\n            vp.addView(jcVideoPlayer, lp);\n\n//            final Animation ra = AnimationUtils.loadAnimation(context, R.anim.start_fullscreen);\n//            jcVideoPlayer.setAnimation(ra);\n\n            jcVideoPlayer.setUp(url, JCVideoPlayerStandard.SCREEN_WINDOW_FULLSCREEN, objects);\n            jcVideoPlayer.addTextureView();\n\n            jcVideoPlayer.startButton.performClick();\n\n        } catch (InstantiationException e) {\n            e.printStackTrace();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static void hideSupportActionBar(Context context) {\n        if (ACTION_BAR_EXIST) {\n            ActionBar ab = JCUtils.getAppCompActivity(context).getSupportActionBar();\n            if (ab != null) {\n                ab.setShowHideAnimationEnabled(false);\n                ab.hide();\n            }\n        }\n        if (TOOL_BAR_EXIST) {\n            JCUtils.getAppCompActivity(context).getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,\n                    WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        }\n    }\n\n    public static void showSupportActionBar(Context context) {\n        if (ACTION_BAR_EXIST) {\n            ActionBar ab = JCUtils.getAppCompActivity(context).getSupportActionBar();\n            if (ab != null) {\n                ab.setShowHideAnimationEnabled(false);\n                ab.show();\n            }\n        }\n        if (TOOL_BAR_EXIST) {\n            JCUtils.getAppCompActivity(context).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        }\n    }\n\n    public static class JCAutoFullscreenListener implements SensorEventListener {\n        @Override\n        public void onSensorChanged(SensorEvent event) {//可以得到传感器实时测量出来的变化值\n            final float x = event.values[SensorManager.DATA_X];\n            float y = event.values[SensorManager.DATA_Y];\n            float z = event.values[SensorManager.DATA_Z];\n            //过滤掉用力过猛会有一个反向的大数值\n            if (((x > -15 && x < -10) || (x < 15 && x > 10)) && Math.abs(y) < 1.5) {\n                if ((System.currentTimeMillis() - lastAutoFullscreenTime) > 2000) {\n                    if (JCVideoPlayerManager.getFirst() != null) {\n                        JCVideoPlayerManager.getFirst().autoFullscreen(x);\n                    }\n                    lastAutoFullscreenTime = System.currentTimeMillis();\n                }\n            }\n        }\n\n        @Override\n        public void onAccuracyChanged(Sensor sensor, int accuracy) {\n        }\n    }\n\n\n    private void obtainCache() {\n        Point videoSize = JCMediaManager.instance().getVideoSize();\n        if (videoSize != null) {\n            Bitmap bitmap = JCMediaManager.textureView.getBitmap(videoSize.x, videoSize.y);\n            if (bitmap != null) {\n                pauseSwitchCoverBitmap = bitmap;\n            }\n        }\n    }\n\n    public void refreshCache() {\n        if (pauseSwitchCoverBitmap != null) {\n            JCVideoPlayer jcVideoPlayer = ((JCVideoPlayer) JCVideoPlayerManager.getFirst());\n            if (jcVideoPlayer != null) {\n                jcVideoPlayer.cacheImageView.setImageBitmap(pauseSwitchCoverBitmap);\n                jcVideoPlayer.cacheImageView.setVisibility(VISIBLE);\n            }\n        }\n    }\n\n    public void clearCacheImage() {\n        pauseSwitchCoverBitmap = null;\n        cacheImageView.setImageBitmap(null);\n    }\n\n    public void showWifiDialog() {\n    }\n\n    public void showProgressDialog(float deltaX,\n                                   String seekTime, int seekTimePosition,\n                                   String totalTime, int totalTimeDuration) {\n    }\n\n    public void dismissProgressDialog() {\n\n    }\n\n    public void showVolumeDialog(float deltaY, int volumePercent) {\n\n    }\n\n    public void dismissVolumeDialog() {\n\n    }\n\n\n    public abstract int getLayoutId();\n\n\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCVideoPlayerManager.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport java.lang.ref.WeakReference;\nimport java.util.LinkedList;\n\n/**\n * Put JCVideoPlayer into layout\n * From a JCVideoPlayer to another JCVideoPlayer\n * Created by Nathen on 16/7/26.\n */\npublic class JCVideoPlayerManager {\n\n    public static WeakReference<JCMediaPlayerListener> CURRENT_SCROLL_LISTENER;\n    public static LinkedList<WeakReference<JCMediaPlayerListener>> LISTENERLIST = new LinkedList<>();\n\n    public static void putScrollListener(JCMediaPlayerListener listener) {\n        if (listener.getScreenType() == JCVideoPlayer.SCREEN_WINDOW_TINY ||\n                listener.getScreenType() == JCVideoPlayer.SCREEN_WINDOW_FULLSCREEN) return;\n        CURRENT_SCROLL_LISTENER = new WeakReference<>(listener);//每次setUp的时候都应该add\n    }\n\n    public static void putListener(JCMediaPlayerListener listener) {\n        LISTENERLIST.push(new WeakReference<>(listener));\n    }\n\n    public static void checkAndPutListener(JCMediaPlayerListener listener) {\n        if (listener.getScreenType() == JCVideoPlayer.SCREEN_WINDOW_TINY ||\n                listener.getScreenType() == JCVideoPlayer.SCREEN_WINDOW_FULLSCREEN) return;\n        int location = -1;\n        for (int i = 1; i < LISTENERLIST.size(); i++) {\n            JCMediaPlayerListener jcMediaPlayerListener = LISTENERLIST.get(i).get();\n            if (listener.getUrl().equals(jcMediaPlayerListener.getUrl())) {\n                location = i;\n            }\n        }\n        if (location != -1) {\n            LISTENERLIST.remove(location);\n            if (LISTENERLIST.size() <= location) {\n                LISTENERLIST.addLast(new WeakReference<>(listener));\n            } else {\n                LISTENERLIST.set(location, new WeakReference<>(listener));\n\n            }\n        }\n    }\n\n    public static JCMediaPlayerListener popListener() {\n        if (LISTENERLIST.size() == 0) {\n            return null;\n        }\n        return LISTENERLIST.pop().get();\n    }\n\n    public static JCMediaPlayerListener getFirst() {\n        if (LISTENERLIST.size() == 0) {\n            return null;\n        }\n        return LISTENERLIST.getFirst().get();\n    }\n\n    public static void completeAll() {\n        JCMediaPlayerListener ll = popListener();\n        while (ll != null) {\n            ll.onCompletion();\n            ll = popListener();\n        }\n    }\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCVideoPlayerSimple.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.SeekBar;\nimport android.widget.Toast;\n\n/**\n * Manage UI\n * Created by Nathen\n * On 2016/04/10 15:45\n */\npublic class JCVideoPlayerSimple extends JCVideoPlayer {\n\n    public JCVideoPlayerSimple(Context context) {\n        super(context);\n    }\n\n    public JCVideoPlayerSimple(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public int getLayoutId() {\n        return R.layout.jc_layout_base;\n    }\n\n    @Override\n    public boolean setUp(String url, int screen, Object... objects) {\n        if (super.setUp(url, screen, objects)) {\n            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n                fullscreenButton.setImageResource(R.drawable.jc_shrink);\n            } else {\n                fullscreenButton.setImageResource(R.drawable.jc_enlarge);\n            }\n            fullscreenButton.setVisibility(View.GONE);\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public void setUiWitStateAndScreen(int state) {\n        super.setUiWitStateAndScreen(state);\n        switch (currentState) {\n            case CURRENT_STATE_NORMAL:\n                startButton.setVisibility(View.VISIBLE);\n                break;\n            case CURRENT_STATE_PREPARING:\n                startButton.setVisibility(View.INVISIBLE);\n                break;\n            case CURRENT_STATE_PLAYING:\n                startButton.setVisibility(View.VISIBLE);\n                break;\n            case CURRENT_STATE_PAUSE:\n                break;\n            case CURRENT_STATE_ERROR:\n                break;\n        }\n        updateStartImage();\n    }\n\n    private void updateStartImage() {\n        if (currentState == CURRENT_STATE_PLAYING) {\n            startButton.setImageResource(R.drawable.jc_click_pause_selector);\n        } else if (currentState == CURRENT_STATE_ERROR) {\n            startButton.setImageResource(R.drawable.jc_click_error_selector);\n        } else {\n            startButton.setImageResource(R.drawable.jc_click_play_selector);\n        }\n    }\n\n    @Override\n    public void onClick(View v) {\n        if (v.getId() == R.id.fullscreen && currentState == CURRENT_STATE_NORMAL) {\n            Toast.makeText(getContext(), \"Play video first\", Toast.LENGTH_SHORT).show();\n            return;\n        }\n        super.onClick(v);\n    }\n\n    @Override\n    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n        if (fromUser) {\n            if (currentState == CURRENT_STATE_NORMAL) {\n                Toast.makeText(getContext(), \"Play video first\", Toast.LENGTH_SHORT).show();\n                return;\n            }\n        }\n        super.onProgressChanged(seekBar, progress, fromUser);\n    }\n\n    @Override\n    public boolean backToOtherListener() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/java/fm/jiecao/jcvideoplayer_lib/JCVideoPlayerStandard.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.text.TextUtils;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.ImageView;\nimport android.widget.ProgressBar;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport java.util.Timer;\nimport java.util.TimerTask;\n\n/**\n * Created by Nathen\n * On 2016/04/18 16:15\n */\npublic class JCVideoPlayerStandard extends JCVideoPlayer {\n\n    protected static Timer DISMISS_CONTROL_VIEW_TIMER;\n\n    public ImageView backButton;\n    public ProgressBar bottomProgressBar, loadingProgressBar;\n    public TextView titleTextView;\n    public ImageView thumbImageView;\n    public ImageView coverImageView;\n    public ImageView tinyBackImageView;\n\n\n    protected DismissControlViewTimerTask mDismissControlViewTimerTask;\n\n\n    public JCVideoPlayerStandard(Context context) {\n        super(context);\n    }\n\n    public JCVideoPlayerStandard(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public void init(Context context) {\n        super.init(context);\n        bottomProgressBar = (ProgressBar) findViewById(R.id.bottom_progressbar);\n        titleTextView = (TextView) findViewById(R.id.title);\n        backButton = (ImageView) findViewById(R.id.back);\n        thumbImageView = (ImageView) findViewById(R.id.thumb);\n        coverImageView = (ImageView) findViewById(R.id.cover);\n        loadingProgressBar = (ProgressBar) findViewById(R.id.loading);\n        tinyBackImageView = (ImageView) findViewById(R.id.back_tiny);\n\n        thumbImageView.setOnClickListener(this);\n        backButton.setOnClickListener(this);\n        tinyBackImageView.setOnClickListener(this);\n\n    }\n\n    @Override\n    public boolean setUp(String url, int screen, Object... objects) {\n        if (objects.length == 0) return false;\n        if (super.setUp(url, screen, objects)) {\n            titleTextView.setText(objects[0].toString());\n            if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {\n                fullscreenButton.setImageResource(R.drawable.jc_shrink);\n                backButton.setVisibility(View.VISIBLE);\n                tinyBackImageView.setVisibility(View.INVISIBLE);\n            } else if (currentScreen == SCREEN_LAYOUT_NORMAL\n                    || currentScreen == SCREEN_LAYOUT_LIST) {\n                fullscreenButton.setImageResource(R.drawable.jc_enlarge);\n                backButton.setVisibility(View.GONE);\n                tinyBackImageView.setVisibility(View.INVISIBLE);\n            } else if (currentScreen == SCREEN_WINDOW_TINY) {\n                tinyBackImageView.setVisibility(View.VISIBLE);\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public int getLayoutId() {\n        return R.layout.jc_layout_standard;\n    }\n\n    @Override\n    public void setUiWitStateAndScreen(int state) {\n        super.setUiWitStateAndScreen(state);\n        switch (currentState) {\n            case CURRENT_STATE_NORMAL:\n                changeUiToNormal();\n                break;\n            case CURRENT_STATE_PREPARING:\n                changeUiToPreparingShow();\n                startDismissControlViewTimer();\n                break;\n            case CURRENT_STATE_PLAYING:\n                changeUiToPlayingShow();\n                startDismissControlViewTimer();\n                break;\n            case CURRENT_STATE_PAUSE:\n                changeUiToPauseShow();\n                cancelDismissControlViewTimer();\n                break;\n            case CURRENT_STATE_ERROR:\n                changeUiToError();\n                break;\n            case CURRENT_STATE_AUTO_COMPLETE:\n                changeUiToCompleteShow();\n                cancelDismissControlViewTimer();\n                bottomProgressBar.setProgress(100);\n                break;\n            case CURRENT_STATE_PLAYING_BUFFERING_START:\n                changeUiToPlayingBufferingShow();\n                break;\n        }\n    }\n\n\n    @Override\n    public boolean onTouch(View v, MotionEvent event) {\n        int id = v.getId();\n        if (id == R.id.surface_container) {\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    break;\n                case MotionEvent.ACTION_MOVE:\n                    break;\n                case MotionEvent.ACTION_UP:\n                    startDismissControlViewTimer();\n                    if (mChangePosition) {\n                        int duration = getDuration();\n                        int progress = mSeekTimePosition * 100 / (duration == 0 ? 1 : duration);\n                        bottomProgressBar.setProgress(progress);\n                    }\n                    if (!mChangePosition && !mChangeVolume) {\n                        onEvent(JCUserActionStandard.ON_CLICK_BLANK);\n                        onClickUiToggle();\n                    }\n                    break;\n            }\n        } else if (id == R.id.progress) {\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    cancelDismissControlViewTimer();\n                    break;\n                case MotionEvent.ACTION_UP:\n                    startDismissControlViewTimer();\n                    break;\n            }\n        }\n        return super.onTouch(v, event);\n    }\n\n    @Override\n    public void onClick(View v) {\n        super.onClick(v);\n        int i = v.getId();\n        if (i == R.id.thumb) {\n            if (TextUtils.isEmpty(url)) {\n                Toast.makeText(getContext(), getResources().getString(R.string.no_url), Toast.LENGTH_SHORT).show();\n                return;\n            }\n            if (currentState == CURRENT_STATE_NORMAL) {\n                if (!url.startsWith(\"file\") && !JCUtils.isWifiConnected(getContext()) && !WIFI_TIP_DIALOG_SHOWED) {\n                    showWifiDialog();\n                    return;\n                }\n                startPlayLogic();\n            } else if (currentState == CURRENT_STATE_AUTO_COMPLETE) {\n                onClickUiToggle();\n            }\n        } else if (i == R.id.surface_container) {\n            startDismissControlViewTimer();\n        } else if (i == R.id.back) {\n            backPress();\n        } else if (i == R.id.back_tiny) {\n            if (JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get() != null) {\n                if (JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get().getUrl() != JCMediaManager.instance().mediaPlayer.getDataSource()) {\n//                    if (!((JCVideoPlayer) JCVideoPlayerManager.CURRENT_SCROLL_LISTENER.get(0).get()).isShown()) {\n                    releaseAllVideos();\n                    return;\n//                    }\n                }\n            }\n            backPress();\n        }\n    }\n\n\n    @Override\n    public void showWifiDialog() {\n        super.showWifiDialog();\n        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());\n        builder.setMessage(getResources().getString(R.string.tips_not_wifi));\n        builder.setPositiveButton(getResources().getString(R.string.tips_not_wifi_confirm), new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                dialog.dismiss();\n                startPlayLogic();\n                WIFI_TIP_DIALOG_SHOWED = true;\n            }\n        });\n        builder.setNegativeButton(getResources().getString(R.string.tips_not_wifi_cancel), new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                dialog.dismiss();\n            }\n        });\n        builder.create().show();\n    }\n\n    @Override\n    public void onStartTrackingTouch(SeekBar seekBar) {\n        super.onStartTrackingTouch(seekBar);\n        cancelDismissControlViewTimer();\n    }\n\n    @Override\n    public void onStopTrackingTouch(SeekBar seekBar) {\n        super.onStopTrackingTouch(seekBar);\n        startDismissControlViewTimer();\n    }\n\n    public void startPlayLogic() {\n        prepareVideo();\n        onEvent(JCUserActionStandard.ON_CLICK_START_THUMB);\n    }\n\n    public void onClickUiToggle() {\n        if (currentState == CURRENT_STATE_PREPARING) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPreparingClear();\n            } else {\n                changeUiToPreparingShow();\n            }\n        } else if (currentState == CURRENT_STATE_PLAYING) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPlayingClear();\n            } else {\n                changeUiToPlayingShow();\n            }\n        } else if (currentState == CURRENT_STATE_PAUSE) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPauseClear();\n            } else {\n                changeUiToPauseShow();\n            }\n        } else if (currentState == CURRENT_STATE_AUTO_COMPLETE) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToCompleteClear();\n            } else {\n                changeUiToCompleteShow();\n            }\n        } else if (currentState == CURRENT_STATE_PLAYING_BUFFERING_START) {\n            if (bottomContainer.getVisibility() == View.VISIBLE) {\n                changeUiToPlayingBufferingClear();\n            } else {\n                changeUiToPlayingBufferingShow();\n            }\n        }\n    }\n\n    @Override\n    public void setProgressAndTime(int progress, int secProgress, int currentTime, int totalTime) {\n        super.setProgressAndTime(progress, secProgress, currentTime, totalTime);\n        if (progress != 0) bottomProgressBar.setProgress(progress);\n        if (secProgress != 0) bottomProgressBar.setSecondaryProgress(secProgress);\n    }\n\n    @Override\n    public void resetProgressAndTime() {\n        super.resetProgressAndTime();\n        bottomProgressBar.setProgress(0);\n        bottomProgressBar.setSecondaryProgress(0);\n    }\n\n    //Unified management Ui\n    public void changeUiToNormal() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n    }\n\n    public void changeUiToPreparingShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToPreparingClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.VISIBLE, View.VISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    //JustPreparedUi\n    @Override\n    public void onPrepared() {\n        super.onPrepared();\n        setAllControlsVisible(View.VISIBLE, View.INVISIBLE, View.INVISIBLE,\n                View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE);\n        startDismissControlViewTimer();\n    }\n\n    public void changeUiToPlayingShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToPlayingClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToPauseShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToPauseClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToPlayingBufferingShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE);\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToPlayingBufferingClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE,\n                        View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToCompleteShow() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.VISIBLE, View.VISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToCompleteClear() {\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.VISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void changeUiToError() {\n        clearCacheImage();\n        switch (currentScreen) {\n            case SCREEN_LAYOUT_NORMAL:\n            case SCREEN_LAYOUT_LIST:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_FULLSCREEN:\n                setAllControlsVisible(View.INVISIBLE, View.INVISIBLE, View.VISIBLE,\n                        View.INVISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE);\n                updateStartImage();\n                break;\n            case SCREEN_WINDOW_TINY:\n                break;\n        }\n\n    }\n\n    public void setAllControlsVisible(int topCon, int bottomCon, int startBtn, int loadingPro,\n                                      int thumbImg, int coverImg, int bottomPro) {\n        topContainer.setVisibility(topCon);\n        bottomContainer.setVisibility(bottomCon);\n        startButton.setVisibility(startBtn);\n        loadingProgressBar.setVisibility(loadingPro);\n        if (thumbImg == View.VISIBLE) {\n            thumbImageView.setVisibility(thumbImg);\n        } else {\n            thumbImageView.setVisibility(View.GONE);\n        }\n        coverImageView.setVisibility(coverImg);\n        bottomProgressBar.setVisibility(bottomPro);\n    }\n\n    public void updateStartImage() {\n        if (currentState == CURRENT_STATE_PLAYING) {\n            startButton.setImageResource(R.drawable.jc_click_pause_selector);\n        } else if (currentState == CURRENT_STATE_ERROR) {\n            startButton.setImageResource(R.drawable.jc_click_error_selector);\n        } else {\n            startButton.setImageResource(R.drawable.jc_click_play_selector);\n        }\n    }\n\n\n    protected Dialog mProgressDialog;\n    protected ProgressBar mDialogProgressBar;\n    protected TextView mDialogSeekTime;\n    protected TextView mDialogTotalTime;\n    protected ImageView mDialogIcon;\n\n    @Override\n    public void showProgressDialog(float deltaX, String seekTime, int seekTimePosition, String totalTime, int totalTimeDuration) {\n        super.showProgressDialog(deltaX, seekTime, seekTimePosition, totalTime, totalTimeDuration);\n        if (mProgressDialog == null) {\n            View localView = LayoutInflater.from(getContext()).inflate(R.layout.jc_progress_dialog, null);\n            mDialogProgressBar = ((ProgressBar) localView.findViewById(R.id.duration_progressbar));\n            mDialogSeekTime = ((TextView) localView.findViewById(R.id.tv_current));\n            mDialogTotalTime = ((TextView) localView.findViewById(R.id.tv_duration));\n            mDialogIcon = ((ImageView) localView.findViewById(R.id.duration_image_tip));\n            mProgressDialog = new Dialog(getContext(), R.style.jc_style_dialog_progress);\n            mProgressDialog.setContentView(localView);\n            mProgressDialog.getWindow().addFlags(Window.FEATURE_ACTION_BAR);\n            mProgressDialog.getWindow().addFlags(32);\n            mProgressDialog.getWindow().addFlags(16);\n            mProgressDialog.getWindow().setLayout(-2, -2);\n            WindowManager.LayoutParams localLayoutParams = mProgressDialog.getWindow().getAttributes();\n            localLayoutParams.gravity = 49;\n            localLayoutParams.y = getResources().getDimensionPixelOffset(fm.jiecao.jcvideoplayer_lib.R.dimen.jc_progress_dialog_margin_top);\n            mProgressDialog.getWindow().setAttributes(localLayoutParams);\n        }\n        if (!mProgressDialog.isShowing()) {\n            mProgressDialog.show();\n        }\n\n        mDialogSeekTime.setText(seekTime);\n        mDialogTotalTime.setText(\" / \" + totalTime);\n        mDialogProgressBar.setProgress(totalTimeDuration <= 0 ? 0 : (seekTimePosition * 100 / totalTimeDuration));\n        if (deltaX > 0) {\n            mDialogIcon.setBackgroundResource(R.drawable.jc_forward_icon);\n        } else {\n            mDialogIcon.setBackgroundResource(R.drawable.jc_backward_icon);\n        }\n\n    }\n\n    @Override\n    public void dismissProgressDialog() {\n        super.dismissProgressDialog();\n        if (mProgressDialog != null) {\n            mProgressDialog.dismiss();\n        }\n    }\n\n\n    protected Dialog mVolumeDialog;\n    protected ProgressBar mDialogVolumeProgressBar;\n\n    @Override\n    public void showVolumeDialog(float deltaY, int volumePercent) {\n        super.showVolumeDialog(deltaY, volumePercent);\n        if (mVolumeDialog == null) {\n            View localView = LayoutInflater.from(getContext()).inflate(R.layout.jc_volume_dialog, null);\n            mDialogVolumeProgressBar = ((ProgressBar) localView.findViewById(R.id.volume_progressbar));\n            mVolumeDialog = new Dialog(getContext(), R.style.jc_style_dialog_progress);\n            mVolumeDialog.setContentView(localView);\n            mVolumeDialog.getWindow().addFlags(8);\n            mVolumeDialog.getWindow().addFlags(32);\n            mVolumeDialog.getWindow().addFlags(16);\n            mVolumeDialog.getWindow().setLayout(-2, -2);\n            WindowManager.LayoutParams localLayoutParams = mVolumeDialog.getWindow().getAttributes();\n            localLayoutParams.gravity = 19;\n            localLayoutParams.x = getContext().getResources().getDimensionPixelOffset(R.dimen.jc_volume_dialog_margin_left);\n            mVolumeDialog.getWindow().setAttributes(localLayoutParams);\n        }\n        if (!mVolumeDialog.isShowing()) {\n            mVolumeDialog.show();\n        }\n\n        mDialogVolumeProgressBar.setProgress(volumePercent);\n    }\n\n    @Override\n    public void dismissVolumeDialog() {\n        super.dismissVolumeDialog();\n        if (mVolumeDialog != null) {\n            mVolumeDialog.dismiss();\n        }\n    }\n\n    public void startDismissControlViewTimer() {\n        cancelDismissControlViewTimer();\n        DISMISS_CONTROL_VIEW_TIMER = new Timer();\n        mDismissControlViewTimerTask = new DismissControlViewTimerTask();\n        DISMISS_CONTROL_VIEW_TIMER.schedule(mDismissControlViewTimerTask, 2500);\n    }\n\n    public void cancelDismissControlViewTimer() {\n        if (DISMISS_CONTROL_VIEW_TIMER != null) {\n            DISMISS_CONTROL_VIEW_TIMER.cancel();\n        }\n        if (mDismissControlViewTimerTask != null) {\n            mDismissControlViewTimerTask.cancel();\n        }\n\n    }\n\n    public class DismissControlViewTimerTask extends TimerTask {\n\n        @Override\n        public void run() {\n            if (currentState != CURRENT_STATE_NORMAL\n                    && currentState != CURRENT_STATE_ERROR\n                    && currentState != CURRENT_STATE_AUTO_COMPLETE) {\n                if (getContext() != null && getContext() instanceof Activity) {\n                    ((Activity) getContext()).runOnUiThread(new Runnable() {\n                        @Override\n                        public void run() {\n                            bottomContainer.setVisibility(View.INVISIBLE);\n                            topContainer.setVisibility(View.INVISIBLE);\n                            startButton.setVisibility(View.INVISIBLE);\n                            if (currentScreen != SCREEN_WINDOW_TINY) {\n                                bottomProgressBar.setVisibility(View.VISIBLE);\n                            }\n                        }\n                    });\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/anim/quit_fullscreen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@android:anim/linear_interpolator\">\n    <rotate\n        android:duration=\"5\"\n        android:fromDegrees=\"0\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toDegrees=\"-2\" />\n</set>"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/anim/start_fullscreen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@android:anim/linear_interpolator\">\n    <rotate\n        android:duration=\"20\"\n        android:fromDegrees=\"-2\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"50%\"\n        android:toDegrees=\"0\" />\n</set>"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_click_back_tiny_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/jc_back_tiny_normal\" android:state_pressed=\"false\" />\n    <item android:drawable=\"@drawable/jc_back_tiny_pressed\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/jc_back_tiny_normal\" />\n</selector>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_click_error_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/jc_error_normal\" android:state_pressed=\"false\" />\n    <item android:drawable=\"@drawable/jc_error_pressed\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/jc_error_normal\" />\n</selector>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_click_pause_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/jc_pause_normal\" android:state_pressed=\"false\" />\n    <item android:drawable=\"@drawable/jc_pause_pressed\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/jc_pause_normal\" />\n\n</selector>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_click_play_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/jc_play_normal\" android:state_pressed=\"false\" />\n    <item android:drawable=\"@drawable/jc_play_pressed\" android:state_pressed=\"true\" />\n    <item android:drawable=\"@drawable/jc_play_normal\" />\n</selector>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_dialog_progress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:id=\"@android:id/background\">\n        <shape>\n            <solid android:color=\"#ffffffff\" />\n            <corners android:radius=\"2.0dip\" />\n        </shape>\n    </item>\n    <item android:id=\"@android:id/progress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#fff85959\" />\n                <corners android:radius=\"2.0dip\" />\n            </shape>\n        </clip>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_dialog_progress_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"#4c000000\" />\n    <corners\n        android:bottomLeftRadius=\"6.0dip\"\n        android:bottomRightRadius=\"6.0dip\"\n        android:topLeftRadius=\"6.0dip\"\n        android:topRightRadius=\"6.0dip\" />\n</shape>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rotate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:drawable=\"@drawable/jc_loading_bg\"\n    android:fromDegrees=\"0.0\"\n    android:pivotX=\"50.0%\"\n    android:pivotY=\"50.0%\"\n    android:toDegrees=\"360.0\" />\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_progress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:id=\"@android:id/background\">\n        <shape>\n            <solid android:color=\"#4c000000\" />\n            <size android:height=\"4.0dip\" />\n            <corners android:radius=\"1.0dip\" />\n        </shape>\n    </item>\n    <item android:id=\"@android:id/secondaryProgress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#ffe0e0e0\" />\n                <size android:height=\"4.0dip\" />\n                <corners android:radius=\"1.0dip\" />\n            </shape>\n        </clip>\n    </item>\n    <item android:id=\"@android:id/progress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#fff85959\" />\n                <size android:height=\"4.0dip\" />\n                <corners android:radius=\"1.0dip\" />\n            </shape>\n        </clip>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_seek_progress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:id=\"@android:id/background\">\n        <shape>\n            <solid android:color=\"#cc999999\" />\n            <size android:height=\"3dp\" />\n            <corners android:radius=\"1.5dp\" />\n        </shape>\n    </item>\n    <item android:id=\"@android:id/secondaryProgress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#ffe0e0e0\" />\n                <size android:height=\"3dp\" />\n                <corners android:radius=\"1.5dp\" />\n            </shape>\n        </clip>\n    </item>\n    <item android:id=\"@android:id/progress\">\n        <clip>\n            <shape>\n                <solid android:color=\"#fff85959\" />\n                <size android:height=\"3dp\" />\n                <corners android:radius=\"1.5dp\" />\n            </shape>\n        </clip>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_seek_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:drawable=\"@drawable/jc_seek_thumb_pressed\" />\n    <item android:drawable=\"@drawable/jc_seek_thumb_normal\" />\n</selector>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_seek_thumb_normal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape android:shape=\"oval\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"#ffffffff\" />\n    <size\n        android:height=\"14.0dip\"\n        android:width=\"14.0dip\" />\n</shape>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_seek_thumb_pressed.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape android:shape=\"oval\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"#fff0f0f0\" />\n    <size\n        android:height=\"14.0dip\"\n        android:width=\"14.0dip\" />\n</shape>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_title_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <gradient\n        android:angle=\"90\"\n        android:endColor=\"#99000000\"\n        android:startColor=\"#00000000\" />\n</shape>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/drawable/jc_volume_progress_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:id=\"@android:id/background\">\n        <shape>\n            <solid android:color=\"#ffffffff\" />\n            <corners android:radius=\"2.0dip\" />\n        </shape>\n    </item>\n    <item android:id=\"@android:id/progress\">\n        <clip\n            android:clipOrientation=\"vertical\"\n            android:gravity=\"bottom\">\n            <shape>\n                <solid android:color=\"#fff85959\" />\n                <corners android:radius=\"2.0dip\" />\n            </shape>\n        </clip>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/layout/jc_layout_base.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/black\">\n\n    <FrameLayout\n        android:id=\"@+id/surface_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n    </FrameLayout>\n\n    <fm.jiecao.jcvideoplayer_lib.JCResizeImageView\n        android:id=\"@+id/cache\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\" />\n\n    <LinearLayout\n        android:id=\"@+id/layout_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"#99000000\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/current\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <SeekBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_weight=\"1.0\"\n            android:background=\"@null\"\n            android:max=\"100\"\n            android:maxHeight=\"4dp\"\n            android:minHeight=\"4dp\"\n            android:paddingBottom=\"8dp\"\n            android:paddingTop=\"8dp\"\n            android:progressDrawable=\"@drawable/jc_seek_progress\"\n            android:thumb=\"@drawable/jc_seek_thumb\" />\n\n        <TextView\n            android:id=\"@+id/total\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <ImageView\n            android:id=\"@+id/fullscreen\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"fill_parent\"\n            android:paddingRight=\"16dp\"\n            android:scaleType=\"center\"\n            android:src=\"@drawable/jc_enlarge\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:id=\"@+id/layout_top\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:background=\"@drawable/jc_title_bg\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/loading\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:indeterminateDrawable=\"@drawable/jc_loading\"\n        android:visibility=\"invisible\" />\n\n    <ImageView\n        android:id=\"@+id/start\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_gravity=\"center_vertical\"\n        android:src=\"@drawable/jc_click_play_selector\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/layout/jc_layout_standard.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/black\">\n\n    <FrameLayout\n        android:id=\"@+id/surface_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n    </FrameLayout>\n\n    <fm.jiecao.jcvideoplayer_lib.JCResizeImageView\n        android:id=\"@+id/cache\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_centerInParent=\"true\" />\n\n    <ImageView\n        android:id=\"@+id/cover\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentStart=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"#222222\" />\n\n    <ImageView\n        android:id=\"@+id/thumb\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentStart=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:background=\"#000000\"\n        android:scaleType=\"fitCenter\" />\n\n    <LinearLayout\n        android:id=\"@+id/layout_bottom\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"40dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:background=\"#99000000\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\"\n        android:visibility=\"invisible\">\n\n        <TextView\n            android:id=\"@+id/current\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <SeekBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_vertical\"\n            android:layout_weight=\"1.0\"\n            android:background=\"@null\"\n            android:max=\"100\"\n            android:maxHeight=\"4dp\"\n            android:minHeight=\"4dp\"\n            android:paddingBottom=\"8dp\"\n            android:paddingTop=\"8dp\"\n            android:progressDrawable=\"@drawable/jc_seek_progress\"\n            android:thumb=\"@drawable/jc_seek_thumb\" />\n\n        <TextView\n            android:id=\"@+id/total\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"16dp\"\n            android:text=\"00:00\"\n            android:textColor=\"#ffffff\" />\n\n        <ImageView\n            android:id=\"@+id/fullscreen\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"fill_parent\"\n            android:paddingRight=\"16dp\"\n            android:scaleType=\"center\"\n            android:src=\"@drawable/jc_enlarge\" />\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/bottom_progressbar\"\n        style=\"?android:attr/progressBarStyleHorizontal\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1.5dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:max=\"100\"\n        android:progressDrawable=\"@drawable/jc_progress\" />\n\n    <ImageView\n        android:id=\"@+id/back_tiny\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_marginLeft=\"6dp\"\n        android:layout_marginTop=\"6dp\"\n        android:background=\"@drawable/jc_click_back_tiny_selector\"\n        android:visibility=\"visible\" />\n\n    <LinearLayout\n        android:id=\"@+id/layout_top\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:background=\"@drawable/jc_title_bg\"\n        android:gravity=\"center_vertical\">\n\n        <ImageView\n            android:id=\"@+id/back\"\n            android:layout_width=\"48dp\"\n            android:layout_height=\"48dp\"\n            android:paddingLeft=\"10dp\"\n            android:scaleType=\"centerInside\"\n            android:src=\"@drawable/jc_back\" />\n\n        <TextView\n            android:id=\"@+id/title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:paddingLeft=\"10dp\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"18sp\" />\n    </LinearLayout>\n\n    <ProgressBar\n        android:id=\"@+id/loading\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:indeterminateDrawable=\"@drawable/jc_loading\"\n        android:visibility=\"invisible\" />\n\n    <ImageView\n        android:id=\"@+id/start\"\n        android:layout_width=\"60dp\"\n        android:layout_height=\"60dp\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_gravity=\"center_vertical\"\n        android:src=\"@drawable/jc_click_play_selector\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/layout/jc_progress_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/jc_dialog_progress_bg\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:layout_width=\"152dp\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <ImageView\n            android:id=\"@+id/duration_image_tip\"\n            android:layout_width=\"36dp\"\n            android:layout_height=\"27dp\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginTop=\"16dp\" />\n\n        <LinearLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginTop=\"20dp\"\n            android:gravity=\"center_horizontal\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/tv_current\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"#fff85959\"\n                android:textSize=\"14.0sp\" />\n\n            <TextView\n                android:id=\"@+id/tv_duration\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:textColor=\"#ffffffff\"\n                android:textSize=\"14.0sp\" />\n        </LinearLayout>\n\n        <ProgressBar\n            android:id=\"@+id/duration_progressbar\"\n            style=\"@android:style/Widget.ProgressBar.Horizontal\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"4dp\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginBottom=\"16dp\"\n            android:layout_marginLeft=\"16dp\"\n            android:layout_marginRight=\"16dp\"\n            android:layout_marginTop=\"8dp\"\n            android:max=\"100\"\n            android:progressDrawable=\"@drawable/jc_dialog_progress\" />\n    </LinearLayout>\n</LinearLayout>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/layout/jc_volume_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/jc_dialog_progress_bg\"\n    android:orientation=\"vertical\">\n\n    <LinearLayout\n        android:layout_width=\"40.0dip\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\">\n\n        <ProgressBar\n            android:id=\"@+id/volume_progressbar\"\n            style=\"@style/jc_vertical_progressBar\"\n            android:layout_width=\"4.0dip\"\n            android:layout_height=\"81.0dip\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginTop=\"16.0dip\"\n            android:max=\"100\" />\n\n        <ImageView\n            android:layout_width=\"24dp\"\n            android:layout_height=\"24dp\"\n            android:layout_gravity=\"center_horizontal\"\n            android:layout_marginBottom=\"16.0dip\"\n            android:layout_marginTop=\"16.0dip\"\n            android:src=\"@drawable/jc_volume_icon\" />\n    </LinearLayout>\n</LinearLayout>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources></resources>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <dimen name=\"jc_progress_dialog_margin_top\">80dp</dimen>\n    <dimen name=\"jc_volume_dialog_margin_left\">24dp</dimen>\n</resources>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"tips_not_wifi\">You are currently using the mobile network, the player will continue to consume traffic</string>\n    <string name=\"tips_not_wifi_confirm\">Resume</string>\n    <string name=\"tips_not_wifi_cancel\">Stop play</string>\n    <string name=\"no_url\">No mUrl</string>\n</resources>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"jc_style_dialog_progress\" parent=\"@android:style/Theme.Dialog\">\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowAnimationStyle\">@style/jc_popup_toast_anim</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n    </style>\n\n    <style name=\"jc_popup_toast_anim\" parent=\"@android:style/Animation\">\n        <item name=\"android:windowEnterAnimation\">@android:anim/fade_in</item>\n        <item name=\"android:windowExitAnimation\">@android:anim/fade_out</item>\n    </style>\n\n    <style name=\"jc_vertical_progressBar\">\n        <item name=\"android:maxWidth\">12dp</item>\n        <item name=\"android:indeterminateOnly\">false</item>\n        <item name=\"android:indeterminateDrawable\">\n            @android:drawable/progress_indeterminate_horizontal\n        </item>\n        <item name=\"android:progressDrawable\">@drawable/jc_volume_progress_bg</item>\n        <item name=\"android:indeterminateDuration\">3500</item>\n        <item name=\"android:indeterminateBehavior\">repeat</item>\n        <item name=\"android:minWidth\">1dp</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"tips_not_wifi\">Você está usando a rede móvel, você deseja mesmo ver o video?</string>\n    <string name=\"tips_not_wifi_confirm\">Continuar</string>\n    <string name=\"tips_not_wifi_cancel\">Parar</string>\n    <string name=\"no_url\">Sem Vídeo</string>\n</resources>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"tips_not_wifi\">Şu anda mobil veriyi kullanıyorsunuz, yüksek veri kaybına yol açabilir</string>\n    <string name=\"tips_not_wifi_confirm\">Devam Et</string>\n    <string name=\"tips_not_wifi_cancel\">Durdur</string>\n    <string name=\"no_url\">URL Bulunamadı</string>\n</resources>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"tips_not_wifi\">您当前正在使用移动网络，继续播放将消耗流量</string>\n    <string name=\"tips_not_wifi_confirm\">继续播放</string>\n    <string name=\"tips_not_wifi_cancel\">停止播放</string>\n    <string name=\"no_url\">播放地址无效</string>\n</resources>\n"
  },
  {
    "path": "jcvideoplayer-lib/src/test/java/fm/jiecao/jcvideoplayer_lib/ExampleUnitTest.java",
    "content": "package fm.jiecao.jcvideoplayer_lib;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app', ':StartAllVideoPlayer', ':vitamio-sample', ':vitamio', ':xutils', ':xUtils3-master', ':Android-MaterialRefreshLayout-master', ':MaterialRefresh_library', ':AgoraDemo', ':SurfaceViewDemo', ':binderService', ':EventBus', ':EventBus3.0_Sample', ':Android应用源码音乐实时跳动频谱显示Demo', ':SpeechDemo2', ':voicedialog', ':jcvideoplayer-lib', ':JieCaoVideoPlayer-develop', ':PhotoView-master', ':PhotoView_library', ':RecyclerViewDemo'\ninclude ':speechDemo'\n"
  },
  {
    "path": "speechDemo/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.3\"\n\n    defaultConfig {\n        applicationId \"com.iflytek.voicedemo\"\n        minSdkVersion 14\n        targetSdkVersion 23\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n        }\n    }\n}\n\ndependencies {\n    compile 'com.android.support:support-v4:23.3.0'\n    compile files('libs/Msc.jar')\n    compile files('libs/Sunflower.jar')\n}\n"
  },
  {
    "path": "speechDemo/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<lint>\n</lint>"
  },
  {
    "path": "speechDemo/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.iflytek.voicedemo\"\n    android:versionCode=\"1\"\n    android:versionName=\"2.0.1018.1013\" >\n\n    <application\n        android:icon=\"@drawable/icon\"\n        android:name=\"SpeechApp\"\n        android:label=\"讯飞语音示例\" >\n        <activity\n            android:name=\"com.iflytek.voicedemo.MainActivity\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale\"\n            android:icon=\"@drawable/icon\"\n            android:label=\"讯飞语音示例\"\n            android:screenOrientation=\"portrait\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity android:name=\"com.iflytek.speech.setting.TtsSettings\" >\n        </activity>\n        <activity android:name=\"com.iflytek.speech.setting.IatSettings\" >\n        </activity>\n        <activity android:name=\"com.iflytek.speech.setting.UnderstanderSettings\" >\n        </activity>\n        <activity\n            android:name=\"com.iflytek.voicedemo.IatDemo\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"com.iflytek.voicedemo.AsrDemo\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"com.iflytek.voicedemo.UnderstanderDemo\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"com.iflytek.voicedemo.TtsDemo\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"com.iflytek.voicedemo.IseDemo\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <activity\n            android:name=\"com.iflytek.speech.setting.IseSettings\"\n            android:configChanges=\"mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|fontScale\"\n            android:screenOrientation=\"portrait\" >\n        </activity>\n        <!-- 移动统计分析 -->\n        <meta-data\n            android:name=\"IFLYTEK_APPKEY\"\n            android:value=\"'5838f0d9'\" />\n        <meta-data\n            android:name=\"IFLYTEK_CHANNEL\"\n            android:value=\"Android_Demo\" />\n\n\n    </application>\n\n    <uses-sdk android:minSdkVersion=\"8\" />\n\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.READ_CONTACTS\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\t<uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />\n\t<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n\n</manifest>"
  },
  {
    "path": "speechDemo/src/main/assets/call.bnf",
    "content": "#BNF+IAT 1.0 UTF-8;\n!grammar call;\n!slot <contact>;\n!slot <callPre>;\n!slot <callPhone>;\n!slot <callTo>;\n!start <callStart>;\n<callStart>:[<callPre>][<callTo>]<contact><callPhone>|[<callPre>]<callPhone>[<callTo>]<contact>;\n<contact>:张海洋;\n<callPre>:我要|我想|我想要;\n<callPhone>:打电话;\n<callTo>:给;"
  },
  {
    "path": "speechDemo/src/main/assets/grammar_sample.abnf",
    "content": "#ABNF 1.0 UTF-8;\nlanguage zh-CN; \nmode voice;\n\nroot $main;\n$main = $place1 到 $place2;\n$place1 = 北京|武汉|南京|天津|东京;\n$place2 = 上海|合肥;"
  },
  {
    "path": "speechDemo/src/main/assets/userwords",
    "content": "{\"userword\":[{\"name\":\"我的常用词\",\"words\":[\"佳晨实业\",\"蜀南庭苑\",\"高兰路\",\"复联二\"]},{\"name\":\"我的好友\",\"words\":[\"李馨琪\",\"鹿晓雷\",\"张集栋\",\"周家莉\",\"叶震珂\",\"熊泽萌\"]}]}"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/FinalResult.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result;\n\n/**\n * <p>Title: FinalResult</p>\n * <p>Description: </p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月14日 上午11:12:58\n */\npublic class FinalResult extends Result {\n\t\n\tpublic int ret;\n\t\n\tpublic float total_score;\n\t\n\t@Override\n\tpublic String toString() {\n\t\treturn \"返回值：\" + ret + \"，总分：\" + total_score;\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/ReadSentenceResult.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result;\n\nimport com.iflytek.ise.result.util.ResultFormatUtil;\n\n/**\n * <p>Title: ReadSentenceResult</p>\n * <p>Description: </p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午5:04:14\n */\npublic class ReadSentenceResult extends Result {\n\t\n\tpublic ReadSentenceResult() {\n\t\tcategory = \"read_sentence\";\n\t}\n\t\n\t@Override\n\tpublic String toString() {\n\t\tStringBuffer buffer = new StringBuffer();\n\t\t\n\t\tif (\"cn\".equals(language)) {\n\t\t\tbuffer.append(\"[总体结果]\\n\")\n\t\t\t\t.append(\"评测内容：\" + content + \"\\n\")\n\t\t\t\t.append(\"朗读时长：\" + time_len + \"\\n\")\n\t\t\t\t.append(\"总分：\" + total_score + \"\\n\\n\")\n\t\t\t\t.append(\"[朗读详情]\").append(ResultFormatUtil.formatDetails_CN(sentences));\n\t\t} else {\n\t\t\tif (is_rejected) {\n\t\t\t\tbuffer.append(\"检测到乱读，\")\n\t\t\t\t\t.append(\"except_info:\" + except_info + \"\\n\\n\");\t// except_info代码说明详见《语音评测参数、结果说明文档》\n\t\t\t}\n\t\t\t\n\t\t\tbuffer.append(\"[总体结果]\\n\")\n\t\t\t\t.append(\"评测内容：\" + content + \"\\n\")\n\t\t\t\t.append(\"总分：\" + total_score + \"\\n\\n\")\n\t\t\t\t.append(\"[朗读详情]\").append(ResultFormatUtil.formatDetails_EN(sentences));\n\t\t}\n\t\t\n\t\treturn buffer.toString();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/ReadSyllableResult.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result;\n\nimport com.iflytek.ise.result.util.ResultFormatUtil;\n\n/**\n * <p>Title: ReadSyllableResult</p>\n * <p>Description: 中文单字评测结果</p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午5:03:14\n */\npublic class ReadSyllableResult extends Result {\n\t\n\tpublic ReadSyllableResult() {\n\t\tlanguage = \"cn\";\n\t\tcategory = \"read_syllable\";\n\t}\n\t\n\t@Override\n\tpublic String toString() {\n\t\tStringBuffer buffer = new StringBuffer();\n\t\tbuffer.append(\"[总体结果]\\n\")\n\t\t\t.append(\"评测内容：\" + content + \"\\n\")\n\t\t\t.append(\"朗读时长：\" + time_len + \"\\n\")\n\t\t\t.append(\"总分：\" + total_score + \"\\n\\n\")\n\t\t\t.append(\"[朗读详情]\").append(ResultFormatUtil.formatDetails_CN(sentences));\n\t\t\n\t\treturn buffer.toString();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/ReadWordResult.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result;\n\nimport com.iflytek.ise.result.util.ResultFormatUtil;\n\n/**\n * <p>Title: ReadWordResult</p>\n * <p>Description: </p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午5:03:50\n */\npublic class ReadWordResult extends Result {\n\t\n\tpublic ReadWordResult() {\n\t\tcategory = \"read_word\";\n\t}\n\t\n\t@Override\n\tpublic String toString() {\n\t\tStringBuffer buffer = new StringBuffer();\n\t\t\n\t\tif (\"cn\".equals(language)) {\n\t\t\tbuffer.append(\"[总体结果]\\n\")\n\t\t\t\t.append(\"评测内容：\" + content + \"\\n\")\n\t\t\t\t.append(\"朗读时长：\" + time_len + \"\\n\")\n\t\t\t\t.append(\"总分：\" + total_score + \"\\n\\n\")\n\t\t\t\t.append(\"[朗读详情]\")\n\t\t\t\t.append(ResultFormatUtil.formatDetails_CN(sentences));\n\t\t} else {\n\t\t\tif (is_rejected) {\n\t\t\t\tbuffer.append(\"检测到乱读，\")\n\t\t\t\t.append(\"except_info:\" + except_info + \"\\n\\n\");\t// except_info代码说明详见《语音评测参数、结果说明文档》\n\t\t\t}\n\t\t\t\n\t\t\tbuffer.append(\"[总体结果]\\n\")\n\t\t\t\t.append(\"评测内容：\" + content + \"\\n\")\n\t\t\t\t.append(\"总分：\" + total_score + \"\\n\\n\")\n\t\t\t\t.append(\"[朗读详情]\")\n\t\t\t\t.append(ResultFormatUtil.formatDetails_EN(sentences));\n\t\t}\n\t\t\n\t\treturn buffer.toString();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/Result.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result;\n\nimport java.util.ArrayList;\n\nimport com.iflytek.ise.result.entity.Sentence;\n\n/**\n * <p>Title: Result</p>\n * <p>Description: 评测结果</p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午4:58:38\n */\npublic class Result {\n\t/**\n\t * 评测语种：en（英文）、cn（中文）\n\t */\n\tpublic String language;\n\t/**\n\t * 评测种类：read_syllable（cn单字）、read_word（词语）、read_sentence（句子） \n\t */\n\tpublic String category;\n\t/**\n\t * 开始帧位置，每帧相当于10ms\n\t */\n\tpublic int beg_pos;\n\t/**\n\t * 结束帧位置\n\t */\n\tpublic int end_pos;\n\t/**\n\t * 评测内容\n\t */\n\tpublic String content;\n\t/**\n\t * 总得分\n\t */\n\tpublic float total_score;\n\t/**\n\t * 时长（cn）\n\t */\n\tpublic int time_len;\n\t/**\n\t * 异常信息（en）\n\t */\n\tpublic String except_info;\n\t/**\n\t * 是否乱读（cn）\n\t */\n\tpublic boolean is_rejected;\n\t/**\n\t * xml结果中的sentence标签\n\t */\n\tpublic ArrayList<Sentence> sentences;\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/entity/Phone.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result.entity;\n\nimport java.util.HashMap;\n\n/**\n * <p>Title: Phone</p>\n * <p>Description: 音素，对应于xml结果中的Phone标签</p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午3:55:56\n */\npublic class Phone {\n\t/**\n\t * 讯飞音标-标准音标映射表（en）\n\t */\n\tpublic static HashMap<String, String> phone_map = new HashMap<String, String>();\n\t\n\tstatic {\n\t\tphone_map.put(\"aa\", \"ɑ:\");\n\t\tphone_map.put(\"oo\", \"ɔ\");\n\t\tphone_map.put(\"ae\", \"æ\");\n\t\tphone_map.put(\"ah\", \"ʌ\");\n\t\tphone_map.put(\"ao\", \"ɔ:\");\n\t\tphone_map.put(\"aw\", \"aʊ\");\n\t\tphone_map.put(\"ax\", \"ə\");\n\t\tphone_map.put(\"ay\", \"aɪ\");\n\t\tphone_map.put(\"eh\", \"e\");\n\t\tphone_map.put(\"er\", \"ə:\");\n\t\tphone_map.put(\"ey\", \"eɪ\");\n\t\tphone_map.put(\"ih\", \"ɪ\");\n\t\tphone_map.put(\"iy\", \"i:\");\n\t\tphone_map.put(\"ow\", \"əʊ\");\n\t\tphone_map.put(\"oy\", \"ɔɪ\");\n\t\tphone_map.put(\"uh\", \"ʊ\");\n\t\tphone_map.put(\"uw\", \"ʊ:\");\n\t\tphone_map.put(\"ch\", \"tʃ\");\n\t\tphone_map.put(\"dh\", \"ð\");\n\t\tphone_map.put(\"hh\", \"h\");\n\t\tphone_map.put(\"jh\", \"dʒ\");\n\t\tphone_map.put(\"ng\", \"ŋ\");\n\t\tphone_map.put(\"sh\", \"ʃ\");\n\t\tphone_map.put(\"th\", \"θ\");\n\t\tphone_map.put(\"zh\", \"ʒ\");\n\t\tphone_map.put(\"y\", \"j\");\n\t\tphone_map.put(\"d\", \"d\");\n\t\tphone_map.put(\"k\", \"k\");\n\t\tphone_map.put(\"l\", \"l\");\n\t\tphone_map.put(\"m\", \"m\");\n\t\tphone_map.put(\"n\", \"n\");\n\t\tphone_map.put(\"b\", \"b\");\n\t\tphone_map.put(\"f\", \"f\");\n\t\tphone_map.put(\"g\", \"g\");\n\t\tphone_map.put(\"p\", \"p\");\n\t\tphone_map.put(\"r\", \"r\");\n\t\tphone_map.put(\"s\", \"s\");\n\t\tphone_map.put(\"t\", \"t\");\n\t\tphone_map.put(\"v\", \"v\");\n\t\tphone_map.put(\"w\", \"w\");\n\t\tphone_map.put(\"z\", \"z\");\n\t\tphone_map.put(\"ar\", \"eə\");\n\t\tphone_map.put(\"ir\", \"iə\");\n\t\tphone_map.put(\"ur\", \"ʊə\");\n\t\tphone_map.put(\"tr\", \"tr\");\n\t\tphone_map.put(\"dr\", \"dr\");\n\t\tphone_map.put(\"ts\", \"ts\");\n\t\tphone_map.put(\"dz\", \"dz\");\n\t}\n\t\n\t/**\n\t * 开始帧位置，每帧相当于10ms\n\t */\n\tpublic int beg_pos;\n\t/**\n\t * 结束帧位置\n\t */\n\tpublic int end_pos;\n\t/**\n\t * 音素内容\n\t */\n\tpublic String content;\n\t/**\n\t * 增漏读信息：0（正确），16（漏读），32（增读），64（回读），128（替换）\n\t */\n\tpublic int dp_message;\n\t/**\n\t * 时长（单位：帧，每帧相当于10ms）（cn）\n\t */\n\tpublic int time_len;\n\t\n\t/**\n\t * 得到content对应的标准音标（en）\n\t */\n\tpublic String getStdSymbol() {\n\t\treturn getStdSymbol(content);\n\t}\n\t\n\tpublic static String getStdSymbol(String content) {\n\t\tString std = phone_map.get(content);\n\t\treturn (null == std)? content: std;\n\t}\n\t\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/entity/Sentence.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result.entity;\n\nimport java.util.ArrayList;\n\n/**\n * <p>Title: Sentence</p>\n * <p>Description: 句子，对应于xml结果中的sentence标签</p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午4:10:09\n */\npublic class Sentence {\n\t/**\n\t * 开始帧位置，每帧相当于10ms\n\t */\n\tpublic int beg_pos;\n\t/**\n\t * 结束帧位置\n\t */\n\tpublic int end_pos;\n\t/**\n\t * 句子内容\n\t */\n\tpublic String content;\n\t/**\n\t * 总得分\n\t */\n\tpublic float total_score;\n\t/**\n\t * 时长（单位：帧，每帧相当于10ms）（cn）\n\t */\n\tpublic int time_len;\n\t/**\n\t * 句子的索引（en）\n\t */\n\tpublic int index;\n\t/**\n\t * 单词数（en）\n\t */\n\tpublic int word_count;\n\t/**\n\t * sentence包括的word\n\t */\n\tpublic ArrayList<Word> words;\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/entity/Syll.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result.entity;\n\nimport java.util.ArrayList;\n\n/**\n * <p>Title: Syll</p>\n * <p>Description: 音节，对应于结果xml中的Syll标签</p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午3:49:51\n */\npublic class Syll {\n\t/**\n\t * 开始帧位置，每帧相当于10ms\n\t */\n\tpublic int beg_pos;\n\t/**\n\t * 结束帧位置\n\t */\n\tpublic int end_pos;\n\t/**\n\t * 音节内容\n\t */\n\tpublic String content;\n\t/**\n\t * 拼音（cn），数字代表声调，5表示轻声，如fen1\n\t */\n\tpublic String symbol;\n\t/**\n\t * 增漏读信息：0（正确），16（漏读），32（增读），64（回读），128（替换）\n\t */\n\tpublic int dp_message;\n\t/**\n\t * 时长（单位：帧，每帧相当于10ms）（cn）\n\t */\n\tpublic int time_len;\n\t/**\n\t * Syll包含的音节\n\t */\n\tpublic ArrayList<Phone> phones;\n\t\n\t/**\n\t * 获取音节的标准音标（en）\n\t * \n\t * @return 标准音标\n\t */\n\tpublic String getStdSymbol() {\n\t\tString stdSymbol = \"\";\n\t\tString[] symbols = content.split(\" \");\n\t\t\n\t\tfor (int i = 0; i < symbols.length; i++) {\n\t\t\tstdSymbol += Phone.getStdSymbol(symbols[i]);\n\t\t}\n\t\t\n\t\treturn stdSymbol;\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/entity/Word.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result.entity;\n\nimport java.util.ArrayList;\n\n/**\n * <p>Title: Word</p>\n * <p>Description: 单词，对应于结果xml中的word标签</p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午3:29:30\n */\npublic class Word {\n\t/**\n\t * 开始帧位置，每帧相当于10ms\n\t */\n\tpublic int beg_pos;\n\t/**\n\t * 结束帧位置\n\t */\n\tpublic int end_pos;\n\t/**\n\t * 单词内容\n\t */\n\tpublic String content;\n\t/**\n\t * 增漏读信息：0（正确），16（漏读），32（增读），64（回读），128（替换）\n\t */\n\tpublic int dp_message;\n\t/**\n\t * 单词在全篇索引（en）\n\t */\n\tpublic int global_index;\n\t/**\n\t * 单词在句子中的索引（en）\n\t */\n\tpublic int index;\n\t/**\n\t * 拼音（cn），数字代表声调，5表示轻声，如fen1\n\t */\n\tpublic String symbol;\n\t/**\n\t * 时长（单位：帧，每帧相当于10ms）（cn）\n\t */\n\tpublic int time_len;\n\t/**\n\t * 单词得分（en）\n\t */\n\tpublic float total_score;\n\t/**\n\t * Word包含的Syll\n\t */\n\tpublic ArrayList<Syll> sylls;\n\t\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/util/ResultFormatUtil.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result.util;\n\nimport java.util.ArrayList;\n\nimport com.iflytek.ise.result.entity.Phone;\nimport com.iflytek.ise.result.entity.Sentence;\nimport com.iflytek.ise.result.entity.Syll;\nimport com.iflytek.ise.result.entity.Word;\n\n/**\n * <p>Title: ResultFormatUtl</p>\n * <p>Description: </p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月19日 上午10:01:14\n */\npublic class ResultFormatUtil {\n\t\n\t/**\n\t * 将英语评测详情按格式输出\n\t * \n\t * @param sentences\n\t * @return 英语评测详情\n\t */\n\tpublic static String formatDetails_EN(ArrayList<Sentence> sentences) {\n\t\tStringBuffer buffer = new StringBuffer();\n\t\tif (null == sentences) {\n\t\t\treturn buffer.toString();\n\t\t}\n\t\t\n\t\tfor (Sentence sentence: sentences ) {\n\t\t\tif (\"噪音\".equals(ResultTranslateUtil.getContent(sentence.content)) \n\t\t\t\t\t|| \"静音\".equals(ResultTranslateUtil.getContent(sentence.content))) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\tif (null == sentence.words) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Word word: sentence.words) {\n\t\t\t\tif (\"噪音\".equals(ResultTranslateUtil.getContent(word.content)) \n\t\t\t\t\t\t|| \"静音\".equals(ResultTranslateUtil.getContent(word.content))) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbuffer.append(\"\\n单词[\" + ResultTranslateUtil.getContent(word.content) + \"] \")\n\t\t\t\t\t.append(\"朗读：\" + ResultTranslateUtil.getDpMessageInfo(word.dp_message))\n\t\t\t\t\t.append(\" 得分：\" + word.total_score);\n\t\t\t\tif (null == word.sylls) {\n\t\t\t\t\tbuffer.append(\"\\n\");\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor (Syll syll: word.sylls) {\n\t\t\t\t\tbuffer.append(\"\\n└音节[\" + ResultTranslateUtil.getContent(syll.getStdSymbol()) + \"] \");\n\t\t\t\t\tif (null == syll.phones) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (Phone phone: syll.phones) {\n\t\t\t\t\t\tbuffer.append(\"\\n\\t└音素[\" + ResultTranslateUtil.getContent(phone.getStdSymbol()) + \"] \")\n\t\t\t\t\t\t\t.append(\" 朗读：\" + ResultTranslateUtil.getDpMessageInfo(phone.dp_message));\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\tbuffer.append(\"\\n\");\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn buffer.toString();\n\t}\n\n\t/**\n\t * 将汉语评测详情按格式输出\n\t * \n\t * @param sentences\n\t * @return 汉语评测详情\n\t */\n\tpublic static String formatDetails_CN(ArrayList<Sentence> sentences) {\n\t\tStringBuffer buffer = new StringBuffer();\n\t\tif (null == sentences) {\n\t\t\treturn buffer.toString();\n\t\t}\n\t\t\n\t\tfor (Sentence sentence: sentences ) {\n\t\t\tif (null == sentence.words) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t\n\t\t\tfor (Word word: sentence.words) {\n\t\t\t\tbuffer.append(\"\\n词语[\" + ResultTranslateUtil.getContent(word.content) + \"] \" + word.symbol + \" 时长：\" + word.time_len);\n\t\t\t\tif (null == word.sylls) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor (Syll syll: word.sylls) {\n\t\t\t\t\tif (\"噪音\".equals(ResultTranslateUtil.getContent(syll.content)) \n\t\t\t\t\t\t\t|| \"静音\".equals(ResultTranslateUtil.getContent(syll.content))) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tbuffer.append(\"\\n└音节[\" + ResultTranslateUtil.getContent(syll.content) + \"] \" + syll.symbol + \" 时长：\" + syll.time_len);\n\t\t\t\t\tif (null == syll.phones) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tfor (Phone phone: syll.phones) {\n\t\t\t\t\t\tbuffer.append(\"\\n\\t└音素[\" + ResultTranslateUtil.getContent(phone.content) + \"] \" + \"时长：\" + phone.time_len)\n\t\t\t\t\t\t\t.append(\" 朗读：\" + ResultTranslateUtil.getDpMessageInfo(phone.dp_message));\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t\tbuffer.append(\"\\n\");\n\t\t\t}\n\t\t}\n\t\t\n\t\treturn buffer.toString();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/util/ResultTranslateUtil.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result.util;\n\nimport java.util.HashMap;\n\n/**\n * <p>Title: ResultTranslateUtl</p>\n * <p>Description: </p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月13日 下午6:05:03\n */\npublic class ResultTranslateUtil {\n\t\n\tprivate static HashMap<Integer, String> dp_message_map = new HashMap<Integer, String>();\n\tprivate static HashMap<String, String> special_content_map = new HashMap<String, String>();\n\t\n\tstatic {\n\t\tdp_message_map.put(0, \"正常\");\n\t\tdp_message_map.put(16, \"漏读\");\n\t\tdp_message_map.put(32, \"增读\");\n\t\tdp_message_map.put(64, \"回读\");\n\t\tdp_message_map.put(128, \"替换\");\n\t\t\n\t\tspecial_content_map.put(\"sil\", \"静音\");\n\t\tspecial_content_map.put(\"silv\", \"静音\");\n\t\tspecial_content_map.put(\"fil\", \"噪音\");\n\t}\n\t\n\tpublic static String getDpMessageInfo(int dp_message) {\n\t\treturn dp_message_map.get(dp_message);\n\t}\n\t\n\tpublic static String getContent(String content) {\n\t\tString val = special_content_map.get(content);\n\t\treturn (null == val)? content: val;\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/ise/result/xml/XmlResultParser.java",
    "content": "/**\n * \n */\npackage com.iflytek.ise.result.xml;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\n\nimport org.xmlpull.v1.XmlPullParser;\nimport org.xmlpull.v1.XmlPullParserException;\n\nimport android.text.TextUtils;\nimport android.util.Xml;\n\nimport com.iflytek.ise.result.FinalResult;\nimport com.iflytek.ise.result.ReadSentenceResult;\nimport com.iflytek.ise.result.ReadSyllableResult;\nimport com.iflytek.ise.result.ReadWordResult;\nimport com.iflytek.ise.result.Result;\nimport com.iflytek.ise.result.entity.Phone;\nimport com.iflytek.ise.result.entity.Sentence;\nimport com.iflytek.ise.result.entity.Syll;\nimport com.iflytek.ise.result.entity.Word;\n\n/**\n * <p>Title: XmlResultParser</p>\n * <p>Description: </p>\n * <p>Company: www.iflytek.com</p>\n * @author iflytek\n * @date 2015年1月12日 下午5:21:53\n */\npublic class XmlResultParser {\n\t\n\tpublic Result parse(String xml) {\n\t\tif (TextUtils.isEmpty(xml)) {\n\t\t\treturn null;\n\t\t}\n\t\t\n\t\tXmlPullParser pullParser = Xml.newPullParser();\n\t\t\n\t\ttry {\n\t\t\tInputStream ins = new ByteArrayInputStream(xml.getBytes());\n\t\t\tpullParser.setInput(ins, \"utf-8\");\n\t\t\tFinalResult finalResult = null;\n\t\t\t\n\t\t\tint eventType = pullParser.getEventType();\n\t\t\twhile (XmlPullParser.END_DOCUMENT != eventType) {\n\t\t\t\tswitch (eventType) {\n\t\t\t\tcase XmlPullParser.START_TAG:\n\t\t\t\t\tif (\"FinalResult\".equals(pullParser.getName())) {\n\t\t\t\t\t\t// 只有一个总分的结果\n\t\t\t\t\t\tfinalResult = new FinalResult();\n\t\t\t\t\t} else if (\"ret\".equals(pullParser.getName())) {\n\t\t\t\t\t\tfinalResult.ret = getInt(pullParser, \"value\");\n\t\t\t\t\t} else if (\"total_score\".equals(pullParser.getName())) {\n\t\t\t\t\t\tfinalResult.total_score = getFloat(pullParser, \"value\");\n\t\t\t\t\t} else if (\"xml_result\".equals(pullParser.getName())) {\n\t\t\t\t\t\t// 详细结果\n\t\t\t\t\t\treturn parseResult(pullParser);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tbreak;\n\t\t\t\tcase XmlPullParser.END_TAG:\n\t\t\t\t\tif (\"FinalResult\".equals(pullParser.getName())) {\n\t\t\t\t\t\treturn finalResult;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\teventType = pullParser.next();\n\t\t\t}\n\t\t} catch (XmlPullParserException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\treturn null;\n\t}\n\t\n\tprivate Result parseResult(XmlPullParser pullParser) {\n\t\tResult result = null;\n\t\t// <rec_paper>标签是否已扫描到\n\t\tboolean rec_paperPassed = false;\n\t\tSentence sentence = null;\n\t\tWord word = null;\n\t\tSyll syll = null;\n\t\tPhone phone = null;\n\t\t\n\t\tint eventType;\n\t\ttry {\n\t\t\teventType = pullParser.getEventType();\n\t\t\twhile (XmlPullParser.END_DOCUMENT != eventType) {\n\t\t\t\tswitch (eventType) {\n\t\t\t\tcase XmlPullParser.START_TAG:\n\t\t\t\t\tif (\"rec_paper\".equals(pullParser.getName())) {\n\t\t\t\t\t\trec_paperPassed = true;\n\t\t\t\t\t} else if (\"read_syllable\".equals(pullParser.getName())) {\n\t\t\t\t\t\tif (!rec_paperPassed) {\n\t\t\t\t\t\t\tresult = new ReadSyllableResult();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treadTotalResult(result, pullParser);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (\"read_word\".equals(pullParser.getName())) {\n\t\t\t\t\t\tif (!rec_paperPassed) {\n\t\t\t\t\t\t\tresult = new ReadWordResult();\n\t\t\t\t\t\t\tString lan = getLanguage(pullParser);\n\t\t\t\t\t\t\tresult.language = (null == lan)? \"cn\": lan;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treadTotalResult(result, pullParser);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (\"read_sentence\".equals(pullParser.getName()) || \n\t\t\t\t\t\t\t\"read_chapter\".equals(pullParser.getName())) {\n\t\t\t\t\t\tif (!rec_paperPassed) {\n\t\t\t\t\t\t\tresult = new ReadSentenceResult();\n\t\t\t\t\t\t\tString lan = getLanguage(pullParser);\n\t\t\t\t\t\t\tresult.language = (null == lan)? \"cn\": lan;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treadTotalResult(result, pullParser);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (\"sentence\".equals(pullParser.getName())) {\n\t\t\t\t\t\tif (null == result.sentences) {\n\t\t\t\t\t\t\tresult.sentences = new ArrayList<Sentence>();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsentence = createSentence(pullParser);\n\t\t\t\t\t} else if (\"word\".equals(pullParser.getName())) {\n\t\t\t\t\t\tif (null != sentence && null == sentence.words) {\n\t\t\t\t\t\t\tsentence.words = new ArrayList<Word>();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tword = createWord(pullParser);\n\t\t\t\t\t} else if (\"syll\".equals(pullParser.getName())) {\n\t\t\t\t\t\tif (null != word && null == word.sylls) {\n\t\t\t\t\t\t\tword.sylls = new ArrayList<Syll>();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsyll = createSyll(pullParser);\n\t\t\t\t\t} else if (\"phone\".equals(pullParser.getName())) {\n\t\t\t\t\t\tif (null != syll && null == syll.phones) {\n\t\t\t\t\t\t\tsyll.phones = new ArrayList<Phone>();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tphone = createPhone(pullParser);\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tbreak;\n\t\t\t\tcase XmlPullParser.END_TAG:\n\t\t\t\t\tif (\"phone\".equals(pullParser.getName())) {\n\t\t\t\t\t\tsyll.phones.add(phone);\n\t\t\t\t\t} else if (\"syll\".equals(pullParser.getName())) {\n\t\t\t\t\t\tword.sylls.add(syll);\n\t\t\t\t\t} else if (\"word\".equals(pullParser.getName())) {\n\t\t\t\t\t\tsentence.words.add(word);\n\t\t\t\t\t} else if (\"sentence\".equals(pullParser.getName())) {\n\t\t\t\t\t\tresult.sentences.add(sentence);\n\t\t\t\t\t} else if (\"read_syllable\".equals(pullParser.getName()) \n\t\t\t\t\t\t\t|| \"read_word\".equals(pullParser.getName()) \n\t\t\t\t\t\t\t|| \"read_sentence\".equals(pullParser.getName())) {\n\t\t\t\t\t\treturn result;\n\t\t\t\t\t} \n\t\t\t\t\tbreak;\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\teventType = pullParser.next();\n\t\t\t}\n\t\t} catch (XmlPullParserException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\treturn result;\n\t}\n\t\n\tprivate void readTotalResult(Result result, XmlPullParser pullParser) {\n\t\tresult.beg_pos = getInt(pullParser, \"beg_pos\");\n\t\tresult.end_pos = getInt(pullParser, \"end_pos\");\n\t\tresult.content = getContent(pullParser);\n\t\tresult.total_score = getFloat(pullParser, \"total_score\");\n\t\tresult.time_len = getInt(pullParser, \"time_len\");\n\t\tresult.except_info = getExceptInfo(pullParser);\n\t\tresult.is_rejected = getIsRejected(pullParser);\n\t}\n\t\n\tprivate Phone createPhone(XmlPullParser pullParser) {\n\t\tPhone phone;\n\t\tphone = new Phone();\n\t\tphone.beg_pos = getInt(pullParser, \"beg_pos\");\n\t\tphone.end_pos = getInt(pullParser, \"end_pos\");\n\t\tphone.content = getContent(pullParser);\n\t\tphone.dp_message = getInt(pullParser, \"dp_message\");\n\t\tphone.time_len = getInt(pullParser, \"time_len\");\n\t\treturn phone;\n\t}\n\n\tprivate Syll createSyll(XmlPullParser pullParser) {\n\t\tSyll syll;\n\t\tsyll = new Syll();\n\t\tsyll.beg_pos = getInt(pullParser, \"beg_pos\");\n\t\tsyll.end_pos = getInt(pullParser, \"end_pos\");\n\t\tsyll.content = getContent(pullParser);\n\t\tsyll.symbol = getSymbol(pullParser);\n\t\tsyll.dp_message = getInt(pullParser, \"dp_message\");\n\t\tsyll.time_len = getInt(pullParser, \"time_len\");\n\t\treturn syll;\n\t}\n\n\tprivate Word createWord(XmlPullParser pullParser) {\n\t\tWord word;\n\t\tword = new Word();\n\t\tword.beg_pos = getInt(pullParser, \"beg_pos\");\n\t\tword.end_pos = getInt(pullParser, \"end_pos\");\n\t\tword.content = getContent(pullParser);\n\t\tword.symbol = getSymbol(pullParser);\n\t\tword.time_len = getInt(pullParser, \"time_len\");\n\t\tword.dp_message = getInt(pullParser, \"dp_message\");\n\t\tword.total_score = getFloat(pullParser, \"total_score\");\n\t\tword.global_index = getInt(pullParser, \"global_index\");\n\t\tword.index = getInt(pullParser, \"index\");\n\t\treturn word;\n\t}\n\n\tprivate Sentence createSentence(XmlPullParser pullParser) {\n\t\tSentence sentence;\n\t\tsentence = new Sentence();\n\t\tsentence.beg_pos = getInt(pullParser, \"beg_pos\");\n\t\tsentence.end_pos = getInt(pullParser, \"end_pos\");\n\t\tsentence.content = getContent(pullParser);\n\t\tsentence.time_len = getInt(pullParser, \"time_len\");\n\t\tsentence.index = getInt(pullParser, \"index\");\n\t\tsentence.word_count = getInt(pullParser, \"word_count\");\n\t\treturn sentence;\n\t}\n\n\tprivate String getLanguage(XmlPullParser pullParser) {\n\t\treturn pullParser.getAttributeValue(null, \"lan\");\n\t}\n\t\n\tprivate String getExceptInfo(XmlPullParser pullParser) {\n\t\treturn pullParser.getAttributeValue(null, \"except_info\");\n\t}\n\t\n\tprivate boolean getIsRejected(XmlPullParser pullParser) {\n\t\tString isRejected = pullParser.getAttributeValue(null, \"is_rejected\");\n\t\tif (null == isRejected) {\n\t\t\treturn false;\n\t\t}\n\t\t\n\t\treturn Boolean.parseBoolean(isRejected);\n\t}\n\n\tprivate String getSymbol(XmlPullParser pullParser) {\n\t\treturn pullParser.getAttributeValue(null, \"symbol\");\n\t}\n\n\tprivate float getFloat(XmlPullParser pullParser, String attrName) {\n\t\tString val = pullParser.getAttributeValue(null, attrName);\n\t\tif (null == val) {\n\t\t\treturn 0f;\n\t\t}\n\t\treturn Float.parseFloat(val);\n\t}\n\n\tprivate String getContent(XmlPullParser pullParser) {\n\t\treturn pullParser.getAttributeValue(null, \"content\");\n\t}\n\t\n\tprivate int getInt(XmlPullParser pullParser, String attrName) {\n\t\tString val = pullParser.getAttributeValue(null, attrName);\n\t\tif (null == val) {\n\t\t\treturn 0;\n\t\t}\n\t\treturn Integer.parseInt(val);\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/setting/IatSettings.java",
    "content": "package com.iflytek.speech.setting;\n\nimport android.os.Bundle;\nimport android.preference.EditTextPreference;\nimport android.preference.Preference;\nimport android.preference.Preference.OnPreferenceChangeListener;\nimport android.preference.PreferenceActivity;\nimport android.view.Window;\n\nimport com.iflytek.speech.util.SettingTextWatcher;\nimport com.iflytek.voicedemo.R;\n\n/**\n * 听写设置界面\n */\npublic class IatSettings extends PreferenceActivity implements OnPreferenceChangeListener {\n\t\n\tpublic static final String PREFER_NAME = \"com.iflytek.setting\";\n\tprivate EditTextPreference mVadbosPreference;\n\tprivate EditTextPreference mVadeosPreference;\n\t\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsuper.onCreate(savedInstanceState);\n\t\tgetPreferenceManager().setSharedPreferencesName(PREFER_NAME);\n\t\taddPreferencesFromResource(R.xml.iat_setting);\n\t\t\n\t\tmVadbosPreference = (EditTextPreference)findPreference(\"iat_vadbos_preference\");\n\t\tmVadbosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(IatSettings.this,mVadbosPreference,0,10000));\n\t\t\n\t\tmVadeosPreference = (EditTextPreference)findPreference(\"iat_vadeos_preference\");\n\t\tmVadeosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(IatSettings.this,mVadeosPreference,0,10000));\n\t}\n\t@Override\n\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\treturn true;\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/setting/IseSettings.java",
    "content": "/**\n * \n */\npackage com.iflytek.speech.setting;\n\nimport android.os.Bundle;\nimport android.preference.EditTextPreference;\nimport android.preference.ListPreference;\nimport android.preference.Preference;\nimport android.preference.PreferenceActivity;\nimport android.preference.Preference.OnPreferenceChangeListener;\nimport android.text.InputType;\nimport android.text.TextUtils;\nimport android.view.Window;\nimport android.widget.Toast;\n\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.voicedemo.R;\n\n/**\n * 评测设置界面\n */\npublic class IseSettings extends PreferenceActivity {\n\tprivate final static String PREFER_NAME = \"ise_settings\";\n\t\n\tprivate ListPreference mLanguagePref;\n\tprivate ListPreference mCategoryPref;\n\tprivate ListPreference mResultLevelPref;\n\tprivate EditTextPreference mVadBosPref;\n\tprivate EditTextPreference mVadEosPref;\n\tprivate EditTextPreference mSpeechTimeoutPref;\n\t\n\tprivate Toast mToast;\n\t\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsuper.onCreate(savedInstanceState);\n\t\t\n\t\tgetPreferenceManager().setSharedPreferencesName(PREFER_NAME);\n\t\taddPreferencesFromResource(R.xml.ise_settings);\n\t\t\n\t\tinitUI();\n\t}\n\n\tprivate void initUI() {\n\t\tmLanguagePref = (ListPreference) findPreference(SpeechConstant.LANGUAGE);\n\t\tmCategoryPref = (ListPreference) findPreference(SpeechConstant.ISE_CATEGORY);\n\t\tmResultLevelPref = (ListPreference) findPreference(SpeechConstant.RESULT_LEVEL);\n\t\tmVadBosPref = (EditTextPreference) findPreference(SpeechConstant.VAD_BOS);\n\t\tmVadEosPref = (EditTextPreference) findPreference(SpeechConstant.VAD_EOS);\n\t\tmSpeechTimeoutPref = (EditTextPreference) findPreference(SpeechConstant.KEY_SPEECH_TIMEOUT);\n\n\t\tmToast = Toast.makeText(IseSettings.this, \"\", Toast.LENGTH_LONG);\n\t\t\n\t\tmLanguagePref.setSummary(\"当前：\" + mLanguagePref.getEntry());\n\t\tmCategoryPref.setSummary(\"当前：\" + mCategoryPref.getEntry());\n\t\tmResultLevelPref.setSummary(\"当前：\" + mResultLevelPref.getEntry());\n\t\tmVadBosPref.setSummary(\"当前：\" + mVadBosPref.getText() + \"ms\");\n\t\tmVadEosPref.setSummary(\"当前：\" + mVadEosPref.getText() + \"ms\");\n\t\t\n\t\tString speech_timeout = mSpeechTimeoutPref.getText();\n\t\tString summary = \"当前：\" + speech_timeout;\n\t\tif (!\"-1\".equals(speech_timeout)) {\n\t\t\tsummary += \"ms\";\n\t\t}\n\t\tmSpeechTimeoutPref.setSummary(summary);\n\t\t\n\t\tmLanguagePref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\t\t\tif (\"zh_cn\".equals(newValue.toString())) {\n\t\t\t\t\tif (\"plain\".equals(mResultLevelPref.getValue())) {\n\t\t\t\t\t\tshowTip(\"汉语评测结果格式不支持plain设置\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (\"read_syllable\".equals(mCategoryPref.getValue())) {\n\t\t\t\t\t\tshowTip(\"英语评测不支持单字\");\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tint newValueIndex = mLanguagePref.findIndexOfValue(newValue.toString());\n\t\t\t\tString newEntry = (String) mLanguagePref.getEntries()[newValueIndex];\n\t\t\t\tmLanguagePref.setSummary(\"当前：\" + newEntry);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t\t\n\t\tmCategoryPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\t\t\tif (\"en_us\".equals(mLanguagePref.getValue()) && \"read_syllable\".equals(newValue.toString())) {\n\t\t\t\t\tshowTip(\"英语评测不支持单字，请选其他项\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tint newValueIndex = mCategoryPref.findIndexOfValue(newValue.toString());\n\t\t\t\tString newEntry = (String) mCategoryPref.getEntries()[newValueIndex];\n\t\t\t\tmCategoryPref.setSummary(\"当前：\" + newEntry);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t\t\n\t\tmResultLevelPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\t\t\tif (\"zh_cn\".equals(mLanguagePref.getValue()) && \"plain\".equals(newValue.toString())) {\n\t\t\t\t\tshowTip(\"汉语评测不支持plain，请选其他项\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tmResultLevelPref.setSummary(\"当前：\" + newValue.toString());\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t\t\n\t\tmVadBosPref.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);\n\t\tmVadBosPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\t\t\tint bos;\n\t\t\t\ttry {\n\t\t\t\t\tbos = Integer.parseInt(newValue.toString());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tshowTip(\"无效输入！\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (bos < 0 || bos > 30000) {\n\t\t\t\t\tshowTip(\"取值范围为0~30000\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tmVadBosPref.setSummary(\"当前：\" + bos + \"ms\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t\t\n\t\tmVadEosPref.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);\n\t\tmVadEosPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\t\t\tint eos;\n\t\t\t\ttry {\n\t\t\t\t\teos = Integer.parseInt(newValue.toString());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tshowTip(\"无效输入！\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (eos < 0 || eos > 30000) {\n\t\t\t\t\tshowTip(\"取值范围为0~30000\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tmVadEosPref.setSummary(\"当前：\" + eos + \"ms\");\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t\t\n\t\tmSpeechTimeoutPref.getEditText().setInputType(InputType.TYPE_NUMBER_FLAG_SIGNED|InputType.TYPE_CLASS_NUMBER);\n\t\tmSpeechTimeoutPref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\t\t\tint speech_timeout;\n\t\t\t\ttry {\n\t\t\t\t\tspeech_timeout = Integer.parseInt(newValue.toString());\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tshowTip(\"无效输入！\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t \n\t\t\t\tif (speech_timeout < -1) {\n\t\t\t\t\tshowTip(\"必须大于等于-1\");\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif (speech_timeout == -1) {\n\t\t\t\t\tmSpeechTimeoutPref.setSummary(\"当前：-1\");\n\t\t\t\t} else {\n\t\t\t\t\tmSpeechTimeoutPref.setSummary(\"当前：\" + speech_timeout + \"ms\");\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\treturn true;\n\t\t\t}\n\t\t});\n\t}\n\t\n\tprivate void showTip(String str) {\n\t\tif(!TextUtils.isEmpty(str)) {\n\t\t\tmToast.setText(str);\n\t\t\tmToast.show();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/setting/TtsSettings.java",
    "content": "package com.iflytek.speech.setting;\n\nimport android.os.Bundle;\nimport android.preference.EditTextPreference;\nimport android.preference.Preference;\nimport android.preference.Preference.OnPreferenceChangeListener;\nimport android.preference.PreferenceActivity;\nimport android.view.Window;\n\nimport com.iflytek.speech.util.SettingTextWatcher;\nimport com.iflytek.voicedemo.R;\n\n\n/**\n * 合成设置界面\n */\npublic class TtsSettings extends PreferenceActivity implements OnPreferenceChangeListener {\n\t\n\tpublic static final String PREFER_NAME = \"com.iflytek.setting\";\n\tprivate EditTextPreference mSpeedPreference;\n\tprivate EditTextPreference mPitchPreference;\n\tprivate EditTextPreference mVolumePreference;\n\t\n\t@SuppressWarnings(\"deprecation\")\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsuper.onCreate(savedInstanceState);\n\t\t// 指定保存文件名字\n\t\tgetPreferenceManager().setSharedPreferencesName(PREFER_NAME);\n\t\taddPreferencesFromResource(R.xml.tts_setting);\n\t\tmSpeedPreference = (EditTextPreference)findPreference(\"speed_preference\");\n\t\tmSpeedPreference.getEditText().addTextChangedListener(new SettingTextWatcher(TtsSettings.this,mSpeedPreference,0,200));\n\t\t\n\t\tmPitchPreference = (EditTextPreference)findPreference(\"pitch_preference\");\n\t\tmPitchPreference.getEditText().addTextChangedListener(new SettingTextWatcher(TtsSettings.this,mPitchPreference,0,100));\n\t\t\n\t\tmVolumePreference = (EditTextPreference)findPreference(\"volume_preference\");\n\t\tmVolumePreference.getEditText().addTextChangedListener(new SettingTextWatcher(TtsSettings.this,mVolumePreference,0,100));\n\t\t\n\t}\n\t\n\t@Override\n\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\treturn true;\n\t}\t\t\n\t\n\t\n}"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/setting/UnderstanderSettings.java",
    "content": "package com.iflytek.speech.setting;\n\nimport android.os.Bundle;\nimport android.preference.EditTextPreference;\nimport android.preference.Preference;\nimport android.preference.Preference.OnPreferenceChangeListener;\nimport android.preference.PreferenceActivity;\nimport android.view.Window;\n\nimport com.iflytek.speech.util.SettingTextWatcher;\nimport com.iflytek.sunflower.FlowerCollector;\nimport com.iflytek.voicedemo.R;\n\n/**\n * 语义理解设置界面\n */\npublic class UnderstanderSettings extends PreferenceActivity implements OnPreferenceChangeListener {\n\tprivate static final String TAG = UnderstanderSettings.class.getSimpleName();\n\t\n\tpublic static final String PREFER_NAME = \"com.iflytek.setting\";\n\tprivate EditTextPreference mVadbosPreference;\n\tprivate EditTextPreference mVadeosPreference;\n\t\n\t@SuppressWarnings(\"deprecation\")\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsuper.onCreate(savedInstanceState);\n\t\tgetPreferenceManager().setSharedPreferencesName(PREFER_NAME);\n\t\taddPreferencesFromResource(R.xml.understand_setting);\n\t\t\n\t\tmVadbosPreference = (EditTextPreference)findPreference(\"understander_vadbos_preference\");\n\t\tmVadbosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(UnderstanderSettings.this,mVadbosPreference,0,10000));\n\t\t\n\t\tmVadeosPreference = (EditTextPreference)findPreference(\"understander_vadeos_preference\");\n\t\tmVadeosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(UnderstanderSettings.this,mVadeosPreference,0,10000));\n\t}\n\t@Override\n\tpublic boolean onPreferenceChange(Preference preference, Object newValue) {\n\t\treturn true;\n\t}\n\t\n\t@Override\n\tprotected void onResume() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onResume(UnderstanderSettings.this);\n\t\tFlowerCollector.onPageStart(TAG);\n\t\tsuper.onResume();\n\t}\n\t\n\t@Override\n\tprotected void onPause() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onPageEnd(TAG);\n\t\tFlowerCollector.onPause(UnderstanderSettings.this);\n\t\tsuper.onPause();\n\t}\n}\n\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/util/ApkInstaller.java",
    "content": "package com.iflytek.speech.util;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.AlertDialog.Builder;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.DialogInterface.OnClickListener;\nimport android.content.Intent;\nimport android.net.Uri;\n\nimport com.iflytek.cloud.SpeechUtility;\n\n\n/**\n * 弹出提示框，下载服务组件\n */\npublic class ApkInstaller {\n\tprivate Activity mActivity ;\n\t\n\tpublic ApkInstaller(Activity activity) {\n\t\tmActivity = activity;\n\t}\n\n\tpublic void install(){\n\t\tAlertDialog.Builder builder = new Builder(mActivity);\n\t\tbuilder.setMessage(\"检测到您未安装语记！\\n是否前往下载语记？\");\n\t\tbuilder.setTitle(\"下载提示\");\n\t\tbuilder.setPositiveButton(\"确认前往\", new OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tdialog.dismiss();\n\t\t\t\tString url = SpeechUtility.getUtility().getComponentUrl();\n\t\t\t\tString assetsApk=\"SpeechService.apk\";\n\t\t\t\tprocessInstall(mActivity, url,assetsApk);\n\t\t\t}\n\t\t});\n\t\tbuilder.setNegativeButton(\"残忍拒绝\", new OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tdialog.dismiss();\n\t\t\t}\n\t\t});\n\t\tbuilder.create().show();\n\t\treturn;\n\t}\n\t/**\n\t * 如果服务组件没有安装打开语音服务组件下载页面，进行下载后安装。\n\t */\n\tprivate boolean processInstall(Context context ,String url,String assetsApk){\n\t\t//直接下载方式\n\t\tUri uri = Uri.parse(url);\n\t\tIntent it = new Intent(Intent.ACTION_VIEW, uri);\n\t\tcontext.startActivity(it);\n\t\treturn true;\t\t\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/util/FucUtil.java",
    "content": "package com.iflytek.speech.util;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport com.iflytek.cloud.ErrorCode;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechUtility;\n\nimport android.content.Context;\n\n/**\n * 功能性函数扩展类\n */\npublic class FucUtil {\n\t/**\n\t * 读取asset目录下文件。\n\t * @return content\n\t */\n\tpublic static String readFile(Context mContext,String file,String code)\n\t{\n\t\tint len = 0;\n\t\tbyte []buf = null;\n\t\tString result = \"\";\n\t\ttry {\n\t\t\tInputStream in = mContext.getAssets().open(file);\t\t\t\n\t\t\tlen  = in.available();\n\t\t\tbuf = new byte[len];\n\t\t\tin.read(buf, 0, len);\n\t\t\t\n\t\t\tresult = new String(buf,code);\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn result;\n\t}\n\t/**\n\t * 将字节缓冲区按照固定大小进行分割成数组\n\t * @param buffer 缓冲区\n\t * @param length 缓冲区大小\n\t * @param spsize 切割块大小\n\t * @return\n\t */\n\tpublic ArrayList<byte[]> splitBuffer(byte[] buffer,int length,int spsize)\n\t{\n\t\tArrayList<byte[]> array = new ArrayList<byte[]>();\n\t\tif(spsize <= 0 || length <= 0 || buffer == null || buffer.length < length)\n\t\t\treturn array;\n\t\tint size = 0;\n\t\twhile(size < length)\n\t\t{\n\t\t\tint left = length - size;\n\t\t\tif(spsize < left)\n\t\t\t{\n\t\t\t\tbyte[] sdata = new byte[spsize];\n\t\t\t\tSystem.arraycopy(buffer,size,sdata,0,spsize);\n\t\t\t\tarray.add(sdata);\n\t\t\t\tsize += spsize;\n\t\t\t}else\n\t\t\t{\n\t\t\t\tbyte[] sdata = new byte[left];\n\t\t\t\tSystem.arraycopy(buffer,size,sdata,0,left);\n\t\t\t\tarray.add(sdata);\n\t\t\t\tsize += left;\n\t\t\t}\n\t\t}\n\t\treturn array;\n\t}\n\t/**\n\t * 获取语记是否包含离线听写资源，如未包含跳转至资源下载页面\n\t *1.PLUS_LOCAL_ALL: 本地所有资源 \n      2.PLUS_LOCAL_ASR: 本地识别资源\n      3.PLUS_LOCAL_TTS: 本地合成资源\n\t */\n\tpublic static String checkLocalResource(){\n\t\tString resource = SpeechUtility.getUtility().getParameter(SpeechConstant.PLUS_LOCAL_ASR);\n\t\ttry {\n\t\t\tJSONObject result = new JSONObject(resource);\n\t\t\tint ret = result.getInt(SpeechUtility.TAG_RESOURCE_RET);\n\t\t\tswitch (ret) {\n\t\t\tcase ErrorCode.SUCCESS:\n\t\t\t\tJSONArray asrArray = result.getJSONObject(\"result\").optJSONArray(\"asr\");\n\t\t\t\tif (asrArray != null) {\n\t\t\t\t\tint i = 0;\n\t\t\t\t\t// 查询否包含离线听写资源\n\t\t\t\t\tfor (; i < asrArray.length(); i++) {\n\t\t\t\t\t\tif(\"iat\".equals(asrArray.getJSONObject(i).get(SpeechConstant.DOMAIN))){\n\t\t\t\t\t\t\t//asrArray中包含语言、方言字段，后续会增加支持方言的本地听写。\n\t\t\t\t\t\t\t//如：\"accent\": \"mandarin\",\"language\": \"zh_cn\"\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (i >= asrArray.length()) {\n\t\t\t\t\t\t\n\t\t\t\t\t\tSpeechUtility.getUtility().openEngineSettings(SpeechConstant.ENG_ASR);\t\n\t\t\t\t\t\treturn \"没有听写资源，跳转至资源下载页面\";\n\t\t\t\t\t}\n\t\t\t\t}else {\n\t\t\t\t\tSpeechUtility.getUtility().openEngineSettings(SpeechConstant.ENG_ASR);\n\t\t\t\t\treturn \"没有听写资源，跳转至资源下载页面\";\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase ErrorCode.ERROR_VERSION_LOWER:\n\t\t\t\treturn \"语记版本过低，请更新后使用本地功能\";\n\t\t\tcase ErrorCode.ERROR_INVALID_RESULT:\n\t\t\t\tSpeechUtility.getUtility().openEngineSettings(SpeechConstant.ENG_ASR);\n\t\t\t\treturn \"获取结果出错，跳转至资源下载页面\";\n\t\t\tcase ErrorCode.ERROR_SYSTEM_PREINSTALL:\n\t\t\t\t//语记为厂商预置版本。\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tSpeechUtility.getUtility().openEngineSettings(SpeechConstant.ENG_ASR);\n\t\t\treturn \"获取结果出错，跳转至资源下载页面\";\n\t\t}\n\t\treturn \"\";\n\t}\n\t\n\t/**\n\t * 读取asset目录下音频文件。\n\t * \n\t * @return 二进制文件数据\n\t */\n\tpublic static byte[] readAudioFile(Context context, String filename) {\n\t\ttry {\n\t\t\tInputStream ins = context.getAssets().open(filename);\n\t\t\tbyte[] data = new byte[ins.available()];\n\t\t\t\n\t\t\tins.read(data);\n\t\t\tins.close();\n\t\t\t\n\t\t\treturn data;\n\t\t} catch (IOException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t\t\n\t\treturn null;\n\t}\n\t\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/util/JsonParser.java",
    "content": "package com.iflytek.speech.util;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\nimport org.json.JSONTokener;\n\nimport android.util.Log;\n\n/**\n * Json结果解析类\n */\npublic class JsonParser {\n\n\tpublic static String parseIatResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\t// 转写结果词，默认使用第一个结果\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tJSONObject obj = items.getJSONObject(0);\n\t\t\t\tret.append(obj.getString(\"w\"));\n//\t\t\t\t如果需要多候选结果，解析数组其他字段\n//\t\t\t\tfor(int j = 0; j < items.length(); j++)\n//\t\t\t\t{\n//\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n//\t\t\t\t\tret.append(obj.getString(\"w\"));\n//\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t} \n\t\treturn ret.toString();\n\t}\n\t\n\tpublic static String parseGrammarResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tfor(int j = 0; j < items.length(); j++)\n\t\t\t\t{\n\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n\t\t\t\t\tif(obj.getString(\"w\").contains(\"nomatch\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tret.append(\"没有匹配结果.\");\n\t\t\t\t\t\treturn ret.toString();\n\t\t\t\t\t}\n\t\t\t\t\tret.append(\"【结果】\" + obj.getString(\"w\"));\n\t\t\t\t\tret.append(\"【置信度】\" + obj.getInt(\"sc\"));\n\t\t\t\t\tret.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tret.append(\"没有匹配结果.\");\n\t\t} \n\t\treturn ret.toString();\n\t}\n\t\n\tpublic static String parseLocalGrammarResult(String json) {\n\t\tStringBuffer ret = new StringBuffer();\n\t\ttry {\n\t\t\tJSONTokener tokener = new JSONTokener(json);\n\t\t\tJSONObject joResult = new JSONObject(tokener);\n\n\t\t\tJSONArray words = joResult.getJSONArray(\"ws\");\n\t\t\tfor (int i = 0; i < words.length(); i++) {\n\t\t\t\tJSONArray items = words.getJSONObject(i).getJSONArray(\"cw\");\n\t\t\t\tfor(int j = 0; j < items.length(); j++)\n\t\t\t\t{\n\t\t\t\t\tJSONObject obj = items.getJSONObject(j);\n\t\t\t\t\tif(obj.getString(\"w\").contains(\"nomatch\"))\n\t\t\t\t\t{\n\t\t\t\t\t\tret.append(\"没有匹配结果.\");\n\t\t\t\t\t\treturn ret.toString();\n\t\t\t\t\t}\n\t\t\t\t\tret.append(\"【结果】\" + obj.getString(\"w\"));\n\t\t\t\t\tret.append(\"\\n\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tret.append(\"【置信度】\" + joResult.optInt(\"sc\"));\n\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t\tret.append(\"没有匹配结果.\");\n\t\t} \n\t\treturn ret.toString();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/util/SettingTextWatcher.java",
    "content": "package com.iflytek.speech.util;\n\nimport java.util.regex.Pattern;\nimport android.content.Context;\nimport android.preference.EditTextPreference;\nimport android.text.Editable;\nimport android.text.TextUtils;\nimport android.text.TextWatcher;\nimport android.widget.Toast;\n\n/**\n * 输入框输入范围控制\n */\npublic class SettingTextWatcher implements TextWatcher {\n\tprivate int editStart ;\n\tprivate int editCount ;\n\tprivate EditTextPreference mEditTextPreference;\n\tint minValue;//最小值\n\tint maxValue;//最大值\n\tprivate Context mContext;\n\t\n\tpublic SettingTextWatcher(Context context,EditTextPreference e,int min, int max) {\n\t\tmContext = context;\n\t\tmEditTextPreference = e;\n\t\tminValue = min;\n\t\tmaxValue = max;\n\t }\n\t\n\t@Override\n\tpublic void onTextChanged(CharSequence s, int start, int before, int count) {\n//\t\tLog.e(\"demo\", \"onTextChanged start:\"+start+\" count:\"+count+\" before:\"+before);\n\t\teditStart = start;\n\t\teditCount = count;\n\t}\n\t\n\t@Override\n\tpublic void beforeTextChanged(CharSequence s, int start, int count,int after) {\t\t\n//\t\tLog.e(\"demo\", \"beforeTextChanged start:\"+start+\" count:\"+count+\" after:\"+after);\n\t}\n\t\n\t@Override\n\tpublic void afterTextChanged(Editable s) {\n\t\tif (TextUtils.isEmpty(s)) {\n\t\t\treturn;\n\t\t}\n\t\tString content = s.toString();\n//\t\tLog.e(\"demo\", \"content:\"+content);\n\t\tif (isNumeric(content)) {\n\t\t\tint num = Integer.parseInt(content);\n\t\t\tif (num > maxValue || num < minValue) {\n\t\t\t\ts.delete(editStart, editStart+editCount);\n\t\t\t\tmEditTextPreference.getEditText().setText(s);\n\t\t\t\tToast.makeText(mContext, \"超出有效值范围\", Toast.LENGTH_SHORT).show();\n\t\t\t}\n\t\t}else {\n\t\t\ts.delete(editStart, editStart+editCount);\n\t\t\tmEditTextPreference.getEditText().setText(s);\n\t\t\tToast.makeText(mContext, \"只能输入数字哦\", Toast.LENGTH_SHORT).show();\n\t\t}\n\t}\n\t\n\t/**\n\t * 正则表达式-判断是否为数字\n\t */\n\tpublic static boolean isNumeric(String str){ \n\t    Pattern pattern = Pattern.compile(\"[0-9]*\"); \n\t    return pattern.matcher(str).matches();    \n\t } \n\n};\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/speech/util/XmlParser.java",
    "content": "package com.iflytek.speech.util;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\n\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\n\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\n/**\n * Xml结果解析类\n */\npublic class XmlParser {\n\n\tpublic static String parseNluResult(String xml) \n\t{\n\t\tStringBuffer buffer = new StringBuffer();\n\t\ttry\n\t\t{\n\t\t\t// DOM builder\n\t\t\tDocumentBuilder domBuilder = null;\n\t\t\t// DOM doc\n\t\t\tDocument domDoc = null;\t\n\n\t\t\t// init DOM\n\t\t\tDocumentBuilderFactory domFact = DocumentBuilderFactory.newInstance();\n\t\t\tdomBuilder = domFact.newDocumentBuilder();\n\t\t\tInputStream is = new ByteArrayInputStream(xml.getBytes());\n\t\t\tdomDoc = domBuilder.parse(is);\n\n\t\t\t// 获取根节点\n\t\t\tElement root = (Element) domDoc.getDocumentElement();\n\t\t\t\n\t\t\tElement raw = (Element)root.getElementsByTagName(\"rawtext\").item(0);\n\t\t\tbuffer.append(\"【识别结果】\" + raw.getFirstChild().getNodeValue());\n\t\t\tbuffer.append(\"\\n\");\n\t\t\t\n\t\t\tElement e = (Element)root.getElementsByTagName(\"result\").item(0);\n\t\t\t\n\t\t\tElement focus = (Element)e.getElementsByTagName(\"focus\").item(0);\n\t\t\tbuffer.append(\"【FOCUS】\" + focus.getFirstChild().getNodeValue());\n\t\t\tbuffer.append(\"\\n\");\n\t\t\t\n\t\t\tElement action = (Element)e.getElementsByTagName(\"action\").item(0);\n\t\t\tElement operation = (Element)action.getElementsByTagName(\"operation\").item(0);\n\t\t\tbuffer.append(\"【ACTION】\" + operation.getFirstChild().getNodeValue());\n\t\t\tbuffer.append(\"\\n\");\n\t\t\t\n\n\t\t}catch(Exception e){\n\t\t\te.printStackTrace();\n\t\t};\n\t\tbuffer.append(\"【ALL】\" + xml);\n\t\treturn buffer.toString();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/voicedemo/AsrDemo.java",
    "content": "package com.iflytek.voicedemo;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.SharedPreferences;\nimport android.content.SharedPreferences.Editor;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.Window;\nimport android.widget.EditText;\nimport android.widget.RadioGroup;\nimport android.widget.RadioGroup.OnCheckedChangeListener;\nimport android.widget.Toast;\n\nimport com.iflytek.cloud.ErrorCode;\nimport com.iflytek.cloud.GrammarListener;\nimport com.iflytek.cloud.InitListener;\nimport com.iflytek.cloud.LexiconListener;\nimport com.iflytek.cloud.RecognizerListener;\nimport com.iflytek.cloud.RecognizerResult;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechRecognizer;\nimport com.iflytek.cloud.SpeechUtility;\nimport com.iflytek.cloud.util.ContactManager;\nimport com.iflytek.cloud.util.ContactManager.ContactListener;\nimport com.iflytek.speech.util.ApkInstaller;\nimport com.iflytek.speech.util.FucUtil;\nimport com.iflytek.speech.util.JsonParser;\nimport com.iflytek.sunflower.FlowerCollector;\n\npublic class AsrDemo extends Activity implements OnClickListener{\n\tprivate static String TAG = AsrDemo.class.getSimpleName();\n\t// 语音识别对象\n\tprivate SpeechRecognizer mAsr;\n\tprivate Toast mToast;\t\n\t// 缓存\n\tprivate SharedPreferences mSharedPreferences;\n\t// 本地语法文件\n\tprivate String mLocalGrammar = null;\n\t// 本地词典\n\tprivate String mLocalLexicon = null;\n\t// 云端语法文件\n\tprivate String mCloudGrammar = null;\n\t\t\n\tprivate static final String KEY_GRAMMAR_ABNF_ID = \"grammar_abnf_id\";\n\tprivate static final String GRAMMAR_TYPE_ABNF = \"abnf\";\n\tprivate static final String GRAMMAR_TYPE_BNF = \"bnf\";\n\n\tprivate String mEngineType = null;\n\t// 语记安装助手类\n\tApkInstaller mInstaller ;\n\t\n\t@SuppressLint(\"ShowToast\")\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.isrdemo);\n\t\tinitLayout();\n\t\t\n\t\t// 初始化识别对象\n\t\tmAsr = SpeechRecognizer.createRecognizer(AsrDemo.this, mInitListener);\t\t\n\n\t\t// 初始化语法、命令词\n\t\tmLocalLexicon = \"张海羊\\n刘婧\\n王锋\\n\";\n\t\tmLocalGrammar = FucUtil.readFile(this,\"call.bnf\", \"utf-8\");\n\t\tmCloudGrammar = FucUtil.readFile(this,\"grammar_sample.abnf\",\"utf-8\");\n\t\t\n\t\t// 获取联系人，本地更新词典时使用\n\t\tContactManager mgr = ContactManager.createManager(AsrDemo.this, mContactListener);\t\n\t\tmgr.asyncQueryAllContactsName();\n\t\tmSharedPreferences = getSharedPreferences(getPackageName(),\tMODE_PRIVATE);\n\t\tmToast = Toast.makeText(this,\"\",Toast.LENGTH_SHORT);\t\n\t\t\n\t\tmInstaller = new ApkInstaller(AsrDemo.this);\n\t}\n\t\n\t/**\n\t * 初始化Layout。\n\t */\n\tprivate void initLayout() {\n\t\tfindViewById(R.id.isr_recognize).setOnClickListener(AsrDemo.this);\n\t\tfindViewById(R.id.isr_grammar).setOnClickListener(AsrDemo.this);\n\t\tfindViewById(R.id.isr_lexcion).setOnClickListener(AsrDemo.this);\n\t\tfindViewById(R.id.isr_stop).setOnClickListener(AsrDemo.this);\n\t\tfindViewById(R.id.isr_cancel).setOnClickListener(AsrDemo.this);\n\n\t\t//选择云端or本地\n\t\tRadioGroup group = (RadioGroup)this.findViewById(R.id.radioGroup);\n\t\tgroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {\n\t\t\t\n\t\t\t@Override\n\t\t\tpublic void onCheckedChanged(RadioGroup group, int checkedId) {\n\t\t\t\tif(checkedId == R.id.radioCloud) {\n\t\t\t\t\t((EditText)findViewById(R.id.isr_text)).setText(mCloudGrammar);\n\t\t\t\t\tfindViewById(R.id.isr_lexcion).setEnabled(false);\n\t\t\t\t\tmEngineType = SpeechConstant.TYPE_CLOUD;\n\t\t\t\t} else {\n\t\t\t\t\t((EditText)findViewById(R.id.isr_text)).setText(mLocalGrammar);\n\t\t\t\t\tfindViewById(R.id.isr_lexcion).setEnabled(true);\n\t\t\t\t\tmEngineType = SpeechConstant.TYPE_LOCAL;\n\t\t\t\t\t/**\n\t\t\t\t\t * 选择本地合成\n\t\t\t\t\t * 判断是否安装语记,未安装则跳转到提示安装页面\n\t\t\t\t\t */\n\t\t\t\t\tif (!SpeechUtility.getUtility().checkServiceInstalled()) {\n\t\t\t\t\t\tmInstaller.install();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\t\n\t// 语法、词典临时变量\n\tString mContent;\n\t// 函数调用返回值\n    int ret = 0;\n\t\n    @Override\n\tpublic void onClick(View view) {\t\t\n\t\tif(null == mEngineType) {\n\t\t\tshowTip(\"请先选择识别引擎类型\");\n\t\t\treturn;\n\t\t}\t\n\t\tswitch(view.getId())\n\t\t{\n\t\t\tcase R.id.isr_grammar:\n\t\t\t\tshowTip(\"上传预设关键词/语法文件\");\n\t\t\t\t// 本地-构建语法文件，生成语法id\n\t\t\t\tif (mEngineType.equals(SpeechConstant.TYPE_LOCAL)) {\n\t\t\t\t\t((EditText)findViewById(R.id.isr_text)).setText(mLocalGrammar);\n\t\t\t\t\tmContent = new String(mLocalGrammar);\n\t\t\t\t\tmAsr.setParameter(SpeechConstant.TEXT_ENCODING,\"utf-8\");\n\t\t\t\t\t//指定引擎类型\n\t\t\t\t\tmAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);\n\t\t\t\t\tret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, mLocalGrammarListener);\n\t\t\t\t\tif(ret != ErrorCode.SUCCESS){\n\t\t\t\t\t\tif(ret == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED){\n\t\t\t\t\t\t\t//未安装则跳转到提示安装页面\n\t\t\t\t\t\t\tmInstaller.install();\n\t\t\t\t\t\t}else {\n\t\t\t\t\t\t\tshowTip(\"语法构建失败,错误码：\" + ret);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// 在线-构建语法文件，生成语法id\n\t\t\t\telse {\t\n\t\t\t\t\t((EditText)findViewById(R.id.isr_text)).setText(mCloudGrammar);\n\t\t\t\t\tmContent = new String(mCloudGrammar);\n\t\t\t\t\t//指定引擎类型\n\t\t\t\t\tmAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);\n\t\t\t\t\tmAsr.setParameter(SpeechConstant.TEXT_ENCODING,\"utf-8\");\n\t\t\t\t    ret = mAsr.buildGrammar(GRAMMAR_TYPE_ABNF, mContent, mCloudGrammarListener);\n\t\t\t\t\tif(ret != ErrorCode.SUCCESS)\n\t\t\t\t\t\tshowTip(\"语法构建失败,错误码：\" + ret);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tbreak;\n\t\t\t// 本地-更新词典      注意:更新词典需要在接收到构建语法回调onBuildFinish之后进行，否则会导致错误。\n\t\t\tcase R.id.isr_lexcion: \n\t\t\t\t((EditText)findViewById(R.id.isr_text)).setText(mLocalLexicon);\n\t\t\t\tmContent = new String(mLocalLexicon);\n\t\t\t\t//指定引擎类型\n\t\t\t\tmAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);\n\t\t\t\tmAsr.setParameter(SpeechConstant.GRAMMAR_LIST, \"call\");\n\t\t\t\tret = mAsr.updateLexicon(\"<contact>\", mContent, mLexiconListener);\n\t\t\t\tif(ret != ErrorCode.SUCCESS){\n\t\t\t\t\tif(ret == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED){\n\t\t\t\t\t\t//未安装则跳转到提示安装页面\n\t\t\t\t\t\tmInstaller.install();\n\t\t\t\t\t}else {\n\t\t\t\t\t\tshowTip(\"更新词典失败,错误码：\" + ret);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t// 开始识别\n\t\t\tcase R.id.isr_recognize:\n\t\t\t\t((EditText)findViewById(R.id.isr_text)).setText(null);// 清空显示内容\n\t\t\t\t// 设置参数\n\t\t\t\tif (!setParam()) {\n\t\t\t\t\tshowTip(\"请先构建语法。\");\n\t\t\t\t\treturn;\n\t\t\t\t};\n\t\t\t\t\n\t\t\t\tret = mAsr.startListening(mRecognizerListener);\n\t\t\t\tif (ret != ErrorCode.SUCCESS) {\n\t\t\t\t\tif(ret == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED){\n\t\t\t\t\t\t//未安装则跳转到提示安装页面\n\t\t\t\t\t\tmInstaller.install();\n\t\t\t\t\t}else {\n\t\t\t\t\t\tshowTip(\"识别失败,错误码: \" + ret);\t\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t// 停止识别\n\t\t\tcase R.id.isr_stop:\n\t\t\t\tmAsr.stopListening();\n\t\t\t\tshowTip(\"停止识别\");\n\t\t\t\tbreak;\n\t\t\t// 取消识别\n\t\t\tcase R.id.isr_cancel:\n\t\t\t\tmAsr.cancel();\n\t\t\t\tshowTip(\"取消识别\");\n\t\t\t\tbreak;\n\t\t}\n\t}\n\t\n\t/**\n     * 初始化监听器。\n     */\n    private InitListener mInitListener = new InitListener() {\n\n\t\t@Override\n\t\tpublic void onInit(int code) {\n\t\t\tLog.d(TAG, \"SpeechRecognizer init() code = \" + code);\n\t\t\tif (code != ErrorCode.SUCCESS) {\n        \t\tshowTip(\"初始化失败,错误码：\"+code);\n        \t}\n\t\t}\n    };\n    \t\n\t/**\n     * 更新词典监听器。\n     */\n\tprivate LexiconListener mLexiconListener = new LexiconListener() {\n\t\t@Override\n\t\tpublic void onLexiconUpdated(String lexiconId, SpeechError error) {\n\t\t\tif(error == null){\n\t\t\t\tshowTip(\"词典更新成功\");\n\t\t\t}else{\n\t\t\t\tshowTip(\"词典更新失败,错误码：\"+error.getErrorCode());\n\t\t\t}\n\t\t}\n\t};\n\t\n\t/**\n     * 本地构建语法监听器。\n     */\n\tprivate GrammarListener mLocalGrammarListener = new GrammarListener() {\n\t\t@Override\n\t\tpublic void onBuildFinish(String grammarId, SpeechError error) {\n\t\t\tif(error == null){\n\t\t\t\tshowTip(\"语法构建成功：\" + grammarId);\n\t\t\t}else{\n\t\t\t\tshowTip(\"语法构建失败,错误码：\" + error.getErrorCode());\n\t\t\t}\t\t\t\n\t\t}\n\t};\n\t/**\n     * 云端构建语法监听器。\n     */\n\tprivate GrammarListener mCloudGrammarListener = new GrammarListener() {\n\t\t@Override\n\t\tpublic void onBuildFinish(String grammarId, SpeechError error) {\n\t\t\tif(error == null){\n\t\t\t\tString grammarID = new String(grammarId);\n\t\t\t\tEditor editor = mSharedPreferences.edit();\n\t\t\t\tif(!TextUtils.isEmpty(grammarId))\n\t\t\t\t\teditor.putString(KEY_GRAMMAR_ABNF_ID, grammarID);\n\t\t\t\teditor.commit();\n\t\t\t\tshowTip(\"语法构建成功：\" + grammarId);\n\t\t\t}else{\n\t\t\t\tshowTip(\"语法构建失败,错误码：\" + error.getErrorCode());\n\t\t\t}\t\t\t\n\t\t}\n\t};\n\t/**\n\t * 获取联系人监听器。\n\t */\n\tprivate ContactListener mContactListener = new ContactListener() {\n\t\t@Override\n\t\tpublic void onContactQueryFinish(String contactInfos, boolean changeFlag) {\n\t\t\t//获取联系人\n\t\t\tmLocalLexicon = contactInfos;\n\t\t}\t\t\n\t};\n\t/**\n     * 识别监听器。\n     */\n    private RecognizerListener mRecognizerListener = new RecognizerListener() {\n        \n        @Override\n        public void onVolumeChanged(int volume, byte[] data) {\n        \tshowTip(\"当前正在说话，音量大小：\" + volume);\n        \tLog.d(TAG, \"返回音频数据：\"+data.length);\n        }\n        \n        @Override\n        public void onResult(final RecognizerResult result, boolean isLast) {\n        \tif (null != result) {\n        \t\tLog.d(TAG, \"recognizer result：\" + result.getResultString());\n        \t\tString text ;\n        \t\tif(\"cloud\".equalsIgnoreCase(mEngineType)){\n        \t\t\ttext = JsonParser.parseGrammarResult(result.getResultString());\n        \t\t}else {\n        \t\t\ttext = JsonParser.parseLocalGrammarResult(result.getResultString());\n        \t\t}\n        \t\t\n        \t\t// 显示\n        \t\t((EditText)findViewById(R.id.isr_text)).setText(text);                \n        \t} else {\n        \t\tLog.d(TAG, \"recognizer result : null\");\n        \t}\t\n        }\n        \n        @Override\n        public void onEndOfSpeech() {\n        \t// 此回调表示：检测到了语音的尾端点，已经进入识别过程，不再接受语音输入\n        \tshowTip(\"结束说话\");\n        }\n        \n        @Override\n        public void onBeginOfSpeech() {\n        \t// 此回调表示：sdk内部录音机已经准备好了，用户可以开始语音输入\n        \tshowTip(\"开始说话\");\n        }\n\n\t\t@Override\n\t\tpublic void onError(SpeechError error) {\n\t\t\tshowTip(\"onError Code：\"\t+ error.getErrorCode());\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {\n\t\t\t// 以下代码用于获取与云端的会话id，当业务出错时将会话id提供给技术支持人员，可用于查询会话日志，定位出错原因\n\t\t\t// 若使用本地能力，会话id为null\n\t\t\t//\tif (SpeechEvent.EVENT_SESSION_ID == eventType) {\n\t\t\t//\t\tString sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);\n\t\t\t//\t\tLog.d(TAG, \"session id =\" + sid);\n\t\t\t//\t}\n\t\t}\n\n    };\n    \n\t\n\n\tprivate void showTip(final String str) {\n\t\trunOnUiThread(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tmToast.setText(str);\n\t\t\t\tmToast.show();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * 参数设置\n\t * @param param\n\t * @return \n\t */\n\tpublic boolean setParam(){\n\t\tboolean result = false;\n\t\t//设置识别引擎\n\t\tmAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);\n\t\t//设置返回结果为json格式\n\t\tmAsr.setParameter(SpeechConstant.RESULT_TYPE, \"json\");\n\n\t\tif(\"cloud\".equalsIgnoreCase(mEngineType))\n\t\t{\n\t\t\tString grammarId = mSharedPreferences.getString(KEY_GRAMMAR_ABNF_ID, null);\n\t\t\tif(TextUtils.isEmpty(grammarId))\n\t\t\t{\n\t\t\t\tresult =  false;\n\t\t\t}else {\n\t\t\t\t//设置云端识别使用的语法id\n\t\t\t\tmAsr.setParameter(SpeechConstant.CLOUD_GRAMMAR, grammarId);\n\t\t\t\tresult =  true;\n\t\t\t}\n\t\t} else {\n\t\t\t//设置本地识别使用语法id\n\t\t\tmAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, \"call\");\n\t\t\t//设置本地识别的门限值\n\t\t\tmAsr.setParameter(SpeechConstant.ASR_THRESHOLD, \"30\");\n\t\t\tresult = true;\n\t\t}\n\t\t\n\t\t// 设置音频保存路径，保存音频格式支持pcm、wav，设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限\n\t\t// 注：AUDIO_FORMAT参数语记需要更新版本才能生效\n\t\tmAsr.setParameter(SpeechConstant.AUDIO_FORMAT,\"wav\");\n\t\tmAsr.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+\"/msc/asr.wav\");\n\t\treturn result;\n\t}\n\t\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\t// 退出时释放连接\n\t\tmAsr.cancel();\n\t\tmAsr.destroy();\n\t}\n\t\n\t@Override\n\tprotected void onResume() {\n\t\t//移动数据统计分析\n\t\tFlowerCollector.onResume(AsrDemo.this);\n\t\tFlowerCollector.onPageStart(TAG);\n\t\tsuper.onResume();\n\t}\n\t\n\t@Override\n\tprotected void onPause() {\n\t\t//移动数据统计分析\n\t\tFlowerCollector.onPageEnd(TAG);\n\t\tFlowerCollector.onPause(AsrDemo.this);\n\t\tsuper.onPause();\n\t}\n\t\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/voicedemo/IatDemo.java",
    "content": "package com.iflytek.voicedemo;\n\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.Window;\nimport android.widget.EditText;\nimport android.widget.RadioGroup;\nimport android.widget.RadioGroup.OnCheckedChangeListener;\nimport android.widget.Toast;\n\nimport com.iflytek.cloud.ErrorCode;\nimport com.iflytek.cloud.InitListener;\nimport com.iflytek.cloud.LexiconListener;\nimport com.iflytek.cloud.RecognizerListener;\nimport com.iflytek.cloud.RecognizerResult;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechRecognizer;\nimport com.iflytek.cloud.SpeechUtility;\nimport com.iflytek.cloud.ui.RecognizerDialog;\nimport com.iflytek.cloud.ui.RecognizerDialogListener;\nimport com.iflytek.cloud.util.ContactManager;\nimport com.iflytek.cloud.util.ContactManager.ContactListener;\nimport com.iflytek.speech.setting.IatSettings;\nimport com.iflytek.speech.util.ApkInstaller;\nimport com.iflytek.speech.util.FucUtil;\nimport com.iflytek.speech.util.JsonParser;\nimport com.iflytek.sunflower.FlowerCollector;\n\npublic class IatDemo extends Activity implements OnClickListener {\n\tprivate static String TAG = IatDemo.class.getSimpleName();\n\t// 语音听写对象\n\tprivate SpeechRecognizer mIat;\n\t// 语音听写UI\n\tprivate RecognizerDialog mIatDialog;\n\t// 用HashMap存储听写结果\n\tprivate HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();\n\n\tprivate EditText mResultText;\n\tprivate Toast mToast;\n\tprivate SharedPreferences mSharedPreferences;\n\t// 引擎类型\n\tprivate String mEngineType = SpeechConstant.TYPE_CLOUD;\n\t// 语记安装助手类\n\tApkInstaller mInstaller;\n\t\n\n\t@SuppressLint(\"ShowToast\")\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.iatdemo);\n\n\t\tinitLayout();\n\t\t// 初始化识别无UI识别对象\n\t\t// 使用SpeechRecognizer对象，可根据回调消息自定义界面；\n\t\tmIat = SpeechRecognizer.createRecognizer(IatDemo.this, mInitListener);\n\t\t\n\t\t// 初始化听写Dialog，如果只使用有UI听写功能，无需创建SpeechRecognizer\n\t\t// 使用UI听写功能，请根据sdk文件目录下的notice.txt,放置布局文件和图片资源\n\t\tmIatDialog = new RecognizerDialog(IatDemo.this, mInitListener);\n\n\t\tmSharedPreferences = getSharedPreferences(IatSettings.PREFER_NAME,\n\t\t\t\tActivity.MODE_PRIVATE);\n\t\tmToast = Toast.makeText(this, \"\", Toast.LENGTH_SHORT);\n\t\tmResultText = ((EditText) findViewById(R.id.iat_text));\n\t\tmInstaller = new ApkInstaller(IatDemo.this);\n\t}\n\n\t/**\n\t * 初始化Layout。\n\t */\n\tprivate void initLayout() {\n\t\tfindViewById(R.id.iat_recognize).setOnClickListener(IatDemo.this);\n\t\tfindViewById(R.id.iat_recognize_stream).setOnClickListener(IatDemo.this);\n\t\tfindViewById(R.id.iat_upload_contacts).setOnClickListener(IatDemo.this);\n\t\tfindViewById(R.id.iat_upload_userwords).setOnClickListener(IatDemo.this);\n\t\tfindViewById(R.id.iat_stop).setOnClickListener(IatDemo.this);\n\t\tfindViewById(R.id.iat_cancel).setOnClickListener(IatDemo.this);\n\t\tfindViewById(R.id.image_iat_set).setOnClickListener(IatDemo.this);\n\t\t// 选择云端or本地\n\t\tRadioGroup group = (RadioGroup) findViewById(R.id.radioGroup);\n\t\tgroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onCheckedChanged(RadioGroup group, int checkedId) {\n\t\t\t\tswitch (checkedId) {\n\t\t\t\tcase R.id.iatRadioCloud:\n\t\t\t\t\tmEngineType = SpeechConstant.TYPE_CLOUD;\n\t\t\t\t\tfindViewById(R.id.iat_upload_contacts).setEnabled(true);\n\t\t\t\t\tfindViewById(R.id.iat_upload_userwords).setEnabled(true);\n\t\t\t\t\tbreak;\n\t\t\t\tcase R.id.iatRadioLocal:\n\t\t\t\t\tmEngineType = SpeechConstant.TYPE_LOCAL;\n\t\t\t\t\tfindViewById(R.id.iat_upload_contacts).setEnabled(false);\n\t\t\t\t\tfindViewById(R.id.iat_upload_userwords).setEnabled(false);\n\t\t\t\t\t/**\n\t\t\t\t\t * 选择本地听写 判断是否安装语记,未安装则跳转到提示安装页面\n\t\t\t\t\t */\n\t\t\t\t\tif (!SpeechUtility.getUtility().checkServiceInstalled()) {\n\t\t\t\t\t\tmInstaller.install();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString result = FucUtil.checkLocalResource();\n\t\t\t\t\t\tif (!TextUtils.isEmpty(result)) {\n\t\t\t\t\t\t\tshowTip(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase R.id.iatRadioMix:\n\t\t\t\t\tmEngineType = SpeechConstant.TYPE_MIX;\n\t\t\t\t\tfindViewById(R.id.iat_upload_contacts).setEnabled(false);\n\t\t\t\t\tfindViewById(R.id.iat_upload_userwords).setEnabled(false);\n\t\t\t\t\t/**\n\t\t\t\t\t * 选择本地听写 判断是否安装语记,未安装则跳转到提示安装页面\n\t\t\t\t\t */\n\t\t\t\t\tif (!SpeechUtility.getUtility().checkServiceInstalled()) {\n\t\t\t\t\t\tmInstaller.install();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tString result = FucUtil.checkLocalResource();\n\t\t\t\t\t\tif (!TextUtils.isEmpty(result)) {\n\t\t\t\t\t\t\tshowTip(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tint ret = 0; // 函数调用返回值\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tswitch (view.getId()) {\n\t\t// 进入参数设置页面\n\t\tcase R.id.image_iat_set:\n\t\t\tIntent intents = new Intent(IatDemo.this, IatSettings.class);\n\t\t\tstartActivity(intents);\n\t\t\tbreak;\n\t\t// 开始听写\n\t\t// 如何判断一次听写结束：OnResult isLast=true 或者 onError\n\t\tcase R.id.iat_recognize:\n\t\t\t// 移动数据分析，收集开始听写事件\n\t\t\tFlowerCollector.onEvent(IatDemo.this, \"iat_recognize\");\n\t\t\t\n\t\t\tmResultText.setText(null);// 清空显示内容\n\t\t\tmIatResults.clear();\n\t\t\t// 设置参数\n\t\t\tsetParam();\n\t\t\tboolean isShowDialog = mSharedPreferences.getBoolean(\n\t\t\t\t\tgetString(R.string.pref_key_iat_show), true);\n\t\t\tif (isShowDialog) {\n\t\t\t\t// 显示听写对话框\n\t\t\t\tmIatDialog.setListener(mRecognizerDialogListener);\n\t\t\t\tmIatDialog.show();\n\t\t\t\tshowTip(getString(R.string.text_begin));\n\t\t\t} else {\n\t\t\t\t// 不显示听写对话框\n\t\t\t\tret = mIat.startListening(mRecognizerListener);\n\t\t\t\tif (ret != ErrorCode.SUCCESS) {\n\t\t\t\t\tshowTip(\"听写失败,错误码：\" + ret);\n\t\t\t\t} else {\n\t\t\t\t\tshowTip(getString(R.string.text_begin));\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t// 音频流识别\n\t\tcase R.id.iat_recognize_stream:\n\t\t\tmResultText.setText(null);// 清空显示内容\n\t\t\tmIatResults.clear();\n\t\t\t// 设置参数\n\t\t\tsetParam();\n\t\t\t// 设置音频来源为外部文件\n\t\t\tmIat.setParameter(SpeechConstant.AUDIO_SOURCE, \"-1\");\n\t\t\t// 也可以像以下这样直接设置音频文件路径识别（要求设置文件在sdcard上的全路径）：\n\t\t\t// mIat.setParameter(SpeechConstant.AUDIO_SOURCE, \"-2\");\n\t\t\t// mIat.setParameter(SpeechConstant.ASR_SOURCE_PATH, \"sdcard/XXX/XXX.pcm\");\n\t\t\tret = mIat.startListening(mRecognizerListener);\n\t\t\tif (ret != ErrorCode.SUCCESS) {\n\t\t\t\tshowTip(\"识别失败,错误码：\" + ret);\n\t\t\t} else {\n\t\t\t\tbyte[] audioData = FucUtil.readAudioFile(IatDemo.this, \"iattest.wav\");\n\t\t\t\t\n\t\t\t\tif (null != audioData) {\n\t\t\t\t\tshowTip(getString(R.string.text_begin_recognizer));\n\t\t\t\t\t// 一次（也可以分多次）写入音频文件数据，数据格式必须是采样率为8KHz或16KHz（本地识别只支持16K采样率，云端都支持），位长16bit，单声道的wav或者pcm\n\t\t\t\t\t// 写入8KHz采样的音频时，必须先调用setParameter(SpeechConstant.SAMPLE_RATE, \"8000\")设置正确的采样率\n\t\t\t\t\t// 注：当音频过长，静音部分时长超过VAD_EOS将导致静音后面部分不能识别。\n\t\t\t\t\t// 音频切分方法：FucUtil.splitBuffer(byte[] buffer,int length,int spsize);\n\t\t\t\t\tmIat.writeAudio(audioData, 0, audioData.length);\n\t\t\t\t\tmIat.stopListening();\n\t\t\t\t} else {\n\t\t\t\t\tmIat.cancel();\n\t\t\t\t\tshowTip(\"读取音频流失败\");\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t// 停止听写\n\t\tcase R.id.iat_stop:\n\t\t\tmIat.stopListening();\n\t\t\tshowTip(\"停止听写\");\n\t\t\tbreak;\n\t\t// 取消听写\n\t\tcase R.id.iat_cancel:\n\t\t\tmIat.cancel();\n\t\t\tshowTip(\"取消听写\");\n\t\t\tbreak;\n\t\t// 上传联系人\n\t\tcase R.id.iat_upload_contacts:\n\t\t\tshowTip(getString(R.string.text_upload_contacts));\n\t\t\tContactManager mgr = ContactManager.createManager(IatDemo.this,\n\t\t\t\t\tmContactListener);\n\t\t\tmgr.asyncQueryAllContactsName();\n\t\t\tbreak;\n\t\t// 上传用户词表\n\t\tcase R.id.iat_upload_userwords:\n\t\t\tshowTip(getString(R.string.text_upload_userwords));\n\t\t\tString contents = FucUtil.readFile(IatDemo.this, \"userwords\",\"utf-8\");\n\t\t\tmResultText.setText(contents);\n\t\t\t// 指定引擎类型\n\t\t\tmIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);\n\t\t\tmIat.setParameter(SpeechConstant.TEXT_ENCODING, \"utf-8\");\n\t\t\tret = mIat.updateLexicon(\"userword\", contents, mLexiconListener);\n\t\t\tif (ret != ErrorCode.SUCCESS)\n\t\t\t\tshowTip(\"上传热词失败,错误码：\" + ret);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * 初始化监听器。\n\t */\n\tprivate InitListener mInitListener = new InitListener() {\n\n\t\t@Override\n\t\tpublic void onInit(int code) {\n\t\t\tLog.d(TAG, \"SpeechRecognizer init() code = \" + code);\n\t\t\tif (code != ErrorCode.SUCCESS) {\n\t\t\t\tshowTip(\"初始化失败，错误码：\" + code);\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * 上传联系人/词表监听器。\n\t */\n\tprivate LexiconListener mLexiconListener = new LexiconListener() {\n\n\t\t@Override\n\t\tpublic void onLexiconUpdated(String lexiconId, SpeechError error) {\n\t\t\tif (error != null) {\n\t\t\t\tshowTip(error.toString());\n\t\t\t} else {\n\t\t\t\tshowTip(getString(R.string.text_upload_success));\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * 听写监听器。\n\t */\n\tprivate RecognizerListener mRecognizerListener = new RecognizerListener() {\n\n\t\t@Override\n\t\tpublic void onBeginOfSpeech() {\n\t\t\t// 此回调表示：sdk内部录音机已经准备好了，用户可以开始语音输入\n\t\t\tshowTip(\"开始说话\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void onError(SpeechError error) {\n\t\t\t// Tips：\n\t\t\t// 错误码：10118(您没有说话)，可能是录音机权限被禁，需要提示用户打开应用的录音权限。\n\t\t\t// 如果使用本地功能（语记）需要提示用户开启语记的录音权限。\n\t\t\tshowTip(error.getPlainDescription(true));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEndOfSpeech() {\n\t\t\t// 此回调表示：检测到了语音的尾端点，已经进入识别过程，不再接受语音输入\n\t\t\tshowTip(\"结束说话\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void onResult(RecognizerResult results, boolean isLast) {\n\t\t\tLog.d(TAG, results.getResultString());\n\t\t\tprintResult(results);\n\n\t\t\tif (isLast) {\n\t\t\t\t// TODO 最后的结果\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onVolumeChanged(int volume, byte[] data) {\n\t\t\tshowTip(\"当前正在说话，音量大小：\" + volume);\n\t\t\tLog.d(TAG, \"返回音频数据：\"+data.length);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {\n\t\t\t// 以下代码用于获取与云端的会话id，当业务出错时将会话id提供给技术支持人员，可用于查询会话日志，定位出错原因\n\t\t\t// 若使用本地能力，会话id为null\n\t\t\t//\tif (SpeechEvent.EVENT_SESSION_ID == eventType) {\n\t\t\t//\t\tString sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);\n\t\t\t//\t\tLog.d(TAG, \"session id =\" + sid);\n\t\t\t//\t}\n\t\t}\n\t};\n\n\tprivate void printResult(RecognizerResult results) {\n\t\tString text = JsonParser.parseIatResult(results.getResultString());\n\n\t\tString sn = null;\n\t\t// 读取json结果中的sn字段\n\t\ttry {\n\t\t\tJSONObject resultJson = new JSONObject(results.getResultString());\n\t\t\tsn = resultJson.optString(\"sn\");\n\t\t} catch (JSONException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\n\t\tmIatResults.put(sn, text);\n\n\t\tStringBuffer resultBuffer = new StringBuffer();\n\t\tfor (String key : mIatResults.keySet()) {\n\t\t\tresultBuffer.append(mIatResults.get(key));\n\t\t}\n\n\t\tmResultText.setText(resultBuffer.toString());\n\t\tmResultText.setSelection(mResultText.length());\n\t}\n\n\t/**\n\t * 听写UI监听器\n\t */\n\tprivate RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {\n\t\tpublic void onResult(RecognizerResult results, boolean isLast) {\n\t\t\tprintResult(results);\n\t\t}\n\n\t\t/**\n\t\t * 识别回调错误.\n\t\t */\n\t\tpublic void onError(SpeechError error) {\n\t\t\tshowTip(error.getPlainDescription(true));\n\t\t}\n\n\t};\n\n\t/**\n\t * 获取联系人监听器。\n\t */\n\tprivate ContactListener mContactListener = new ContactListener() {\n\n\t\t@Override\n\t\tpublic void onContactQueryFinish(final String contactInfos, boolean changeFlag) {\n\t\t\t// 注：实际应用中除第一次上传之外，之后应该通过changeFlag判断是否需要上传，否则会造成不必要的流量.\n\t\t\t// 每当联系人发生变化，该接口都将会被回调，可通过ContactManager.destroy()销毁对象，解除回调。\n\t\t\t// if(changeFlag) {\n\t\t\t// 指定引擎类型\n\t\t\trunOnUiThread(new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tmResultText.setText(contactInfos);\n\t\t\t\t}\n\t\t\t});\n\t\t\t\n\t\t\tmIat.setParameter(SpeechConstant.ENGINE_TYPE,SpeechConstant.TYPE_CLOUD);\n\t\t\tmIat.setParameter(SpeechConstant.TEXT_ENCODING, \"utf-8\");\n\t\t\tret = mIat.updateLexicon(\"contact\", contactInfos, mLexiconListener);\n\t\t\tif (ret != ErrorCode.SUCCESS) {\n\t\t\t\tshowTip(\"上传联系人失败：\" + ret);\n\t\t\t}\n\t\t}\n\t};\n\n\tprivate void showTip(final String str) {\n\t\tmToast.setText(str);\n\t\tmToast.show();\n\t}\n\n\t/**\n\t * 参数设置\n\t * \n\t * @param param\n\t * @return\n\t */\n\tpublic void setParam() {\n\t\t// 清空参数\n\t\tmIat.setParameter(SpeechConstant.PARAMS, null);\n\n\t\t// 设置听写引擎\n\t\tmIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);\n\t\t// 设置返回结果格式\n\t\tmIat.setParameter(SpeechConstant.RESULT_TYPE, \"json\");\n\n\t\tString lag = mSharedPreferences.getString(\"iat_language_preference\",\n\t\t\t\t\"mandarin\");\n\t\tif (lag.equals(\"en_us\")) {\n\t\t\t// 设置语言\n\t\t\tmIat.setParameter(SpeechConstant.LANGUAGE, \"en_us\");\n\t\t} else {\n\t\t\t// 设置语言\n\t\t\tmIat.setParameter(SpeechConstant.LANGUAGE, \"zh_cn\");\n\t\t\t// 设置语言区域\n\t\t\tmIat.setParameter(SpeechConstant.ACCENT, lag);\n\t\t}\n\n\t\t// 设置语音前端点:静音超时时间，即用户多长时间不说话则当做超时处理\n\t\tmIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString(\"iat_vadbos_preference\", \"4000\"));\n\t\t\n\t\t// 设置语音后端点:后端点静音检测时间，即用户停止说话多长时间内即认为不再输入， 自动停止录音\n\t\tmIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString(\"iat_vadeos_preference\", \"1000\"));\n\t\t\n\t\t// 设置标点符号,设置为\"0\"返回结果无标点,设置为\"1\"返回结果有标点\n\t\tmIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString(\"iat_punc_preference\", \"1\"));\n\t\t\n\t\t// 设置音频保存路径，保存音频格式支持pcm、wav，设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限\n\t\t// 注：AUDIO_FORMAT参数语记需要更新版本才能生效\n\t\tmIat.setParameter(SpeechConstant.AUDIO_FORMAT,\"wav\");\n\t\tmIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+\"/msc/iat.wav\");\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\t// 退出时释放连接\n\t\tmIat.cancel();\n\t\tmIat.destroy();\n\t}\n\n\t@Override\n\tprotected void onResume() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onResume(IatDemo.this);\n\t\tFlowerCollector.onPageStart(TAG);\n\t\tsuper.onResume();\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onPageEnd(TAG);\n\t\tFlowerCollector.onPause(IatDemo.this);\n\t\tsuper.onPause();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/voicedemo/IseDemo.java",
    "content": "package com.iflytek.voicedemo;\n\nimport com.iflytek.ise.result.Result;\nimport com.iflytek.ise.result.xml.XmlResultParser;\nimport com.iflytek.speech.setting.IseSettings;\nimport com.iflytek.sunflower.FlowerCollector;\nimport com.iflytek.cloud.EvaluatorListener;\nimport com.iflytek.cloud.EvaluatorResult;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechEvaluator;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.Window;\nimport android.widget.Button;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\n/**\n * 语音评测demo\n */\npublic class IseDemo extends Activity implements OnClickListener {\n\tprivate static String TAG = IseDemo.class.getSimpleName();\n\t\n\tprivate final static String PREFER_NAME = \"ise_settings\";\n\tprivate final static int REQUEST_CODE_SETTINGS = 1;\n\n\tprivate EditText mEvaTextEditText;\n\tprivate EditText mResultEditText;\n\tprivate Button mIseStartButton;\n\tprivate Toast mToast;\n\n\t// 评测语种\n\tprivate String language;\n\t// 评测题型\n\tprivate String category;\n\t// 结果等级\n\tprivate String result_level;\n\t\n\tprivate String mLastResult;\n\tprivate SpeechEvaluator mIse;\n\t\n\t\n\t// 评测监听接口\n\tprivate EvaluatorListener mEvaluatorListener = new EvaluatorListener() {\n\t\t\n\t\t@Override\n\t\tpublic void onResult(EvaluatorResult result, boolean isLast) {\n\t\t\tLog.d(TAG, \"evaluator result :\" + isLast);\n\n\t\t\tif (isLast) {\n\t\t\t\tStringBuilder builder = new StringBuilder();\n\t\t\t\tbuilder.append(result.getResultString());\n\t\t\t\t\n\t\t\t\tif(!TextUtils.isEmpty(builder)) {\n\t\t\t\t\tmResultEditText.setText(builder.toString());\n\t\t\t\t}\n\t\t\t\tmIseStartButton.setEnabled(true);\n\t\t\t\tmLastResult = builder.toString();\n\t\t\t\t\n\t\t\t\tshowTip(\"评测结束\");\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onError(SpeechError error) {\n\t\t\tmIseStartButton.setEnabled(true);\n\t\t\tif(error != null) {\t\n\t\t\t\tshowTip(\"error:\"+ error.getErrorCode() + \",\" + error.getErrorDescription());\n\t\t\t\tmResultEditText.setText(\"\");\n\t\t\t\tmResultEditText.setHint(\"请点击“开始评测”按钮\");\n\t\t\t} else {\n\t\t\t\tLog.d(TAG, \"evaluator over\");\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onBeginOfSpeech() {\n\t\t\t// 此回调表示：sdk内部录音机已经准备好了，用户可以开始语音输入\n\t\t\tLog.d(TAG, \"evaluator begin\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEndOfSpeech() {\n\t\t\t// 此回调表示：检测到了语音的尾端点，已经进入识别过程，不再接受语音输入\n\t\t\tLog.d(TAG, \"evaluator stoped\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void onVolumeChanged(int volume, byte[] data) {\n\t\t\tshowTip(\"当前音量：\" + volume);\n\t\t\tLog.d(TAG, \"返回音频数据：\"+data.length);\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {\n\t\t\t// 以下代码用于获取与云端的会话id，当业务出错时将会话id提供给技术支持人员，可用于查询会话日志，定位出错原因\n\t\t\t//\tif (SpeechEvent.EVENT_SESSION_ID == eventType) {\n\t\t\t//\t\tString sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);\n\t\t\t//\t\tLog.d(TAG, \"session id =\" + sid);\n\t\t\t//\t}\n\t\t}\n\t\t\n\t};\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.isedemo);\n\n\t\tmIse = SpeechEvaluator.createEvaluator(IseDemo.this, null);\n\t\tinitUI();\n\t\tsetEvaText();\n\t}\n\n\tprivate void initUI() {\n\t\tfindViewById(R.id.image_ise_set).setOnClickListener(IseDemo.this);\n\t\tmEvaTextEditText = (EditText) findViewById(R.id.ise_eva_text);\n\t\tmResultEditText = (EditText)findViewById(R.id.ise_result_text);\n\t\tmIseStartButton = (Button) findViewById(R.id.ise_start);\n\t\tmIseStartButton.setOnClickListener(IseDemo.this);\n\t\tfindViewById(R.id.ise_parse).setOnClickListener(IseDemo.this);\n\t\tfindViewById(R.id.ise_stop).setOnClickListener(IseDemo.this);\n\t\tfindViewById(R.id.ise_cancel).setOnClickListener(IseDemo.this);\n\t\t\t\t\n\t\tmToast = Toast.makeText(IseDemo.this, \"\", Toast.LENGTH_LONG);\n\t}\n\t\n\t@Override\n\tpublic void onClick(View view) {\n\t\tswitch (view.getId()) {\n\t\t\tcase R.id.image_ise_set:\n\t\t\t\tIntent intent = new Intent(IseDemo.this, IseSettings.class);\n\t\t\t\tstartActivityForResult(intent, REQUEST_CODE_SETTINGS);\n\t\t\t\tbreak;\n\t\t\tcase R.id.ise_start:\n\t\t\t\tif (mIse == null) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\n\t\t\t\tString evaText = mEvaTextEditText.getText().toString();\n\t\t\t\tmLastResult = null;\n\t\t\t\tmResultEditText.setText(\"\");\n\t\t\t\tmResultEditText.setHint(\"请朗读以上内容\");\n\t\t\t\tmIseStartButton.setEnabled(false);\n\t\t\t\t\n\t\t\t\tsetParams();\n\t\t\t\tmIse.startEvaluating(evaText, null, mEvaluatorListener);\n\t\t\t\tbreak;\n\t\t\tcase R.id.ise_parse:\n\t\t\t\t// 解析最终结果\n\t\t\t\tif (!TextUtils.isEmpty(mLastResult)) {\n\t\t\t\t\tXmlResultParser resultParser = new XmlResultParser();\n\t\t\t\t\tResult result = resultParser.parse(mLastResult);\n\t\t\t\t\t\n\t\t\t\t\tif (null != result) {\n\t\t\t\t\t\tmResultEditText.setText(result.toString());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tshowTip(\"结析结果为空\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase R.id.ise_stop:\n\t\t\t\tif (mIse.isEvaluating()) {\n\t\t\t\t\tmResultEditText.setHint(\"评测已停止，等待结果中...\");\n\t\t\t\t\tmIse.stopEvaluating();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase R.id.ise_cancel: {\n\t\t\t\tmIse.cancel();\n\t\t\t\tmIseStartButton.setEnabled(true);\n\t\t\t\tmResultEditText.setText(\"\");\n\t\t\t\tmResultEditText.setHint(\"请点击“开始评测”按钮\");\n\t\t\t\tmLastResult = null;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t@Override\n\tprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t\t\n\t\tif (REQUEST_CODE_SETTINGS == requestCode) {\n\t\t\tsetEvaText();\n\t\t}\n\t}\n\t\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\t\n\t\tif (null != mIse) {\n\t\t\tmIse.destroy();\n\t\t\tmIse = null;\n\t\t}\n\t}\n\t\n\t// 设置评测试题\n\tprivate void setEvaText() {\n\t\tSharedPreferences pref = getSharedPreferences(PREFER_NAME, MODE_PRIVATE);\n\t\tlanguage = pref.getString(SpeechConstant.LANGUAGE, \"zh_cn\");\n\t\tcategory = pref.getString(SpeechConstant.ISE_CATEGORY, \"read_sentence\");\n\t\t\n\t\tString text = \"\";\n\t\tif (\"en_us\".equals(language)) {\n\t\t\tif (\"read_word\".equals(category)) {\n\t\t\t\ttext = getString(R.string.text_en_word);\n\t\t\t} else if (\"read_sentence\".equals(category)) {\n\t\t\t\ttext = getString(R.string.text_en_sentence);\n\t\t\t} \n\t\t} else {\n\t\t\t// 中文评测\n\t\t\tif (\"read_syllable\".equals(category)) {\n\t\t\t\ttext = getString(R.string.text_cn_syllable);\n\t\t\t} else if (\"read_word\".equals(category)) {\n\t\t\t\ttext = getString(R.string.text_cn_word);\n\t\t\t} else if (\"read_sentence\".equals(category)) {\n\t\t\t\ttext = getString(R.string.text_cn_sentence);\n\t\t\t} \n\t\t}\n\t\t\n\t\tmEvaTextEditText.setText(text);\n\t\tmResultEditText.setText(\"\");\n\t\tmLastResult = null;\n\t\tmResultEditText.setHint(\"请点击“开始评测”按钮\");\n\t}\n\n\tprivate void showTip(String str) {\n\t\tif(!TextUtils.isEmpty(str)) {\n\t\t\tmToast.setText(str);\n\t\t\tmToast.show();\n\t\t}\n\t}\n\tprivate void setParams() {\n\t\tSharedPreferences pref = getSharedPreferences(PREFER_NAME, MODE_PRIVATE);\n\t\t// 设置评测语言\n\t\tlanguage = pref.getString(SpeechConstant.LANGUAGE, \"zh_cn\");\n\t\t// 设置需要评测的类型\n\t\tcategory = pref.getString(SpeechConstant.ISE_CATEGORY, \"read_sentence\");\n\t\t// 设置结果等级（中文仅支持complete）\n\t\tresult_level = pref.getString(SpeechConstant.RESULT_LEVEL, \"complete\");\n\t\t// 设置语音前端点:静音超时时间，即用户多长时间不说话则当做超时处理\n\t\tString vad_bos = pref.getString(SpeechConstant.VAD_BOS, \"5000\");\n\t\t// 设置语音后端点:后端点静音检测时间，即用户停止说话多长时间内即认为不再输入， 自动停止录音\n\t\tString vad_eos = pref.getString(SpeechConstant.VAD_EOS, \"1800\");\n\t\t// 语音输入超时时间，即用户最多可以连续说多长时间；\n\t\tString speech_timeout = pref.getString(SpeechConstant.KEY_SPEECH_TIMEOUT, \"-1\");\n\t\t\n\t\tmIse.setParameter(SpeechConstant.LANGUAGE, language);\n\t\tmIse.setParameter(SpeechConstant.ISE_CATEGORY, category);\n\t\tmIse.setParameter(SpeechConstant.TEXT_ENCODING, \"utf-8\");\n\t\tmIse.setParameter(SpeechConstant.VAD_BOS, vad_bos);\n\t\tmIse.setParameter(SpeechConstant.VAD_EOS, vad_eos);\n\t\tmIse.setParameter(SpeechConstant.KEY_SPEECH_TIMEOUT, speech_timeout);\n\t\tmIse.setParameter(SpeechConstant.RESULT_LEVEL, result_level);\n\t\t\n\t\t// 设置音频保存路径，保存音频格式支持pcm、wav，设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限\n\t\t// 注：AUDIO_FORMAT参数语记需要更新版本才能生效\n\t\tmIse.setParameter(SpeechConstant.AUDIO_FORMAT,\"wav\");\n\t\tmIse.setParameter(SpeechConstant.ISE_AUDIO_PATH, Environment.getExternalStorageDirectory().getAbsolutePath() + \"/msc/ise.wav\");\n\t}\n\t\n\t@Override\n\tprotected void onResume() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onResume(IseDemo.this);\n\t\tFlowerCollector.onPageStart(TAG);\n\t\tsuper.onResume();\n\t}\n\t\n\t@Override\n\tprotected void onPause() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onPageEnd(TAG);\n\t\tFlowerCollector.onPause(IseDemo.this);\n\t\tsuper.onPause();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/voicedemo/MainActivity.java",
    "content": "package com.iflytek.voicedemo;\n\nimport com.iflytek.sunflower.FlowerCollector;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.widget.BaseAdapter;\nimport android.widget.Button;\nimport android.widget.ListView;\nimport android.widget.Toast;\n\npublic class MainActivity extends Activity implements OnClickListener {\n\n\tprivate static final String TAG = MainActivity.class.getSimpleName();\n\tprivate Toast mToast;\n\n\t@SuppressLint(\"ShowToast\")\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.main);\n\t\t\n\t\tmToast = Toast.makeText(this, \"\", Toast.LENGTH_SHORT);\n\t\tSimpleAdapter listitemAdapter = new SimpleAdapter();\n\t\t((ListView) findViewById(R.id.listview_main)).setAdapter(listitemAdapter);\n\t}\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tint tag = Integer.parseInt(view.getTag().toString());\n\t\tIntent intent = null;\n\t\tswitch (tag) {\n\t\tcase 0:\n\t\t\t// 语音转写\n\t\t\tintent = new Intent(MainActivity.this, IatDemo.class);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\t// 语法识别\n\t\t\tintent = new Intent(MainActivity.this, AsrDemo.class);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\t// 语义理解\n\t\t\tintent = new Intent(MainActivity.this, UnderstanderDemo.class);\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\t// 语音合成\n\t\t\tintent = new Intent(MainActivity.this, TtsDemo.class);\n\t\t\tbreak;\n\t\tcase 4:\n\t\t\t// 语音评测\n\t\t\tintent = new Intent(MainActivity.this, IseDemo.class);\n\t\t\tbreak;\n\t\tcase 5:\n\t\t\t// 唤醒\n\t\t\tshowTip(\"请登录：http://www.xfyun.cn/ 下载体验吧！\");\n\t\t\tbreak;\n\t\tcase 6:\n\t\t\t// 声纹\n\t\tdefault:\n\t\t\tshowTip(\"在IsvDemo中哦，为了代码简洁，就不放在一起啦，^_^\");\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tif (intent != null) {\n\t\t\tstartActivity(intent);\n\t\t}\n\t}\n\n\t// Menu 列表\n\tString items[] = { \"立刻体验语音听写\", \"立刻体验语法识别\", \"立刻体验语义理解\", \"立刻体验语音合成\",\n\t\t\t\"立刻体验语音评测\", \"立刻体验语音唤醒\", \"立刻体验声纹密码\" };\n\n\tprivate class SimpleAdapter extends BaseAdapter {\n\t\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\t\tif (null == convertView) {\n\t\t\t\tLayoutInflater factory = LayoutInflater.from(MainActivity.this);\n\t\t\t\tView mView = factory.inflate(R.layout.list_items, null);\n\t\t\t\tconvertView = mView;\n\t\t\t}\n\t\t\t\n\t\t\tButton btn = (Button) convertView.findViewById(R.id.btn);\n\t\t\tbtn.setOnClickListener(MainActivity.this);\n\t\t\tbtn.setTag(position);\n\t\t\tbtn.setText(items[position]);\n\t\t\t\n\t\t\treturn convertView;\n\t\t}\n\n\t\t@Override\n\t\tpublic int getCount() {\n\t\t\treturn items.length;\n\t\t}\n\n\t\t@Override\n\t\tpublic Object getItem(int position) {\n\t\t\treturn null;\n\t\t}\n\n\t\t@Override\n\t\tpublic long getItemId(int position) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tprivate void showTip(final String str) {\n\t\tmToast.setText(str);\n\t\tmToast.show();\n\t}\n\n\t@Override\n\tprotected void onResume() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onResume(MainActivity.this);\n\t\tFlowerCollector.onPageStart(TAG);\n\t\tsuper.onResume();\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\t// 开放统计 移动数据统计分析\n\t\tFlowerCollector.onPageEnd(TAG);\n\t\tFlowerCollector.onPause(MainActivity.this);\n\t\tsuper.onPause();\n\t}\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/voicedemo/SpeechApp.java",
    "content": "package com.iflytek.voicedemo;\n\nimport android.app.Application;\nimport com.iflytek.cloud.Setting;\nimport com.iflytek.cloud.SpeechUtility;\n\npublic class SpeechApp extends Application {\n\n\t@Override\n\tpublic void onCreate() {\n\t\t// 应用程序入口处调用，避免手机内存过小，杀死后台进程后通过历史intent进入Activity造成SpeechUtility对象为null\n\t\t// 如在Application中调用初始化，需要在Mainifest中注册该Applicaiton\n\t\t// 注意：此接口在非主进程调用会返回null对象，如需在非主进程使用语音功能，请增加参数：SpeechConstant.FORCE_LOGIN+\"=true\"\n\t\t// 参数间使用半角“,”分隔。\n\t\t// 设置你申请的应用appid,请勿在'='与appid之间添加空格及空转义符\n\t\t\n\t\t// 注意： appid 必须和下载的SDK保持一致，否则会出现10407错误\n\t\t\n\t\tSpeechUtility.createUtility(SpeechApp.this, \"appid=\" + getString(R.string.app_id));\n\t\t\t\n\t\t// 以下语句用于设置日志开关（默认开启），设置成false时关闭语音云SDK日志打印\n\t\t// Setting.setShowLog(false);\n\t\tsuper.onCreate();\n\t}\n\t\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/voicedemo/TtsDemo.java",
    "content": "package com.iflytek.voicedemo;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.Window;\nimport android.widget.EditText;\nimport android.widget.RadioGroup;\nimport android.widget.RadioGroup.OnCheckedChangeListener;\nimport android.widget.Toast;\n\nimport com.iflytek.cloud.ErrorCode;\nimport com.iflytek.cloud.InitListener;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechSynthesizer;\nimport com.iflytek.cloud.SpeechUtility;\nimport com.iflytek.cloud.SynthesizerListener;\nimport com.iflytek.speech.setting.TtsSettings;\nimport com.iflytek.speech.util.ApkInstaller;\nimport com.iflytek.sunflower.FlowerCollector;\n\npublic class TtsDemo extends Activity implements OnClickListener {\n\tprivate static String TAG = TtsDemo.class.getSimpleName(); \t\n\t// 语音合成对象\n\tprivate SpeechSynthesizer mTts;\n\n\t// 默认发音人\n\tprivate String voicer = \"xiaoyan\";\n\t\n\tprivate String[] mCloudVoicersEntries;\n\tprivate String[] mCloudVoicersValue ;\n\t\n\t// 缓冲进度\n\tprivate int mPercentForBuffering = 0;\n\t// 播放进度\n\tprivate int mPercentForPlaying = 0;\n\t\n\t// 云端/本地单选按钮\n\tprivate RadioGroup mRadioGroup;\n\t// 引擎类型\n\tprivate String mEngineType = SpeechConstant.TYPE_CLOUD;\n\t// 语记安装助手类\n\tApkInstaller mInstaller ;\n\t\n\tprivate Toast mToast;\n\tprivate SharedPreferences mSharedPreferences;\n\t\n\t@SuppressLint(\"ShowToast\")\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.ttsdemo);\n\t\t\n\t\tinitLayout();\n\t\t// 初始化合成对象\n\t\tmTts = SpeechSynthesizer.createSynthesizer(TtsDemo.this, mTtsInitListener);\n\t\t\n\t\t// 云端发音人名称列表\n\t\tmCloudVoicersEntries = getResources().getStringArray(R.array.voicer_cloud_entries);\n\t\tmCloudVoicersValue = getResources().getStringArray(R.array.voicer_cloud_values);\n\t\t\t\t\n\t\tmSharedPreferences = getSharedPreferences(TtsSettings.PREFER_NAME, MODE_PRIVATE);\n\t\tmToast = Toast.makeText(this,\"\",Toast.LENGTH_SHORT);\n\t\t\n\t\tmInstaller = new  ApkInstaller(TtsDemo.this);\n\t}\n\n\t/**\n\t * 初始化Layout。\n\t */\n\tprivate void initLayout() {\n\t\tfindViewById(R.id.tts_play).setOnClickListener(TtsDemo.this);\n\t\tfindViewById(R.id.tts_cancel).setOnClickListener(TtsDemo.this);\n\t\tfindViewById(R.id.tts_pause).setOnClickListener(TtsDemo.this);\n\t\tfindViewById(R.id.tts_resume).setOnClickListener(TtsDemo.this);\n\t\tfindViewById(R.id.image_tts_set).setOnClickListener(TtsDemo.this);\n\t\tfindViewById(R.id.tts_btn_person_select).setOnClickListener(TtsDemo.this);\n\t\t\n\t\tmRadioGroup=((RadioGroup) findViewById(R.id.tts_rediogroup));\n\t\tmRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {\n\n\t\t\t@Override\n\t\t\tpublic void onCheckedChanged(RadioGroup group, int checkedId) {\n\t\t\t\tswitch (checkedId) {\n\t\t\t\tcase R.id.tts_radioCloud:\n\t\t\t\t\tmEngineType = SpeechConstant.TYPE_CLOUD;\n\t\t\t\t\tbreak;\n\t\t\t\tcase R.id.tts_radioLocal:\n\t\t\t\t\tmEngineType =  SpeechConstant.TYPE_LOCAL;\n\t\t\t\t\t/**\n\t\t\t\t\t * 选择本地合成\n\t\t\t\t\t * 判断是否安装语记,未安装则跳转到提示安装页面\n\t\t\t\t\t */\n\t\t\t\t\tif (!SpeechUtility.getUtility().checkServiceInstalled()) {\n\t\t\t\t\t\tmInstaller.install();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t}\n\t\t} );\n\t}\t\n\n\t@Override\n\tpublic void onClick(View view) {\n\t\tswitch(view.getId()) {\n\t\tcase R.id.image_tts_set:\n\t\t\tif(SpeechConstant.TYPE_CLOUD.equals(mEngineType)){\n\t\t\t\tIntent intent = new Intent(TtsDemo.this, TtsSettings.class);\n\t\t\t\tstartActivity(intent);\n\t\t\t}else{\n\t\t\t\t// 本地设置跳转到语记中\n\t\t\t\tif (!SpeechUtility.getUtility().checkServiceInstalled()) {\n\t\t\t\t\tmInstaller.install();\n\t\t\t\t}else {\n\t\t\t\t\tSpeechUtility.getUtility().openEngineSettings(null);\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t// 开始合成\n\t\t// 收到onCompleted 回调时，合成结束、生成合成音频\n        // 合成的音频格式：只支持pcm格式\n\t\tcase R.id.tts_play:\n\t\t\t// 移动数据分析，收集开始合成事件\n\t\t\tFlowerCollector.onEvent(TtsDemo.this, \"tts_play\");\n\t\t\t\n\t\t\tString text = ((EditText) findViewById(R.id.tts_text)).getText().toString();\n\t\t\t// 设置参数\n\t\t\tsetParam();\n\t\t\tint code = mTts.startSpeaking(text, mTtsListener);\n//\t\t\t/** \n//\t\t\t * 只保存音频不进行播放接口,调用此接口请注释startSpeaking接口\n//\t\t\t * text:要合成的文本，uri:需要保存的音频全路径，listener:回调接口\n//\t\t\t*/\n//\t\t\tString path = Environment.getExternalStorageDirectory()+\"/tts.pcm\";\n//\t\t\tint code = mTts.synthesizeToUri(text, path, mTtsListener);\n\t\t\t\n\t\t\tif (code != ErrorCode.SUCCESS) {\n\t\t\t\tif(code == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED){\n\t\t\t\t\t//未安装则跳转到提示安装页面\n\t\t\t\t\tmInstaller.install();\n\t\t\t\t}else {\n\t\t\t\t\tshowTip(\"语音合成失败,错误码: \" + code);\t\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t// 取消合成\n\t\tcase R.id.tts_cancel:\n\t\t\tmTts.stopSpeaking();\n\t\t\tbreak;\n\t\t// 暂停播放\n\t\tcase R.id.tts_pause:\n\t\t\tmTts.pauseSpeaking();\n\t\t\tbreak;\n\t\t// 继续播放\n\t\tcase R.id.tts_resume:\n\t\t\tmTts.resumeSpeaking();\n\t\t\tbreak;\n\t\t// 选择发音人\n\t\tcase R.id.tts_btn_person_select:\n\t\t\tshowPresonSelectDialog();\n\t\t\tbreak;\n\t\t}\n\t}\n\tprivate int selectedNum = 0;\n\t/**\n\t * 发音人选择。\n\t */\n\tprivate void showPresonSelectDialog() {\n\t\tswitch (mRadioGroup.getCheckedRadioButtonId()) {\n\t\t// 选择在线合成\n\t\tcase R.id.tts_radioCloud:\t\t\t\n\t\t\tnew AlertDialog.Builder(this).setTitle(\"在线合成发音人选项\")\n\t\t\t\t.setSingleChoiceItems(mCloudVoicersEntries, // 单选框有几项,各是什么名字\n\t\t\t\t\t\tselectedNum, // 默认的选项\n\t\t\t\t\t\tnew DialogInterface.OnClickListener() { // 点击单选框后的处理\n\t\t\t\t\tpublic void onClick(DialogInterface dialog,\n\t\t\t\t\t\t\tint which) { // 点击了哪一项\n\t\t\t\t\t\tvoicer = mCloudVoicersValue[which];\n\t\t\t\t\t\tif (\"catherine\".equals(voicer) || \"henry\".equals(voicer) || \"vimary\".equals(voicer)) {\n\t\t\t\t\t\t\t ((EditText) findViewById(R.id.tts_text)).setText(R.string.text_tts_source_en);\n\t\t\t\t\t\t}else {\n\t\t\t\t\t\t\t((EditText) findViewById(R.id.tts_text)).setText(R.string.text_tts_source);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tselectedNum = which;\n\t\t\t\t\t\tdialog.dismiss();\n\t\t\t\t\t}\n\t\t\t\t}).show();\n\t\t\tbreak;\n\t\t\t\n\t\t// 选择本地合成\n\t\tcase R.id.tts_radioLocal:\n\t\t\tif (!SpeechUtility.getUtility().checkServiceInstalled()) {\n\t\t\t\tmInstaller.install();\n\t\t\t}else {\n\t\t\t\tSpeechUtility.getUtility().openEngineSettings(SpeechConstant.ENG_TTS);\t\t\t\t\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * 初始化监听。\n\t */\n\tprivate InitListener mTtsInitListener = new InitListener() {\n\t\t@Override\n\t\tpublic void onInit(int code) {\n\t\t\tLog.d(TAG, \"InitListener init() code = \" + code);\n\t\t\tif (code != ErrorCode.SUCCESS) {\n        \t\tshowTip(\"初始化失败,错误码：\"+code);\n        \t} else {\n\t\t\t\t// 初始化成功，之后可以调用startSpeaking方法\n        \t\t// 注：有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成，\n        \t\t// 正确的做法是将onCreate中的startSpeaking调用移至这里\n\t\t\t}\t\t\n\t\t}\n\t};\n\n\t/**\n\t * 合成回调监听。\n\t */\n\tprivate SynthesizerListener mTtsListener = new SynthesizerListener() {\n\t\t\n\t\t@Override\n\t\tpublic void onSpeakBegin() {\n\t\t\tshowTip(\"开始播放\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void onSpeakPaused() {\n\t\t\tshowTip(\"暂停播放\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void onSpeakResumed() {\n\t\t\tshowTip(\"继续播放\");\n\t\t}\n\n\t\t@Override\n\t\tpublic void onBufferProgress(int percent, int beginPos, int endPos,\n\t\t\t\tString info) {\n\t\t\t// 合成进度\n\t\t\tmPercentForBuffering = percent;\n\t\t\tshowTip(String.format(getString(R.string.tts_toast_format),\n\t\t\t\t\tmPercentForBuffering, mPercentForPlaying));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onSpeakProgress(int percent, int beginPos, int endPos) {\n\t\t\t// 播放进度\n\t\t\tmPercentForPlaying = percent;\n\t\t\tshowTip(String.format(getString(R.string.tts_toast_format),\n\t\t\t\t\tmPercentForBuffering, mPercentForPlaying));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onCompleted(SpeechError error) {\n\t\t\tif (error == null) {\n\t\t\t\tshowTip(\"播放完成\");\n\t\t\t} else if (error != null) {\n\t\t\t\tshowTip(error.getPlainDescription(true));\n\t\t\t}\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {\n\t\t\t// 以下代码用于获取与云端的会话id，当业务出错时将会话id提供给技术支持人员，可用于查询会话日志，定位出错原因\n\t\t\t// 若使用本地能力，会话id为null\n\t\t\t//\tif (SpeechEvent.EVENT_SESSION_ID == eventType) {\n\t\t\t//\t\tString sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);\n\t\t\t//\t\tLog.d(TAG, \"session id =\" + sid);\n\t\t\t//\t}\n\t\t}\n\t};\n\n\tprivate void showTip(final String str) {\n\t\tmToast.setText(str);\n\t\tmToast.show();\n\t}\n\n\t/**\n\t * 参数设置\n\t * @param param\n\t * @return \n\t */\n\tprivate void setParam(){\n\t\t// 清空参数\n\t\tmTts.setParameter(SpeechConstant.PARAMS, null);\n\t\t// 根据合成引擎设置相应参数\n\t\tif(mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {\n\t\t\tmTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);\n\t\t\t// 设置在线合成发音人\n\t\t\tmTts.setParameter(SpeechConstant.VOICE_NAME, voicer);\n\t\t\t//设置合成语速\n\t\t\tmTts.setParameter(SpeechConstant.SPEED, mSharedPreferences.getString(\"speed_preference\", \"50\"));\n\t\t\t//设置合成音调\n\t\t\tmTts.setParameter(SpeechConstant.PITCH, mSharedPreferences.getString(\"pitch_preference\", \"50\"));\n\t\t\t//设置合成音量\n\t\t\tmTts.setParameter(SpeechConstant.VOLUME, mSharedPreferences.getString(\"volume_preference\", \"50\"));\n\t\t}else {\n\t\t\tmTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);\n\t\t\t// 设置本地合成发音人 voicer为空，默认通过语记界面指定发音人。\n\t\t\tmTts.setParameter(SpeechConstant.VOICE_NAME, \"\");\n\t\t\t/**\n\t\t\t * TODO 本地合成不设置语速、音调、音量，默认使用语记设置\n\t\t\t * 开发者如需自定义参数，请参考在线合成参数设置\n\t\t\t */\n\t\t}\n\t\t//设置播放器音频流类型\n\t\tmTts.setParameter(SpeechConstant.STREAM_TYPE, mSharedPreferences.getString(\"stream_preference\", \"3\"));\n\t\t// 设置播放合成音频打断音乐播放，默认为true\n\t\tmTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, \"true\");\n\t\t\n\t\t// 设置音频保存路径，保存音频格式支持pcm、wav，设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限\n\t\t// 注：AUDIO_FORMAT参数语记需要更新版本才能生效\n\t\tmTts.setParameter(SpeechConstant.AUDIO_FORMAT, \"wav\");\n\t\tmTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory()+\"/msc/tts.wav\");\n\t}\n\t\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\tmTts.stopSpeaking();\n\t\t// 退出时释放连接\n\t\tmTts.destroy();\n\t}\n\t\n\t@Override\n\tprotected void onResume() {\n\t\t//移动数据统计分析\n\t\tFlowerCollector.onResume(TtsDemo.this);\n\t\tFlowerCollector.onPageStart(TAG);\n\t\tsuper.onResume();\n\t}\n\t@Override\n\tprotected void onPause() {\n\t\t//移动数据统计分析\n\t\tFlowerCollector.onPageEnd(TAG);\n\t\tFlowerCollector.onPause(TtsDemo.this);\n\t\tsuper.onPause();\n\t}\n\n}\n"
  },
  {
    "path": "speechDemo/src/main/java/com/iflytek/voicedemo/UnderstanderDemo.java",
    "content": "package com.iflytek.voicedemo;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.view.Window;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\nimport com.iflytek.cloud.ErrorCode;\nimport com.iflytek.cloud.InitListener;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechUnderstander;\nimport com.iflytek.cloud.SpeechUnderstanderListener;\nimport com.iflytek.cloud.TextUnderstander;\nimport com.iflytek.cloud.TextUnderstanderListener;\nimport com.iflytek.cloud.UnderstanderResult;\nimport com.iflytek.speech.setting.UnderstanderSettings;\nimport com.iflytek.sunflower.FlowerCollector;\n\npublic class UnderstanderDemo extends Activity implements OnClickListener {\n\tprivate static String TAG = UnderstanderDemo.class.getSimpleName();\n\t// 语义理解对象（语音到语义）。\n\tprivate SpeechUnderstander mSpeechUnderstander;\n\t// 语义理解对象（文本到语义）。\n\tprivate TextUnderstander   mTextUnderstander;\t\n\tprivate Toast mToast;\t\n\tprivate EditText mUnderstanderText;\n\t\n\tprivate SharedPreferences mSharedPreferences;\n\t\n\t@SuppressLint(\"ShowToast\")\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\trequestWindowFeature(Window.FEATURE_NO_TITLE);\n\t\tsetContentView(R.layout.understander);\n\t\t\n\t\tinitLayout();\n\t\t/**\n\t\t * 申请的appid时，我们为开发者开通了开放语义（语义理解）\n\t\t * 由于语义理解的场景繁多，需开发自己去开放语义平台：http://www.xfyun.cn/services/osp\n\t\t * 配置相应的语音场景，才能使用语义理解，否则文本理解将不能使用，语义理解将返回听写结果。\n\t\t */\n\t\t// 初始化对象\n\t\tmSpeechUnderstander = SpeechUnderstander.createUnderstander(UnderstanderDemo.this, mSpeechUdrInitListener);\n\t\tmTextUnderstander = TextUnderstander.createTextUnderstander(UnderstanderDemo.this, mTextUdrInitListener);\n\t\t\n\t\tmToast = Toast.makeText(UnderstanderDemo.this, \"\", Toast.LENGTH_SHORT);\n\t}\n\t\n\t/**\n\t * 初始化Layout。\n\t */\n\tprivate void initLayout(){\n\t\tfindViewById(R.id.text_understander).setOnClickListener(UnderstanderDemo.this);\n\t\tfindViewById(R.id.start_understander).setOnClickListener(UnderstanderDemo.this);\n\t\t\n\t\tmUnderstanderText = (EditText)findViewById(R.id.understander_text);\n\t\t\n\t\tfindViewById(R.id.understander_stop).setOnClickListener(UnderstanderDemo.this);\n\t\tfindViewById(R.id.understander_cancel).setOnClickListener(UnderstanderDemo.this);\n\t\tfindViewById(R.id.image_understander_set).setOnClickListener(UnderstanderDemo.this);\n\t\t\n\t\tmSharedPreferences = getSharedPreferences(UnderstanderSettings.PREFER_NAME, Activity.MODE_PRIVATE);\n\t}\n\t\n    /**\n     * 初始化监听器（语音到语义）。\n     */\n    private InitListener mSpeechUdrInitListener = new InitListener() {\n    \t\n\t\t@Override\n\t\tpublic void onInit(int code) {\n\t\t\tLog.d(TAG, \"speechUnderstanderListener init() code = \" + code);\n\t\t\tif (code != ErrorCode.SUCCESS) {\n        \t\tshowTip(\"初始化失败,错误码：\"+code);\n        \t}\t\t\t\n\t\t}\n    };\n    \n    /**\n     * 初始化监听器（文本到语义）。\n     */\n    private InitListener mTextUdrInitListener = new InitListener() {\n\n\t\t@Override\n\t\tpublic void onInit(int code) {\n\t\t\tLog.d(TAG, \"textUnderstanderListener init() code = \" + code);\n\t\t\tif (code != ErrorCode.SUCCESS) {\n        \t\tshowTip(\"初始化失败,错误码：\"+code);\n        \t}\n\t\t}\n    };\n\t\n    \n\tint ret = 0;// 函数调用返回值\n\t@Override\n\tpublic void onClick(View view) {\t\t\t\t\n\t\tswitch (view.getId()) {\n\t\t// 进入参数设置页面\n\t\tcase R.id.image_understander_set:\n\t\t\tIntent intent = new Intent(UnderstanderDemo.this, UnderstanderSettings.class);\n\t\t\tstartActivity(intent);\n\t\t\tbreak;\n\t\t// 开始文本理解\n\t\tcase R.id.text_understander:\n\t\t\tmUnderstanderText.setText(\"\");\n\t\t\tString text = \"合肥明天的天气怎么样？\";\t\n\t\t\tshowTip(text);\n\t\t\t\n\t\t\tif(mTextUnderstander.isUnderstanding()){\n\t\t\t\tmTextUnderstander.cancel();\n\t\t\t\tshowTip(\"取消\");\n\t\t\t}else {\n\t\t\t\tret = mTextUnderstander.understandText(text, mTextUnderstanderListener);\n\t\t\t\tif(ret != 0)\n\t\t\t\t{\n\t\t\t\t\tshowTip(\"语义理解失败,错误码:\"+ ret);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t// 开始语音理解\n\t\tcase R.id.start_understander:\n\t\t\tmUnderstanderText.setText(\"\");\n\t\t\t// 设置参数\n\t\t\tsetParam();\n\t\n\t\t\tif(mSpeechUnderstander.isUnderstanding()){// 开始前检查状态\n\t\t\t\tmSpeechUnderstander.stopUnderstanding();\n\t\t\t\tshowTip(\"停止录音\");\n\t\t\t}else {\n\t\t\t\tret = mSpeechUnderstander.startUnderstanding(mSpeechUnderstanderListener);\n\t\t\t\tif(ret != 0){\n\t\t\t\t\tshowTip(\"语义理解失败,错误码:\"\t+ ret);\n\t\t\t\t}else {\n\t\t\t\t\tshowTip(getString(R.string.text_begin));\n\t\t\t\t}\n\t\t\t}\n\t\t\tbreak;\n\t\t// 停止语音理解\n\t\tcase R.id.understander_stop:\n\t\t\tmSpeechUnderstander.stopUnderstanding();\n\t\t\tshowTip(\"停止语义理解\");\n\t\t\tbreak;\n\t\t// 取消语音理解\n\t\tcase R.id.understander_cancel:\n\t\t\tmSpeechUnderstander.cancel();\n\t\t\tshowTip(\"取消语义理解\");\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\tprivate TextUnderstanderListener mTextUnderstanderListener = new TextUnderstanderListener() {\n\t\t\n\t\t@Override\n\t\tpublic void onResult(final UnderstanderResult result) {\n\t\t\tif (null != result) {\n\t\t\t\t// 显示\n\t\t\t\tString text = result.getResultString();\n\t\t\t\tif (!TextUtils.isEmpty(text)) {\n\t\t\t\t\tmUnderstanderText.setText(text);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.d(TAG, \"understander result:null\");\n\t\t\t\tshowTip(\"识别结果不正确。\");\n\t\t\t}\n\t\t}\n\t\t\n\t\t@Override\n\t\tpublic void onError(SpeechError error) {\n\t\t\t// 文本语义不能使用回调错误码14002，请确认您下载sdk时是否勾选语义场景和私有语义的发布\n\t\t\tshowTip(\"onError Code：\"\t+ error.getErrorCode());\n\t\t\t\n\t\t}\n\t};\n\t\n    /**\n     * 语义理解回调。\n     */\n    private SpeechUnderstanderListener mSpeechUnderstanderListener = new SpeechUnderstanderListener() {\n\n\t\t@Override\n\t\tpublic void onResult(final UnderstanderResult result) {\n\t\t\tif (null != result) {\n\t\t\t\tLog.d(TAG, result.getResultString());\n\t\t\t\t\n\t\t\t\t// 显示\n\t\t\t\tString text = result.getResultString();\n\t\t\t\tif (!TextUtils.isEmpty(text)) {\n\t\t\t\t\tmUnderstanderText.setText(text);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tshowTip(\"识别结果不正确。\");\n\t\t\t}\t\n\t\t}\n    \t\n        @Override\n        public void onVolumeChanged(int volume, byte[] data) {\n        \tshowTip(\"当前正在说话，音量大小：\" + volume);\n        \tLog.d(TAG, data.length+\"\");\n        }\n        \n        @Override\n        public void onEndOfSpeech() {\n        \t// 此回调表示：检测到了语音的尾端点，已经进入识别过程，不再接受语音输入\n        \tshowTip(\"结束说话\");\n        }\n        \n        @Override\n        public void onBeginOfSpeech() {\n        \t// 此回调表示：sdk内部录音机已经准备好了，用户可以开始语音输入\n        \tshowTip(\"开始说话\");\n        }\n\n\t\t@Override\n\t\tpublic void onError(SpeechError error) {\n\t\t\tshowTip(error.getPlainDescription(true));\n\t\t}\n\n\t\t@Override\n\t\tpublic void onEvent(int eventType, int arg1, int arg2, Bundle obj) {\n\t\t\t// 以下代码用于获取与云端的会话id，当业务出错时将会话id提供给技术支持人员，可用于查询会话日志，定位出错原因\n\t\t\t//\tif (SpeechEvent.EVENT_SESSION_ID == eventType) {\n\t\t\t//\t\tString sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);\n\t\t\t//\t\tLog.d(TAG, \"session id =\" + sid);\n\t\t\t//\t}\n\t\t}\n    };\n    \n    @Override\n    protected void onDestroy() {\n    \tsuper.onDestroy();\n        // 退出时释放连接\n    \tmSpeechUnderstander.cancel();\n    \tmSpeechUnderstander.destroy();\n    \tif(mTextUnderstander.isUnderstanding())\n    \t\tmTextUnderstander.cancel();\n    \tmTextUnderstander.destroy();    \n    }\n\t\n\tprivate void showTip(final String str) {\n\t\tmToast.setText(str);\n\t\tmToast.show();\n\t}\n\t\n\t/**\n\t * 参数设置\n\t * @param param\n\t * @return \n\t */\n\tpublic void setParam(){\n\t\tString lang = mSharedPreferences.getString(\"understander_language_preference\", \"mandarin\");\n\t\tif (lang.equals(\"en_us\")) {\n\t\t\t// 设置语言\n\t\t\tmSpeechUnderstander.setParameter(SpeechConstant.LANGUAGE, \"en_us\");\n\t\t}else {\n\t\t\t// 设置语言\n\t\t\tmSpeechUnderstander.setParameter(SpeechConstant.LANGUAGE, \"zh_cn\");\n\t\t\t// 设置语言区域\n\t\t\tmSpeechUnderstander.setParameter(SpeechConstant.ACCENT, lang);\n\t\t}\n\t\t// 设置语音前端点:静音超时时间，即用户多长时间不说话则当做超时处理\n\t\tmSpeechUnderstander.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString(\"understander_vadbos_preference\", \"4000\"));\n\t\t\n\t\t// 设置语音后端点:后端点静音检测时间，即用户停止说话多长时间内即认为不再输入， 自动停止录音\n\t\tmSpeechUnderstander.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString(\"understander_vadeos_preference\", \"1000\"));\n\t\t\n\t\t// 设置标点符号，默认：1（有标点）\n\t\tmSpeechUnderstander.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString(\"understander_punc_preference\", \"1\"));\n\t\t\n\t\t// 设置音频保存路径，保存音频格式支持pcm、wav，设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限\n\t\t// 注：AUDIO_FORMAT参数语记需要更新版本才能生效\n\t\tmSpeechUnderstander.setParameter(SpeechConstant.AUDIO_FORMAT, \"wav\");\n\t\tmSpeechUnderstander.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+\"/msc/sud.wav\");\n\t}\t\n\t\n\t@Override\n\tprotected void onResume() {\n\t\t//移动数据统计分析\n\t\tFlowerCollector.onResume(UnderstanderDemo.this);\n\t\tFlowerCollector.onPageStart(TAG);\n\t\tsuper.onResume();\n\t}\n\t\n\t@Override\n\tprotected void onPause() {\n\t\t//移动数据统计分析\n\t\tFlowerCollector.onPageEnd(TAG);\n\t\tFlowerCollector.onPause(UnderstanderDemo.this);\n\t\tsuper.onPause();\n\t}\n\t\n}\n"
  },
  {
    "path": "speechDemo/src/main/res/drawable/list_bg_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\" android:drawable=\"@color/list_backgroud_color\"></item>\n    <item android:drawable=\"@drawable/layout_backgroud\"></item>\n</selector>"
  },
  {
    "path": "speechDemo/src/main/res/drawable/main_setting_btn_np.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/setting\" android:state_focused=\"true\" android:state_pressed=\"false\"/>\n    <item android:drawable=\"@drawable/setting_p\" android:state_focused=\"true\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/setting_p\" android:state_focused=\"false\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/setting\"></item>\n\n</selector>"
  },
  {
    "path": "speechDemo/src/main/res/layout/iatdemo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"10dp\"\n    android:paddingRight=\"10dp\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"讯飞听写示例\"\n            android:textSize=\"30sp\" />\n\n        <ImageButton\n            android:id=\"@+id/image_iat_set\"\n            android:layout_width=\"35dp\"\n            android:layout_height=\"35dp\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"@drawable/main_setting_btn_np\" />\n    </RelativeLayout>\n\n    <EditText\n        android:id=\"@+id/iat_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"top|left\"\n        android:paddingBottom=\"20dp\"\n        android:textSize=\"20sp\" />\n\n    <RadioGroup\n        android:id=\"@+id/radioGroup\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:contentDescription=\"听写类型\"\n        android:orientation=\"horizontal\" >\n\n        <RadioButton\n            android:id=\"@+id/iatRadioCloud\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:checked=\"true\"\n            android:text=\"云端\" >\n        </RadioButton>\n\n        <RadioButton\n            android:id=\"@+id/iatRadioLocal\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"本地\" >\n        </RadioButton>\n\n        <RadioButton\n            android:id=\"@+id/iatRadioMix\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"混合\" >\n        </RadioButton>\n    </RadioGroup>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/iat_recognize\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"开始\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/iat_stop\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"停止\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/iat_cancel\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"取消\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n    <Button\n        android:id=\"@+id/iat_recognize_stream\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:text=\"音频流识别\"\n        android:textSize=\"20sp\" />\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:text=\"上传联系人、词表，可以使云端识别联系人和词表更加准确，这里上传的内容对语义理解同样有效。\"\n        android:textSize=\"@dimen/txt_size\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/iat_upload_contacts\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"上传联系人\"\n            android:textSize=\"17sp\" />\n\n        <Button\n            android:id=\"@+id/iat_upload_userwords\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"上传词表\"\n            android:textSize=\"17sp\" />\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "speechDemo/src/main/res/layout/isedemo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"10dp\"\n    android:paddingRight=\"10dp\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"讯飞评测示例\"\n            android:textSize=\"30sp\" />\n\n        <ImageButton\n            android:id=\"@+id/image_ise_set\"\n            android:layout_width=\"35dp\"\n            android:layout_height=\"35dp\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"@drawable/main_setting_btn_np\" />\n    </RelativeLayout>\n\n    <EditText\n        android:id=\"@+id/ise_eva_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"120dp\"\n        android:layout_marginBottom=\"5dp\"\n        android:gravity=\"top|left\"\n        android:textSize=\"20sp\" />\n\n    <EditText\n        android:id=\"@+id/ise_result_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"200dp\"\n        android:layout_marginBottom=\"5dp\"\n        android:layout_weight=\"1\"\n        android:editable=\"false\"\n        android:gravity=\"top|left\"\n        android:hint=\"请点击“开始评测”按钮\"\n        android:textSize=\"16sp\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"5dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/ise_start\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"开始评测\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/ise_stop\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"停止评测\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/ise_cancel\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"取消评测\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/ise_parse\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"结果解析\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "speechDemo/src/main/res/layout/isrdemo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"10dp\"\n    android:paddingRight=\"10dp\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"讯飞语法示例\"\n            android:textSize=\"30sp\" />\n    </RelativeLayout>\n\n    <EditText\n        android:id=\"@+id/isr_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"top|left\"\n        android:textSize=\"20sp\" />\n\n    <RadioGroup\n        android:id=\"@+id/radioGroup\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:contentDescription=\"识别类型\"\n        android:orientation=\"horizontal\" >\n\n        <RadioButton\n            android:id=\"@+id/radioCloud\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"云端\" >\n        </RadioButton>\n\n        <RadioButton\n            android:id=\"@+id/radioLocal\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"本地\" >\n        </RadioButton>\n    </RadioGroup>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/isr_grammar\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"构建语法\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/isr_lexcion\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:enabled=\"false\"\n            android:text=\"更新词典\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/isr_recognize\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"开始识别\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/isr_stop\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"停止录音\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/isr_cancel\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"取消\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "speechDemo/src/main/res/layout/list_items.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/list_bg_color\"\n    android:descendantFocusability=\"blocksDescendants\"\n    android:orientation=\"horizontal\" >\n\n    <Button\n        android:id=\"@+id/btn\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"@dimen/margin_\"\n        android:layout_marginRight=\"@dimen/margin_\"\n        android:layout_marginTop=\"@dimen/pading_\"\n        android:textSize=\"@dimen/btn_size\" />\n\n</LinearLayout>"
  },
  {
    "path": "speechDemo/src/main/res/layout/main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/layout_backgroud\"\n    android:orientation=\"vertical\" >\n\n    <include layout=\"@layout/title\" />\n\n    <TextView\n        android:id=\"@+id/edit_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"@dimen/margin_\"\n        android:layout_marginRight=\"@dimen/margin_\"\n        android:text=\"@string/example_explain\"\n        android:textSize=\"@dimen/txt_size\" />\n\n    <ListView\n        android:id=\"@+id/listview_main\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_marginLeft=\"5dp\"\n        android:layout_marginRight=\"5dp\"\n        android:cacheColorHint=\"@color/white\"\n        android:divider=\"@drawable/line_background\"\n        android:dividerHeight=\"@dimen/line_height\"\n        android:paddingBottom=\"5dp\" />\n\n</LinearLayout>"
  },
  {
    "path": "speechDemo/src/main/res/layout/title.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"top\"\n    android:gravity=\"center\" >\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\"\n        android:gravity=\"center\"\n        android:text=\"讯飞语音示例\"\n        android:textSize=\"30sp\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "speechDemo/src/main/res/layout/ttsdemo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"10dp\"\n    android:paddingRight=\"10dp\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"讯飞合成示例\"\n            android:textSize=\"30sp\" />\n\n        <ImageButton\n            android:id=\"@+id/image_tts_set\"\n            android:layout_width=\"35dp\"\n            android:layout_height=\"35dp\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"@drawable/main_setting_btn_np\" />\n    </RelativeLayout>\n\n    <EditText\n        android:id=\"@+id/tts_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"top|left\"\n        android:text=\"@string/text_tts_source\"\n        android:textSize=\"20sp\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"5dp\"\n        android:layout_marginTop=\"5dp\"\n        android:orientation=\"horizontal\" >\n\n        <RadioGroup\n            android:id=\"@+id/tts_rediogroup\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"2\"\n            android:orientation=\"horizontal\" >\n\n            <RadioButton\n                android:id=\"@+id/tts_radioCloud\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:checked=\"true\"\n                android:text=\"在线合成\" />\n\n            <RadioButton\n                android:id=\"@+id/tts_radioLocal\"\n                android:layout_width=\"0dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:text=\"本地合成\" />\n        </RadioGroup>\n\n        <Button\n            android:id=\"@+id/tts_btn_person_select\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"发音人\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/tts_play\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"开始合成\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/tts_cancel\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"取消\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/tts_pause\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"暂停播放\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/tts_resume\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"继续播放\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n</LinearLayout>\n"
  },
  {
    "path": "speechDemo/src/main/res/layout/understander.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:focusable=\"true\"\n    android:focusableInTouchMode=\"true\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"10dp\"\n    android:paddingRight=\"10dp\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"10dp\" >\n\n        <TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:text=\"讯飞语义示例\"\n            android:textSize=\"30sp\" />\n\n        <ImageButton\n            android:id=\"@+id/image_understander_set\"\n            android:layout_width=\"35dp\"\n            android:layout_height=\"35dp\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:background=\"@drawable/main_setting_btn_np\" />\n    </RelativeLayout>\n\n    <EditText\n        android:id=\"@+id/understander_text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:gravity=\"top|left\"\n        android:hint=\"@string/text_understand_hint\"\n        android:paddingBottom=\"40dp\"\n        android:textSize=\"20sp\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/text_understander\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"文本语义\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/start_understander\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"语音语义\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"2dp\"\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center_horizontal\"\n        android:orientation=\"horizontal\" >\n\n        <Button\n            android:id=\"@+id/understander_stop\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"停止录音\"\n            android:textSize=\"20sp\" />\n\n        <Button\n            android:id=\"@+id/understander_cancel\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:text=\"取消\"\n            android:textSize=\"20sp\" />\n    </LinearLayout>\n\n</LinearLayout>"
  },
  {
    "path": "speechDemo/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n    <!-- 画面背景色 -->\n    <drawable name=\"layout_backgroud\">#000000</drawable>\n    \n    <!-- 分割线背景色 -->\n    <drawable name=\"line_background\">#161823</drawable>\n\n    <color name=\"white\">#FFFFFF</color>\n    \n    <!-- 控件按下时显示的颜色 -->\n    <color name=\"list_backgroud_color\">#2fb3cb</color>\n    <color name=\"title_color\">#0f8ec5</color>\n    <color name=\"content_color\">#737373</color>\n\n</resources>\n"
  },
  {
    "path": "speechDemo/src/main/res/values/dimen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>    \n    <dimen name=\"margin_\">10dp</dimen>\n    <dimen name=\"pading_\">5dp</dimen>\n    <dimen name=\"btn_size\">22sp</dimen>\n    <dimen name=\"txt_size\">18sp</dimen>\n    \n    <!-- 分割线高度 -->\n    <dimen name=\"line_height\">1dp</dimen>\n</resources>\n"
  },
  {
    "path": "speechDemo/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">讯飞语音示例</string>\n    <!-- 请替换成在语音云官网申请的appid -->\n    <string name=\"app_id\">5838f0d9</string>\n    <string name=\"example_explain\">本示例为讯飞语音Android平台开发者提供语音听写、语法识别、语义理解和语音合成等代码样例，旨在让用户能够依据该示例快速开发出基于语音接口的应用程序。</string>\n    <string name=\"text_tts_source\">科大讯飞作为中国最大的智能语音技术提供商，在智能语音技术领域有着长期的研究积累，并在中文语音合成、语音识别、口语评测等多项技术上拥有国际领先的成果。科大讯飞是我国唯一以语音技术为产业化方向的“国家863计划成果产业化基地”…</string>\n    <string name=\"text_tts_source_en\">iFLYTEK is a national key software enterprise dedicated to the research of intelligent speech and language technologies, development of software and chip products, provision of speech information services, and integration of E-government systems. The intelligent speech technology of iFLYTEK, the core technology of the company, represents the top level in the world.</string>\n    <string name=\"text_isr_abnf_hint\">\\t上传内容为：\\n\\t#ABNF 1.0 gb2312;\\n\\tlanguage zh-CN;\\n\\tmode voice;\\n\\troot $main;\\n\\t$main = $place1 到$place2 ;\\n\\t$place1 = 北京 | 武汉 | 南京 | 天津 | 东京;\\n\\t$place2 = 上海 | 合肥;</string>\n    <string name=\"text_understand_hint\">\\t您可以说：\\n\\t今天的天气怎么样?\\n\\t北京到上海的火车?\\n\\t有什么好吃的?\\n\\t上海外滩有哪些酒店?\\n\\n\\t更多语义请登录：\\n\\thttp://osp.voicecloud.cn/ \\n\\t配置您的专属语义吧!</string>\n    <!-- 听写 -->\n    <string name=\"text_begin\">请开始说话…</string>\n    <string name=\"text_begin_recognizer\">开始音频流识别</string>\n    <string name=\"text_upload_contacts\">上传联系人</string>\n    <string name=\"text_upload_userwords\">上传用户词表</string>\n    <string name=\"text_upload_success\">上传成功</string>\n    <string name=\"text_userword_empty\">词表下载失败或内容为空</string>\n    <string name=\"text_download_success\">下载成功</string>\n    <string name=\"pref_key_iat_show\">iat_show</string>\n    <string name=\"pref_title_iat_show\">显示听写界面</string>\n    <string name=\"pref_title_iat_dwa\">结果动态修正</string>\n    \n    <!-- 合成 -->\n    <string-array name=\"engine_entries\">\n        <item>本地合成</item>\n        <item>在线合成</item>\n    </string-array>\n    <string-array name=\"engine_values\">\n        <item>local</item>\n        <item>cloud</item>\n    </string-array>\n    <string-array name=\"voicer_cloud_entries\">\n        <item>小燕—女青、中英、普通话</item>\n        <item>小宇—男青、中英、普通话</item>\n        <item>凯瑟琳—女青、英</item>\n        <item>亨利—男青、英</item>\n        <item>玛丽—女青、英</item>\n        <item>小研—女青、中英、普通话</item>\n        <item>小琪—女青、中英、普通话</item>\n        <item>小峰—男青、中英、普通话</item>\n        <item>小梅—女青、中英、粤语</item>\n        <item>小莉—女青、中英、台湾普通话</item>\n        <item>小蓉—女青、中、四川话</item>\n        <item>小芸—女青、中、东北话</item>\n        <item>小坤—男青、中、河南话</item>\n        <item>小强—男青、中、湖南话</item>\n        <item>小莹—女青、中、陕西话</item>\n        <item>小新—男童、中、普通话</item>\n        <item>楠楠—女童、中、普通话</item>\n        <item>老孙—男老、中、普通话</item>\n    </string-array>\n    <string-array name=\"voicer_cloud_values\">\n        <item>xiaoyan</item>\n        <item>xiaoyu</item>\n        <item>catherine</item>\n        <item>henry</item>\n        <item>vimary</item>\n        <item>vixy</item>\n        <item>xiaoqi</item>\n        <item>vixf</item>\n        <item>xiaomei</item>\n        <item>xiaolin</item>\n        <item>xiaorong</item>\n        <item>xiaoqian</item>\n        <item>xiaokun</item>\n        <item>xiaoqiang</item>\n        <item>vixying</item>\n        <item>xiaoxin</item>\n        <item>nannan</item>\n        <item>vils</item>\n    </string-array>\n     <string-array name=\"emot_entries\">\n        <item>正常</item>\n        <item>高兴</item>\n        <item>悲伤</item>\n        <item>生气</item>\n    </string-array>\n    <string-array name=\"emot_values\">\n        <item>neutral</item>\n        <item>happy</item>\n        <item>sad</item>\n        <item>angry</item>\n    </string-array>\n    <string-array name=\"stream_entries\">\n        <item>通话</item>\n        <item>系统</item>\n        <item>铃声</item>\n        <item>音乐</item>\n        <item>闹铃</item>\n        <item>通知</item>\n    </string-array>\n    <string-array name=\"stream_values\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n        <item>3</item>\n        <item>4</item>\n        <item>5</item>\n    </string-array>\n    <string formatted=\"false\" name=\"tts_toast_format\">缓冲进度为%d%%，播放进度为%d%%</string>\n    <!-- 语言 -->\n    <string-array name=\"language_entries\">\n        <item>普通话</item>\n        <item>粤语</item>\n        <item>河南话</item>\n        <item>英语</item>\n    </string-array>\n    <string-array name=\"language_values\">\n        <item>mandarin</item>\n        <item>cantonese</item>\n        <item>henanese</item>\n        <item>en_us</item>\n    </string-array>\n    <!-- 语音评测 -->\n    <string name=\"text_en_word\">\"[word]\\napple\\nbanana\\norange\"</string>\n    <string name=\"text_en_sentence\">\"The quick brown fox jumps over the lazy dog.\"</string>\n    <string name=\"text_cn_syllable\">\"知，痴，是\"</string>\n    <string name=\"text_cn_word\">\"磁铁，率领，脆弱，动手，古筝\"</string>\n    <string name=\"text_cn_sentence\">\"一座座雪峰插入云霄，峰顶银光闪闪，大大小小的湖泊，像颗颗宝石镶嵌在彩带般的沟谷中。\"</string>\n    <string-array name=\"ise_language_entries\">\n        <item>英语</item>\n        <item>汉语</item>\n    </string-array>\n    <string-array name=\"ise_language_values\">\n        <item>en_us</item>\n        <item>zh_cn</item>\n    </string-array>\n    <string-array name=\"category_entries\">\n        <item>单字</item>\n        <item>词语</item>\n        <item>句子</item>\n    </string-array>\n    <string-array name=\"category_values\">\n        <item>read_syllable</item>\n        <item>read_word</item>\n        <item>read_sentence</item>\n    </string-array>\n    <string-array name=\"result_level_entries\">\n        <item>plain</item>\n        <item>complete</item>\n    </string-array>\n    <!-- 标点符号 -->\n    <string-array name=\"punc_entries\">\n        <item>有标点</item>\n        <item>无标点</item>\n    </string-array>\n    <string-array name=\"punc_values\">\n        <item>1</item>\n        <item>0</item>\n    </string-array>\n    <string-array name=\"dwa_entries\">\n        <item>开启</item>\n        <item>关闭</item>\n    </string-array>\n</resources>\n"
  },
  {
    "path": "speechDemo/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n      <style name=\"dialog\" parent=\"@android:style/Theme.Dialog\">\n  <item name=\"android:windowFrame\">@null</item>\n  <item name=\"android:windowIsFloating\">true</item>\n  <item name=\"android:windowIsTranslucent\">true</item>\n  <item name=\"android:windowNoTitle\">true</item>\n  <item name=\"android:background\">@android:color/transparent</item>\n  <item name=\"android:windowBackground\">@android:color/transparent</item>\n  <item name=\"android:backgroundDimEnabled\">true</item>\n  <item name=\"android:backgroundDimAmount\">0.6</item>\n </style>\n</resources>"
  },
  {
    "path": "speechDemo/src/main/res/xml/iat_setting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\" > \n    \n    <ListPreference\n        android:key=\"iat_language_preference\"\n        android:title=\"语言设置\"\n        android:entries=\"@array/language_entries\"\n        android:entryValues=\"@array/language_values\"\n        android:summary=\"支持：普通话，粤语，河南话，英语 \"\n        android:defaultValue=\"mandarin\"  />\n    \n    <EditTextPreference\n        android:key=\"iat_vadbos_preference\"   \n\t\tandroid:title=\"前端点超时\" \n\t\tandroid:dialogTitle=\"请输入时间(0-10000)ms\"\n        android:summary=\"默认值：短信转写5000，其他4000\"\n\t\tandroid:defaultValue=\"5000\" />\n    \n    <EditTextPreference\n        android:key=\"iat_vadeos_preference\"   \n\t\tandroid:title=\"后端点超时\" \n\t\tandroid:dialogTitle=\"请输入时间(0-10000)ms\"\n        android:summary=\"默认值：短信转写1800，其他700 \"\n\t\tandroid:defaultValue=\"1800\" />\n\n    <ListPreference\n        android:key=\"iat_punc_preference\"\n        android:title=\"标点符号\"\n        android:entries=\"@array/punc_entries\"\n        android:entryValues=\"@array/punc_values\"\n        android:summary=\"默认值：有标点 \"\n        android:defaultValue=\"1\"  />\n    \n\t<CheckBoxPreference\n\t\tandroid:key=\"@string/pref_key_iat_show\"\n\t\tandroid:title=\"@string/pref_title_iat_show\"\n\t\tandroid:defaultValue=\"true\" />\n\n</PreferenceScreen>\n\n"
  },
  {
    "path": "speechDemo/src/main/res/xml/ise_settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n    \n    <ListPreference \n        android:key=\"language\"\n        android:title=\"评测语种\"\n        android:summary=\"\"\n        android:entries=\"@array/ise_language_entries\"\n        android:entryValues=\"@array/ise_language_values\"\n        android:defaultValue=\"zh_cn\"/>\n    \n    <ListPreference \n        android:key=\"category\"\n        android:title=\"评测题型\"\n        android:summary=\"\"\n        android:entries=\"@array/category_entries\"\n        android:entryValues=\"@array/category_values\"\n        android:defaultValue=\"read_sentence\"/>\n    \n    <ListPreference \n        android:key=\"result_level\"\n        android:title=\"结果等级\"\n        android:summary=\"\"\n        android:entries=\"@array/result_level_entries\"\n        android:entryValues=\"@array/result_level_entries\"\n        android:defaultValue=\"complete\"/>\n    \n    <EditTextPreference \n        android:key=\"vad_bos\"\n        android:title=\"前端点超时\"\n        android:summary=\"\"\n        android:defaultValue=\"5000\"/>\n    \n    <EditTextPreference \n        android:key=\"vad_eos\"\n        android:title=\"后端点超时\"\n        android:summary=\"\"\n        android:defaultValue=\"1800\"/>\n        \n   \t<EditTextPreference \n        android:key=\"speech_timeout\"\n        android:title=\"评测超时\"\n        android:summary=\"\"\n        android:defaultValue=\"-1\"/>\n   \t\t\n    \n</PreferenceScreen>\n"
  },
  {
    "path": "speechDemo/src/main/res/xml/tts_setting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <EditTextPreference\n        android:dialogTitle=\"请输入语速:在线(0-100)本地(0-200)\"\n        android:key=\"speed_preference\"\n        android:defaultValue=\"50\"\n        android:summary=\"默认值：50 \"\n        android:title=\"语速\" />\n    <EditTextPreference\n        android:dialogTitle=\"请输入音调(0-100)\"\n        android:key=\"pitch_preference\"\n        android:defaultValue=\"50\"\n        android:summary=\"默认值：50 \"\n        android:title=\"音调\" />\n    <EditTextPreference\n        android:dialogTitle=\"请输入音量(0-100)\"\n        android:key=\"volume_preference\"\n        android:defaultValue=\"50\"\n        android:summary=\"默认值：50 \"\n        android:title=\"音量\" />\n    <ListPreference\n        android:defaultValue=\"3\"\n        android:entries=\"@array/stream_entries\"\n        android:entryValues=\"@array/stream_values\"\n        android:key=\"stream_preference\"\n        android:title=\"音频流类型\" />\n\n</PreferenceScreen>"
  },
  {
    "path": "speechDemo/src/main/res/xml/understand_setting.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\" > \n    <ListPreference\n        android:key=\"understander_language_preference\"\n        android:title=\"语言设置\"\n        android:entries=\"@array/language_entries\"\n        android:entryValues=\"@array/language_values\"\n        android:summary=\"支持：普通话，粤语，河南话，英语 \"\n        android:defaultValue=\"mandarin\"  />\n    <EditTextPreference\n        android:key=\"understander_vadbos_preference\"   \n\t\tandroid:title=\"前端点超时\" \n\t\tandroid:dialogTitle=\"请输入时间(0-10000)ms\"\n        android:summary=\"默认值：短信转写5000，其他4000\"\n\t\tandroid:defaultValue=\"4000\" />\n    \n    <EditTextPreference\n        android:key=\"understander_vadeos_preference\"   \n\t\tandroid:title=\"后端点超时\" \n\t\tandroid:dialogTitle=\"请输入时间(0-10000)ms\"\n        android:summary=\"默认值：短信转写1800，其他700 \"\n\t\tandroid:defaultValue=\"700\" />\n    \n    <ListPreference\n        android:key=\"understander_punc_preference\"\n        android:title=\"标点符号\"\n        android:entries=\"@array/punc_entries\"\n        android:entryValues=\"@array/punc_values\"\n        android:summary=\"默认值：有标点 \"\n        android:defaultValue=\"1\"  />\n    \n<!--     <CheckBoxPreference -->\n<!--         android:key=\"nbest_preference\" -->\n<!--         android:title=\"@string/set_multiple_candidate_title\" -->\n<!--         android:summary=\"@string/set_multiple_candidate_summary\" -->\n<!--         android:defaultValue=\"false\" /> -->\n\n</PreferenceScreen>\n\n\n"
  },
  {
    "path": "vitamio/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"src\" path=\"gen\"/>\n\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n</classpath>\n"
  },
  {
    "path": "vitamio/.gitignore",
    "content": "/build\n/bin\n\n"
  },
  {
    "path": "vitamio/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>InitActivity</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.ApkBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>com.android.ide.eclipse.adt.AndroidNature</nature>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "vitamio/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.source=1.6\n"
  },
  {
    "path": "vitamio/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"io.vov.vitamio\"\n          android:versionCode=\"400\"\n          android:versionName=\"4.0\">\n\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    \n    <application>\n        <activity\n                android:name=\"io.vov.vitamio.activity.InitActivity\"\n                android:configChanges=\"orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation\"\n                android:launchMode=\"singleTop\"\n                android:theme=\"@android:style/Theme.NoTitleBar\"\n                android:windowSoftInputMode=\"stateAlwaysHidden\"/>\n        \n    </application>\n\n</manifest>\n"
  },
  {
    "path": "vitamio/README.md",
    "content": "Vitamio\n===============\n\nThis folder contains the main library which should be linked against as an\nAndroid library project in your application.\n"
  },
  {
    "path": "vitamio/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\ndependencies {\n}\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"24.0.3\"\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 24\n    }\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            java.srcDirs = ['src']\n            jniLibs.srcDirs = ['libs']\n            aidl.srcDirs = ['src']\n            renderscript.srcDirs = ['src']\n            res.srcDirs = ['res']\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "vitamio/gen/io/vov/vitamio/BuildConfig.java",
    "content": "/** Automatically generated file. DO NOT MODIFY */\npackage io.vov.vitamio;\n\npublic final class BuildConfig {\n    public final static boolean DEBUG = true;\n}"
  },
  {
    "path": "vitamio/gen/io/vov/vitamio/R.java",
    "content": "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n *\n * This class was automatically generated by the\n * aapt tool from the resource data it found.  It\n * should not be modified by hand.\n */\n\npackage io.vov.vitamio;\n\npublic final class R {\n    public static final class attr {\n    }\n    public static final class color {\n        public static int mediacontroller_bg=0x7f040002;\n        public static int mediacontroller_bg_pressed=0x7f040001;\n        public static int transparent=0x7f040000;\n    }\n    public static final class drawable {\n        public static int ic_launcher=0x7f020000;\n        public static int mediacontroller_button=0x7f020001;\n        public static int mediacontroller_pause=0x7f020002;\n        public static int mediacontroller_play=0x7f020003;\n        public static int scrubber_control_disabled_holo=0x7f020004;\n        public static int scrubber_control_focused_holo=0x7f020005;\n        public static int scrubber_control_normal_holo=0x7f020006;\n        public static int scrubber_control_pressed_holo=0x7f020007;\n        public static int scrubber_control_selector_holo=0x7f020008;\n        public static int scrubber_primary_holo=0x7f020009;\n        public static int scrubber_progress_horizontal_holo_dark=0x7f02000a;\n        public static int scrubber_secondary_holo=0x7f02000b;\n        public static int scrubber_track_holo_dark=0x7f02000c;\n    }\n    public static final class id {\n        public static int mediacontroller_file_name=0x7f070004;\n        public static int mediacontroller_play_pause=0x7f070000;\n        public static int mediacontroller_seekbar=0x7f070003;\n        public static int mediacontroller_time_current=0x7f070001;\n        public static int mediacontroller_time_total=0x7f070002;\n    }\n    public static final class layout {\n        public static int mediacontroller=0x7f030000;\n    }\n    public static final class string {\n        public static int VideoView_error_button=0x7f05000b;\n        public static int VideoView_error_text_invalid_progressive_playback=0x7f050009;\n        public static int VideoView_error_text_unknown=0x7f05000a;\n        public static int VideoView_error_title=0x7f050008;\n        public static int mediacontroller_play_pause=0x7f05000c;\n        public static int permission_group_tools_description=0x7f050003;\n        public static int permission_group_tools_label=0x7f050002;\n        public static int permission_receive_messages_description=0x7f050005;\n        public static int permission_receive_messages_label=0x7f050004;\n        public static int permission_write_providers_description=0x7f050007;\n        public static int permission_write_providers_label=0x7f050006;\n        public static int vitamio_init_decoders=0x7f050001;\n        public static int vitamio_library_app_name=0x7f050000;\n    }\n    public static final class style {\n        public static int MediaController_SeekBar=0x7f060000;\n        public static int MediaController_Text=0x7f060001;\n    }\n}\n"
  },
  {
    "path": "vitamio/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# For Vitamio classes\n-keep public class io.vov.vitamio.MediaPlayer { *; }\n-keep public class io.vov.vitamio.IMediaScannerService { *; }\n-keep public class io.vov.vitamio.MediaScanner { *; }\n-keep public class io.vov.vitamio.MediaScannerClient { *; }\n-keep public class io.vov.vitamio.VitamioLicense { *; }\n-keep public class io.vov.vitamio.Vitamio { *; }\n-keep public class io.vov.vitamio.MediaMetadataRetriever { *; }\n"
  },
  {
    "path": "vitamio/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-19\nandroid.library=true\n"
  },
  {
    "path": "vitamio/res/drawable/mediacontroller_button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@color/mediacontroller_bg_pressed\" android:state_focused=\"true\"/>\n    <item android:drawable=\"@color/mediacontroller_bg_pressed\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@color/transparent\" android:state_enabled=\"false\"/>\n    <item android:drawable=\"@color/transparent\"/>\n\n\n</selector>"
  },
  {
    "path": "vitamio/res/drawable/scrubber_control_selector_holo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2011 The Android Open Source Project Licensed under the \n\tApache License, Version 2.0 (the \"License\"); you may not use this file except \n\tin compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 \n\tUnless required by applicable law or agreed to in writing, software distributed \n\tunder the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES \n\tOR CONDITIONS OF ANY KIND, either express or implied. See the License for \n\tthe specific language governing permissions and limitations under the License.\n-->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@drawable/scrubber_control_disabled_holo\" android:state_enabled=\"false\"/>\n    <item android:drawable=\"@drawable/scrubber_control_pressed_holo\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/scrubber_control_focused_holo\" android:state_selected=\"true\"/>\n    <item android:drawable=\"@drawable/scrubber_control_normal_holo\"/>\n\n</selector>"
  },
  {
    "path": "vitamio/res/drawable/scrubber_progress_horizontal_holo_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n     Copyright (C) 2010 The Android Open Source Project Licensed under the \n\tApache License, Version 2.0 (the \"License\"); you may not use this file except \n\tin compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 \n\tUnless required by applicable law or agreed to in writing, software distributed \n\tunder the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES \n\tOR CONDITIONS OF ANY KIND, either express or implied. See the License for \n\tthe specific language governing permissions and limitations under the License.\n-->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <item\n        android:id=\"@android:id/background\"\n        android:drawable=\"@drawable/scrubber_secondary_holo\"/>\n    <item android:id=\"@android:id/secondaryProgress\">\n        <scale\n            android:drawable=\"@drawable/scrubber_secondary_holo\"\n            android:scaleWidth=\"100%\" />\n    </item>\n    <item android:id=\"@android:id/progress\">\n        <scale\n            android:drawable=\"@drawable/scrubber_primary_holo\"\n            android:scaleWidth=\"100%\" />\n    </item>\n\n</layer-list>"
  },
  {
    "path": "vitamio/res/layout/mediacontroller.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"74dp\"\n    android:background=\"@color/mediacontroller_bg\"\n    android:orientation=\"vertical\" >\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\" >\n\n        <ImageButton\n            android:id=\"@+id/mediacontroller_play_pause\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginLeft=\"5dp\"\n            android:background=\"@drawable/mediacontroller_button\"\n            android:contentDescription=\"@string/mediacontroller_play_pause\"\n            android:src=\"@drawable/mediacontroller_pause\" />\n\n        <TextView\n            android:id=\"@+id/mediacontroller_time_current\"\n            style=\"@style/MediaController_Text\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginLeft=\"5dp\"\n            android:layout_toRightOf=\"@id/mediacontroller_play_pause\" />\n\n        <TextView\n            android:id=\"@+id/mediacontroller_time_total\"\n            style=\"@style/MediaController_Text\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginRight=\"5dp\" />\n\n        <SeekBar\n            android:id=\"@+id/mediacontroller_seekbar\"\n            style=\"@style/MediaController_SeekBar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toLeftOf=\"@id/mediacontroller_time_total\"\n            android:layout_toRightOf=\"@id/mediacontroller_time_current\"\n            android:focusable=\"true\"\n            android:max=\"1000\" />\n    </RelativeLayout>\n\n    <TextView\n        android:id=\"@+id/mediacontroller_file_name\"\n        style=\"@style/MediaController_Text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"5dp\"\n        android:ellipsize=\"marquee\"\n        android:singleLine=\"true\" />\n\n</LinearLayout>"
  },
  {
    "path": "vitamio/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<resources>\n\n    <color name=\"transparent\">#00000000</color>\n    <color name=\"mediacontroller_bg_pressed\">#ff53c1bd</color>\n    <color name=\"mediacontroller_bg\">#99000000</color>\n\n</resources>"
  },
  {
    "path": "vitamio/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"vitamio_library_app_name\">VitamioLibrary</string>\n    <string name=\"vitamio_init_decoders\">Initializing decoders…</string>\n    <string name=\"permission_group_tools_label\">Vitamio tools</string>\n    <string name=\"permission_group_tools_description\">Access Vitamio package and resources.</string>\n    <string name=\"permission_receive_messages_label\">Receive Vitamio messages</string>\n    <string name=\"permission_receive_messages_description\">Receive all broadcasts from Vitamio service.</string>\n    <string name=\"permission_write_providers_label\">Write Vitamio providers</string>\n    <string name=\"permission_write_providers_description\">Delete, update or create new items in Vitamio providers.\n    </string>\n    <string name=\"VideoView_error_title\">Cannot play video</string>\n    <string name=\"VideoView_error_text_invalid_progressive_playback\">Sorry, this video is not valid for streaming to\n        this device.\n    </string>\n    <string name=\"VideoView_error_text_unknown\">Sorry, this video cannot be played.</string>\n    <string name=\"VideoView_error_button\">OK</string>\n    <string name=\"mediacontroller_play_pause\">Play/Pause</string>\n\n</resources>"
  },
  {
    "path": "vitamio/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<resources>\n\n    \n    <style name=\"MediaController_SeekBar\" parent=\"android:Widget.SeekBar\">\n        <item name=\"android:progressDrawable\">@drawable/scrubber_progress_horizontal_holo_dark</item>\n        <item name=\"android:indeterminateDrawable\">@drawable/scrubber_progress_horizontal_holo_dark</item>\n        <item name=\"android:minHeight\">13dip</item>\n        <item name=\"android:maxHeight\">13dip</item>\n        <item name=\"android:thumb\">@drawable/scrubber_control_selector_holo</item>\n        <item name=\"android:thumbOffset\">16dip</item>\n        <item name=\"android:paddingLeft\">16dip</item>\n        <item name=\"android:paddingRight\">16dip</item>\n    </style>\n\n    <style name=\"MediaController_Text\">\n        <item name=\"android:textColor\">#ffffffff</item>\n        <item name=\"android:textSize\">14sp</item>\n        <item name=\"android:textStyle\">bold</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/EGL.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\nimport javax.microedition.khronos.egl.EGL10;\nimport javax.microedition.khronos.egl.EGL11;\nimport javax.microedition.khronos.egl.EGLConfig;\nimport javax.microedition.khronos.egl.EGLContext;\nimport javax.microedition.khronos.egl.EGLDisplay;\nimport javax.microedition.khronos.egl.EGLSurface;\nimport javax.microedition.khronos.opengles.GL;\nimport android.util.Log;\nimport android.view.Surface;\n\n\n/**\n * DON'T MODIFY THIS FILE IF YOU'RE NOT FAMILIAR WITH EGL, IT'S USED BY NATIVE CODE!!!\n */\npublic class EGL {\n  private EGL10 mEgl;\n  private EGLDisplay mEglDisplay;\n  private EGLSurface mEglSurface;\n  private EGLConfig mEglConfig;\n  private EGLContext mEglContext;\n  private EGLConfigChooser mEGLConfigChooser;\n  private EGLContextFactory mEGLContextFactory;\n  private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;\n\n  public EGL() {\n    mEGLConfigChooser = new SimpleEGLConfigChooser();\n    mEGLContextFactory = new EGLContextFactory();\n    mEGLWindowSurfaceFactory = new EGLWindowSurfaceFactory();\n  }\n\n  public boolean initialize(Surface surface) {\n    start();\n    return createSurface(surface) != null;\n  }\n\n  public void release() {\n    destroySurface();\n    finish();\n  }\n\n  public void start() {\n    mEgl = (EGL10) EGLContext.getEGL();\n    mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);\n\n    if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {\n      throw new RuntimeException(\"eglGetDisplay failed\");\n    }\n\n    int[] version = new int[2];\n    if (!mEgl.eglInitialize(mEglDisplay, version)) {\n      throw new RuntimeException(\"eglInitialize failed\");\n    }\n    mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);\n\n    mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);\n    if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {\n      mEglContext = null;\n      throwEglException(\"createContext\");\n    }\n\n    mEglSurface = null;\n  }\n\n  public GL createSurface(Surface surface) {\n    if (mEgl == null)\n      throw new RuntimeException(\"egl not initialized\");\n    if (mEglDisplay == null)\n      throw new RuntimeException(\"eglDisplay not initialized\");\n    if (mEglConfig == null)\n      throw new RuntimeException(\"mEglConfig not initialized\");\n\n    if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {\n      mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);\n      mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);\n    }\n\n    mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl, mEglDisplay, mEglConfig, surface);\n\n    if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {\n      int error = mEgl.eglGetError();\n      if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {\n        Log.e(\"EglHelper\", \"createWindowSurface returned EGL_BAD_NATIVE_WINDOW.\");\n        return null;\n      }\n      throwEglException(\"createWindowSurface\", error);\n    }\n\n    if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {\n      throwEglException(\"eglMakeCurrent\");\n    }\n\n    GL gl = mEglContext.getGL();\n\n    return gl;\n  }\n\n  public boolean swap() {\n    if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {\n      int error = mEgl.eglGetError();\n      switch (error) {\n        case EGL11.EGL_CONTEXT_LOST:\n          return false;\n        case EGL10.EGL_BAD_NATIVE_WINDOW:\n          Log.e(\"EglHelper\", \"eglSwapBuffers returned EGL_BAD_NATIVE_WINDOW. tid=\" + Thread.currentThread().getId());\n          break;\n        case EGL10.EGL_BAD_SURFACE:\n          Log.e(\"EglHelper\", \"eglSwapBuffers returned EGL_BAD_SURFACE. tid=\" + Thread.currentThread().getId());\n          return false;\n        default:\n          throwEglException(\"eglSwapBuffers\", error);\n      }\n    }\n    return true;\n  }\n\n  public void destroySurface() {\n    if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {\n      mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);\n      mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);\n      mEglSurface = null;\n    }\n  }\n\n  public void finish() {\n    if (mEglContext != null) {\n      mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);\n      mEglContext = null;\n    }\n    if (mEglDisplay != null) {\n      mEgl.eglTerminate(mEglDisplay);\n      mEglDisplay = null;\n    }\n  }\n\n  private void throwEglException(String function) {\n    throwEglException(function, mEgl.eglGetError());\n  }\n\n  private void throwEglException(String function, int error) {\n    String message = String.format(\"%s failed: %x\", function, error);\n    Log.e(\"EglHelper\", \"throwEglException tid=\" + Thread.currentThread().getId() + \" \" + message);\n    throw new RuntimeException(message);\n  }\n\n  private static class EGLWindowSurfaceFactory {\n\n    public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow) {\n      return egl.eglCreateWindowSurface(display, config, nativeWindow, null);\n    }\n\n    public void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface) {\n      egl.eglDestroySurface(display, surface);\n    }\n  }\n\n  private class EGLContextFactory {\n    private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;\n\n    public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {\n      int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};\n\n      return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrib_list);\n    }\n\n    public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {\n      if (!egl.eglDestroyContext(display, context)) {\n        Log.e(\"DefaultContextFactory\", \"display:\" + display + \" context: \" + context);\n        throw new RuntimeException(\"eglDestroyContext failed: \");\n      }\n    }\n  }\n\n  private abstract class EGLConfigChooser {\n    protected int[] mConfigSpec;\n\n    public EGLConfigChooser(int[] configSpec) {\n      mConfigSpec = filterConfigSpec(configSpec);\n    }\n\n    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {\n      int[] num_config = new int[1];\n      if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config)) {\n        throw new IllegalArgumentException(\"eglChooseConfig failed\");\n      }\n\n      int numConfigs = num_config[0];\n\n      if (numConfigs <= 0) {\n        throw new IllegalArgumentException(\"No configs match configSpec\");\n      }\n\n      EGLConfig[] configs = new EGLConfig[numConfigs];\n      if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) {\n        throw new IllegalArgumentException(\"eglChooseConfig#2 failed\");\n      }\n      EGLConfig config = chooseConfig(egl, display, configs);\n      if (config == null) {\n        throw new IllegalArgumentException(\"No config chosen\");\n      }\n      return config;\n    }\n\n    abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);\n\n    private int[] filterConfigSpec(int[] configSpec) {\n      int len = configSpec.length;\n      int[] newConfigSpec = new int[len + 2];\n      System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);\n      newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;\n      newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */\n      newConfigSpec[len + 1] = EGL10.EGL_NONE;\n      return newConfigSpec;\n    }\n  }\n\n  private class ComponentSizeChooser extends EGLConfigChooser {\n    protected int mRedSize;\n    protected int mGreenSize;\n    protected int mBlueSize;\n    protected int mAlphaSize;\n    protected int mDepthSize;\n    protected int mStencilSize;\n    private int[] mValue;\n\n    public ComponentSizeChooser(int redSize, int greenSize, int blueSize, int alphaSize, int depthSize, int stencilSize) {\n      super(new int[]{EGL10.EGL_RED_SIZE, redSize, EGL10.EGL_GREEN_SIZE, greenSize, EGL10.EGL_BLUE_SIZE, blueSize, EGL10.EGL_ALPHA_SIZE, alphaSize, EGL10.EGL_DEPTH_SIZE, depthSize, EGL10.EGL_STENCIL_SIZE, stencilSize, EGL10.EGL_NONE});\n      mValue = new int[1];\n      mRedSize = redSize;\n      mGreenSize = greenSize;\n      mBlueSize = blueSize;\n      mAlphaSize = alphaSize;\n      mDepthSize = depthSize;\n      mStencilSize = stencilSize;\n    }\n\n    @Override\n    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {\n      for (EGLConfig config : configs) {\n        int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);\n        int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);\n        if ((d >= mDepthSize) && (s >= mStencilSize)) {\n          int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);\n          int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);\n          int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);\n          int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);\n          if ((r == mRedSize) && (g == mGreenSize) && (b == mBlueSize) && (a == mAlphaSize)) {\n            return config;\n          }\n        }\n      }\n      return null;\n    }\n\n    private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attribute, int defaultValue) {\n\n      if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {\n        return mValue[0];\n      }\n      return defaultValue;\n    }\n  }\n\n  private class SimpleEGLConfigChooser extends ComponentSizeChooser {\n    public SimpleEGLConfigChooser() {\n      super(5, 6, 5, 0, 0, 0);\n    }\n  }\n\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/MediaFile.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\nimport java.util.HashMap;\nimport java.util.Iterator;\n\n\npublic class MediaFile {\n\tprotected final static String sFileExtensions;\n\n\tpublic static final int FILE_TYPE_MP3 = 1;\n\tpublic static final int FILE_TYPE_M4A = 2;\n\tpublic static final int FILE_TYPE_WAV = 3;\n\tpublic static final int FILE_TYPE_AMR = 4;\n\tpublic static final int FILE_TYPE_AWB = 5;\n\tpublic static final int FILE_TYPE_WMA = 6;\n\tpublic static final int FILE_TYPE_OGG = 7;\n\tpublic static final int FILE_TYPE_AAC = 8;\n\tpublic static final int FILE_TYPE_MKA = 9;\n\tpublic static final int FILE_TYPE_MID = 10;\n\tpublic static final int FILE_TYPE_SMF = 11;\n\tpublic static final int FILE_TYPE_IMY = 12;\n\tpublic static final int FILE_TYPE_APE = 13;\n\tpublic static final int FILE_TYPE_FLAC = 14;\n\tprivate static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3;\n\tprivate static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_FLAC;\n\n\tpublic static final int FILE_TYPE_MP4 = 701;\n\tpublic static final int FILE_TYPE_M4V = 702;\n\tpublic static final int FILE_TYPE_3GPP = 703;\n\tpublic static final int FILE_TYPE_3GPP2 = 704;\n\tpublic static final int FILE_TYPE_WMV = 705;\n\tpublic static final int FILE_TYPE_ASF = 706;\n\tpublic static final int FILE_TYPE_MKV = 707;\n\tpublic static final int FILE_TYPE_MP2TS = 708;\n\tpublic static final int FILE_TYPE_FLV = 709;\n\tpublic static final int FILE_TYPE_MOV = 710;\n\tpublic static final int FILE_TYPE_RM = 711;\n\tpublic static final int FILE_TYPE_DVD = 712;\n\tpublic static final int FILE_TYPE_DIVX = 713;\n\tpublic static final int FILE_TYPE_OGV = 714;\n\tpublic static final int FILE_TYPE_VIVO = 715;\n\tpublic static final int FILE_TYPE_WTV = 716;\n\tpublic static final int FILE_TYPE_AVS = 717;\n\tpublic static final int FILE_TYPE_SWF = 718;\n\tpublic static final int FILE_TYPE_RAW = 719;\n\tprivate static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4;\n\tprivate static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_RAW;\n\n\tprotected static class MediaFileType {\n\t\tint fileType;\n\t\tString mimeType;\n\n\t\tMediaFileType(int fileType, String mimeType) {\n\t\t\tthis.fileType = fileType;\n\t\t\tthis.mimeType = mimeType;\n\t\t}\n\t}\n\n\tprivate static HashMap<String, MediaFileType> sFileTypeMap = new HashMap<String, MediaFileType>();\n\tprivate static HashMap<String, Integer> sMimeTypeMap = new HashMap<String, Integer>();\n\n\tstatic void addFileType(String extension, int fileType, String mimeType) {\n\t\tsFileTypeMap.put(extension, new MediaFileType(fileType, mimeType));\n\t\tsMimeTypeMap.put(mimeType, Integer.valueOf(fileType));\n\t}\n\n\tstatic {\n\t\t// addFileType(\"MP3\", FILE_TYPE_MP3, \"audio/mpeg\");\n\t\t// addFileType(\"M4A\", FILE_TYPE_M4A, \"audio/mp4\");\n\t\t// addFileType(\"WAV\", FILE_TYPE_WAV, \"audio/x-wav\");\n\t\t// addFileType(\"AMR\", FILE_TYPE_AMR, \"audio/amr\");\n\t\t// addFileType(\"AWB\", FILE_TYPE_AWB, \"audio/amr-wb\");\n\t\t// addFileType(\"WMA\", FILE_TYPE_WMA, \"audio/x-ms-wma\");\n\t\t// addFileType(\"OGG\", FILE_TYPE_OGG, \"application/ogg\");\n\t\t// addFileType(\"OGA\", FILE_TYPE_OGG, \"application/ogg\");\n\t\t// addFileType(\"AAC\", FILE_TYPE_AAC, \"audio/aac\");\n\t\t// addFileType(\"MKA\", FILE_TYPE_MKA, \"audio/x-matroska\");\n\t\t// addFileType(\"MID\", FILE_TYPE_MID, \"audio/midi\");\n\t\t// addFileType(\"MIDI\", FILE_TYPE_MID, \"audio/midi\");\n\t\t// addFileType(\"XMF\", FILE_TYPE_MID, \"audio/midi\");\n\t\t// addFileType(\"RTTTL\", FILE_TYPE_MID, \"audio/midi\");\n\t\t// addFileType(\"SMF\", FILE_TYPE_SMF, \"audio/sp-midi\");\n\t\t// addFileType(\"IMY\", FILE_TYPE_IMY, \"audio/imelody\");\n\t\t// addFileType(\"RTX\", FILE_TYPE_MID, \"audio/midi\");\n\t\t// addFileType(\"OTA\", FILE_TYPE_MID, \"audio/midi\");\n\t\t// addFileType(\"APE\", FILE_TYPE_APE, \"audio/x-ape\");\n\t\t// addFileType(\"FLAC\", FILE_TYPE_FLAC, \"audio/flac\");\n\n\t\taddFileType(\"M1V\", FILE_TYPE_MP4, \"video/mpeg\");\n\t\taddFileType(\"MP2\", FILE_TYPE_MP4, \"video/mpeg\");\n\t\taddFileType(\"MPE\", FILE_TYPE_MP4, \"video/mpeg\");\n\t\taddFileType(\"MPG\", FILE_TYPE_MP4, \"video/mpeg\");\n\t\taddFileType(\"MPEG\", FILE_TYPE_MP4, \"video/mpeg\");\n\t\taddFileType(\"MP4\", FILE_TYPE_MP4, \"video/mp4\");\n\t\taddFileType(\"M4V\", FILE_TYPE_M4V, \"video/mp4\");\n\t\taddFileType(\"3GP\", FILE_TYPE_3GPP, \"video/3gpp\");\n\t\taddFileType(\"3GPP\", FILE_TYPE_3GPP, \"video/3gpp\");\n\t\taddFileType(\"3G2\", FILE_TYPE_3GPP2, \"video/3gpp2\");\n\t\taddFileType(\"3GPP2\", FILE_TYPE_3GPP2, \"video/3gpp2\");\n\t\taddFileType(\"MKV\", FILE_TYPE_MKV, \"video/x-matroska\");\n\t\taddFileType(\"WEBM\", FILE_TYPE_MKV, \"video/x-matroska\");\n\t\taddFileType(\"MTS\", FILE_TYPE_MP2TS, \"video/mp2ts\");\n\t\taddFileType(\"TS\", FILE_TYPE_MP2TS, \"video/mp2ts\");\n\t\taddFileType(\"TP\", FILE_TYPE_MP2TS, \"video/mp2ts\");\n\t\taddFileType(\"WMV\", FILE_TYPE_WMV, \"video/x-ms-wmv\");\n\t\taddFileType(\"ASF\", FILE_TYPE_ASF, \"video/x-ms-asf\");\n\t\taddFileType(\"ASX\", FILE_TYPE_ASF, \"video/x-ms-asf\");\n\t\taddFileType(\"FLV\", FILE_TYPE_FLV, \"video/x-flv\");\n\t\taddFileType(\"F4V\", FILE_TYPE_FLV, \"video/x-flv\");\n\t\taddFileType(\"HLV\", FILE_TYPE_FLV, \"video/x-flv\");\n\t\taddFileType(\"MOV\", FILE_TYPE_MOV, \"video/quicktime\");\n\t\taddFileType(\"QT\", FILE_TYPE_MOV, \"video/quicktime\");\n\t\taddFileType(\"RM\", FILE_TYPE_RM, \"video/x-pn-realvideo\");\n\t\taddFileType(\"RMVB\", FILE_TYPE_RM, \"video/x-pn-realvideo\");\n\t\taddFileType(\"VOB\", FILE_TYPE_DVD, \"video/dvd\");\n\t\taddFileType(\"DAT\", FILE_TYPE_DVD, \"video/dvd\");\n\t\taddFileType(\"AVI\", FILE_TYPE_DIVX, \"video/x-divx\");\n\t\taddFileType(\"OGV\", FILE_TYPE_OGV, \"video/ogg\");\n\t\taddFileType(\"OGG\", FILE_TYPE_OGV, \"video/ogg\");\n\t\taddFileType(\"VIV\", FILE_TYPE_VIVO, \"video/vnd.vivo\");\n\t\taddFileType(\"VIVO\", FILE_TYPE_VIVO, \"video/vnd.vivo\");\n\t\taddFileType(\"WTV\", FILE_TYPE_WTV, \"video/wtv\");\n\t\taddFileType(\"AVS\", FILE_TYPE_AVS, \"video/avs-video\");\n\t\taddFileType(\"SWF\", FILE_TYPE_SWF, \"video/x-shockwave-flash\");\n\t\taddFileType(\"YUV\", FILE_TYPE_RAW, \"video/x-raw-yuv\");\n\n\t\tStringBuilder builder = new StringBuilder();\n\t\tIterator<String> iterator = sFileTypeMap.keySet().iterator();\n\n\t\twhile (iterator.hasNext()) {\n\t\t\tif (builder.length() > 0)\n\t\t\t\tbuilder.append(',');\n\t\t\tbuilder.append(iterator.next());\n\t\t}\n\t\tsFileExtensions = builder.toString();\n\t}\n\n\tpublic static boolean isAudioFileType(int fileType) {\n\t\treturn (fileType >= FIRST_AUDIO_FILE_TYPE && fileType <= LAST_AUDIO_FILE_TYPE);\n\t}\n\n\tpublic static boolean isVideoFileType(int fileType) {\n\t\treturn (fileType >= FIRST_VIDEO_FILE_TYPE && fileType <= LAST_VIDEO_FILE_TYPE);\n\t}\n\n\tpublic static MediaFileType getFileType(String path) {\n\t\tint lastDot = path.lastIndexOf(\".\");\n\t\tif (lastDot < 0)\n\t\t\treturn null;\n\t\treturn sFileTypeMap.get(path.substring(lastDot + 1).toUpperCase());\n\t}\n\n\tpublic static int getFileTypeForMimeType(String mimeType) {\n\t\tInteger value = sMimeTypeMap.get(mimeType);\n\t\treturn (value == null ? 0 : value.intValue());\n\t}\n\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/MediaFormat.java",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio;\nimport java.nio.ByteBuffer;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Encapsulates the information describing the format of media data,\n * be it audio or video.\n *\n * The format of the media data is specified as string/value pairs.\n *\n * Keys common to all audio/video formats, <b>all keys not marked optional are mandatory</b>:\n *\n * <table>\n * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>\n * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr>\n * <tr><td>{@link #KEY_MAX_INPUT_SIZE}</td><td>Integer</td><td>optional, maximum size of a buffer of input data</td></tr>\n * <tr><td>{@link #KEY_BIT_RATE}</td><td>Integer</td><td><b>encoder-only</b>, desired bitrate in bits/second</td></tr>\n * </table>\n *\n * Video formats have the following keys:\n * <table>\n * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>\n * <tr><td>{@link #KEY_WIDTH}</td><td>Integer</td><td></td></tr>\n * <tr><td>{@link #KEY_HEIGHT}</td><td>Integer</td><td></td></tr>\n * <tr><td>{@link #KEY_COLOR_FORMAT}</td><td>Integer</td><td>set by the user\n *         for encoders, readable in the output format of decoders</b></td></tr>\n * <tr><td>{@link #KEY_FRAME_RATE}</td><td>Integer or Float</td><td><b>encoder-only</b></td></tr>\n * <tr><td>{@link #KEY_I_FRAME_INTERVAL}</td><td>Integer</td><td><b>encoder-only</b></td></tr>\n * <tr><td>{@link #KEY_MAX_WIDTH}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution width</td></tr>\n * <tr><td>{@link #KEY_MAX_HEIGHT}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution height</td></tr>\n * <tr><td>{@link #KEY_REPEAT_PREVIOUS_FRAME_AFTER}</td><td>Long</td><td><b>video encoder in surface-mode only</b></td></tr>\n * <tr><td>{@link #KEY_PUSH_BLANK_BUFFERS_ON_STOP}</td><td>Integer(1)</td><td><b>video decoder rendering to a surface only</b></td></tr>\n * </table>\n * Specify both {@link #KEY_MAX_WIDTH} and {@link #KEY_MAX_HEIGHT} to enable\n * adaptive playback (seamless resolution change) for a video decoder that\n * supports it ({@link MediaCodecInfo.CodecCapabilities#FEATURE_AdaptivePlayback}).\n * The values are used as hints for the codec: they are the maximum expected\n * resolution to prepare for.  Depending on codec support, preparing for larger\n * maximum resolution may require more memory even if that resolution is never\n * reached.  These fields have no effect for codecs that do not support adaptive\n * playback.<br /><br />\n *\n * Audio formats have the following keys:\n * <table>\n * <tr><th>Name</th><th>Value Type</th><th>Description</th></tr>\n * <tr><td>{@link #KEY_CHANNEL_COUNT}</td><td>Integer</td><td></td></tr>\n * <tr><td>{@link #KEY_SAMPLE_RATE}</td><td>Integer</td><td></td></tr>\n * <tr><td>{@link #KEY_IS_ADTS}</td><td>Integer</td><td>optional, if <em>decoding</em> AAC audio content, setting this key to 1 indicates that each audio frame is prefixed by the ADTS header.</td></tr>\n * <tr><td>{@link #KEY_AAC_PROFILE}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is AAC audio, specifies the desired profile.</td></tr>\n * <tr><td>{@link #KEY_CHANNEL_MASK}</td><td>Integer</td><td>optional, a mask of audio channel assignments</td></tr>\n * <tr><td>{@link #KEY_FLAC_COMPRESSION_LEVEL}</td><td>Integer</td><td><b>encoder-only</b>, optional, if content is FLAC audio, specifies the desired compression level.</td></tr>\n * </table>\n *\n * Subtitle formats have the following keys:\n * <table>\n * <tr><td>{@link #KEY_MIME}</td><td>String</td><td>The type of the format.</td></tr>\n * <tr><td>{@link #KEY_LANGUAGE}</td><td>String</td><td>The language of the content.</td></tr>\n * </table>\n */\npublic final class MediaFormat {\n    private Map<String, Object> mMap;\n\n    /**\n     * A key describing the mime type of the MediaFormat.\n     * The associated value is a string.\n     */\n    public static final String KEY_MIME = \"mime\";\n\n    /**\n     * A key describing the language of the content, using either ISO 639-1\n     * or 639-2/T codes.  The associated value is a string.\n     */\n    public static final String KEY_LANGUAGE = \"language\";\n    \n    /**\n     * A key describing the title of the content\n     */\n    public static final String KEY_TITLE = \"title\";\n    \n    /**\n     * A key describing the external subtitle path\n     */\n    public static final String KEY_PATH = \"path\";\n\n    /**\n     * A key describing the sample rate of an audio format.\n     * The associated value is an integer\n     */\n    public static final String KEY_SAMPLE_RATE = \"sample-rate\";\n\n    /**\n     * A key describing the number of channels in an audio format.\n     * The associated value is an integer\n     */\n    public static final String KEY_CHANNEL_COUNT = \"channel-count\";\n\n    /**\n     * A key describing the width of the content in a video format.\n     * The associated value is an integer\n     */\n    public static final String KEY_WIDTH = \"width\";\n\n    /**\n     * A key describing the height of the content in a video format.\n     * The associated value is an integer\n     */\n    public static final String KEY_HEIGHT = \"height\";\n\n    /**\n     * A key describing the maximum expected width of the content in a video\n     * decoder format, in case there are resolution changes in the video content.\n     * The associated value is an integer\n     */\n    public static final String KEY_MAX_WIDTH = \"max-width\";\n\n    /**\n     * A key describing the maximum expected height of the content in a video\n     * decoder format, in case there are resolution changes in the video content.\n     * The associated value is an integer\n     */\n    public static final String KEY_MAX_HEIGHT = \"max-height\";\n\n    /** A key describing the maximum size in bytes of a buffer of data\n     * described by this MediaFormat.\n     * The associated value is an integer\n     */\n    public static final String KEY_MAX_INPUT_SIZE = \"max-input-size\";\n\n    /**\n     * A key describing the bitrate in bits/sec.\n     * The associated value is an integer\n     */\n    public static final String KEY_BIT_RATE = \"bitrate\";\n\n    /**\n     * A key describing the color format of the content in a video format.\n     * Constants are declared in {@link android.media.MediaCodecInfo.CodecCapabilities}.\n     */\n    public static final String KEY_COLOR_FORMAT = \"color-format\";\n\n    /**\n     * A key describing the frame rate of a video format in frames/sec.\n     * The associated value is an integer or a float.\n     */\n    public static final String KEY_FRAME_RATE = \"frame-rate\";\n\n    /**\n     * A key describing the frequency of I frames expressed in secs\n     * between I frames.\n     * The associated value is an integer.\n     */\n    public static final String KEY_I_FRAME_INTERVAL = \"i-frame-interval\";\n\n    /**\n     * @hide\n     */\n    public static final String KEY_STRIDE = \"stride\";\n    /**\n     * @hide\n     */\n    public static final String KEY_SLICE_HEIGHT = \"slice-height\";\n\n    /**\n     * Applies only when configuring a video encoder in \"surface-input\" mode.\n     * The associated value is a long and gives the time in microseconds\n     * after which the frame previously submitted to the encoder will be\n     * repeated (once) if no new frame became available since.\n     */\n    public static final String KEY_REPEAT_PREVIOUS_FRAME_AFTER\n        = \"repeat-previous-frame-after\";\n\n    /**\n     * If specified when configuring a video decoder rendering to a surface,\n     * causes the decoder to output \"blank\", i.e. black frames to the surface\n     * when stopped to clear out any previously displayed contents.\n     * The associated value is an integer of value 1.\n     */\n    public static final String KEY_PUSH_BLANK_BUFFERS_ON_STOP\n        = \"push-blank-buffers-on-shutdown\";\n\n    /**\n     * A key describing the duration (in microseconds) of the content.\n     * The associated value is a long.\n     */\n    public static final String KEY_DURATION = \"durationUs\";\n\n    /**\n     * A key mapping to a value of 1 if the content is AAC audio and\n     * audio frames are prefixed with an ADTS header.\n     * The associated value is an integer (0 or 1).\n     * This key is only supported when _decoding_ content, it cannot\n     * be used to configure an encoder to emit ADTS output.\n     */\n    public static final String KEY_IS_ADTS = \"is-adts\";\n\n    /**\n     * A key describing the channel composition of audio content. This mask\n     * is composed of bits drawn from channel mask definitions in {@link android.media.AudioFormat}.\n     * The associated value is an integer.\n     */\n    public static final String KEY_CHANNEL_MASK = \"channel-mask\";\n\n    /**\n     * A key describing the AAC profile to be used (AAC audio formats only).\n     * Constants are declared in {@link android.media.MediaCodecInfo.CodecProfileLevel}.\n     */\n    public static final String KEY_AAC_PROFILE = \"aac-profile\";\n\n    /**\n     * A key describing the FLAC compression level to be used (FLAC audio format only).\n     * The associated value is an integer ranging from 0 (fastest, least compression)\n     * to 8 (slowest, most compression).\n     */\n    public static final String KEY_FLAC_COMPRESSION_LEVEL = \"flac-compression-level\";\n\n    /**\n     * A key for boolean AUTOSELECT behavior for the track. Tracks with AUTOSELECT=true\n     * are considered when automatically selecting a track without specific user\n     * choice, based on the current locale.\n     * This is currently only used for subtitle tracks, when the user selected\n     * 'Default' for the captioning locale.\n     * The associated value is an integer, where non-0 means TRUE.  This is an optional\n     * field; if not specified, AUTOSELECT defaults to TRUE.\n     */\n    public static final String KEY_IS_AUTOSELECT = \"is-autoselect\";\n\n    /**\n     * A key for boolean DEFAULT behavior for the track. The track with DEFAULT=true is\n     * selected in the absence of a specific user choice.\n     * This is currently only used for subtitle tracks, when the user selected\n     * 'Default' for the captioning locale.\n     * The associated value is an integer, where non-0 means TRUE.  This is an optional\n     * field; if not specified, DEFAULT is considered to be FALSE.\n     */\n    public static final String KEY_IS_DEFAULT = \"is-default\";\n\n\n    /**\n     * A key for the FORCED field for subtitle tracks. True if it is a\n     * forced subtitle track.  Forced subtitle tracks are essential for the\n     * content and are shown even when the user turns off Captions.  They\n     * are used for example to translate foreign/alien dialogs or signs.\n     * The associated value is an integer, where non-0 means TRUE.  This is an\n     * optional field; if not specified, FORCED defaults to FALSE.\n     */\n    public static final String KEY_IS_FORCED_SUBTITLE = \"is-forced-subtitle\";\n\n    /* package private */ MediaFormat(Map<String, Object> map) {\n        mMap = map;\n    }\n\n    /**\n     * Creates an empty MediaFormat\n     */\n    public MediaFormat() {\n        mMap = new HashMap<String, Object>();\n    }\n\n    /* package private */ Map<String, Object> getMap() {\n        return mMap;\n    }\n\n    /**\n     * Returns true iff a key of the given name exists in the format.\n     */\n    public final boolean containsKey(String name) {\n        return mMap.containsKey(name);\n    }\n\n    /**\n     * Returns the value of an integer key.\n     */\n    public final int getInteger(String name) {\n        return ((Integer)mMap.get(name)).intValue();\n    }\n\n    /**\n     * Returns the value of an integer key, or the default value if the\n     * key is missing or is for another type value.\n     * @hide\n     */\n    public final int getInteger(String name, int defaultValue) {\n        try {\n            return getInteger(name);\n        }\n        catch (NullPointerException  e) { /* no such field */ }\n        catch (ClassCastException e) { /* field of different type */ }\n        return defaultValue;\n    }\n\n    /**\n     * Returns the value of a long key.\n     */\n    public final long getLong(String name) {\n        return ((Long)mMap.get(name)).longValue();\n    }\n\n    /**\n     * Returns the value of a float key.\n     */\n    public final float getFloat(String name) {\n        return ((Float)mMap.get(name)).floatValue();\n    }\n\n    /**\n     * Returns the value of a string key.\n     */\n    public final String getString(String name) {\n        return (String)mMap.get(name);\n    }\n\n    /**\n     * Returns the value of a ByteBuffer key.\n     */\n    public final ByteBuffer getByteBuffer(String name) {\n        return (ByteBuffer)mMap.get(name);\n    }\n\n    /**\n     * Sets the value of an integer key.\n     */\n    public final void setInteger(String name, int value) {\n        mMap.put(name, Integer.valueOf(value));\n    }\n\n    /**\n     * Sets the value of a long key.\n     */\n    public final void setLong(String name, long value) {\n        mMap.put(name, Long.valueOf(value));\n    }\n\n    /**\n     * Sets the value of a float key.\n     */\n    public final void setFloat(String name, float value) {\n        mMap.put(name, Float.valueOf(value));\n    }\n\n    /**\n     * Sets the value of a string key.\n     */\n    public final void setString(String name, String value) {\n        mMap.put(name, value);\n    }\n\n    /**\n     * Sets the value of a ByteBuffer key.\n     */\n    public final void setByteBuffer(String name, ByteBuffer bytes) {\n        mMap.put(name, bytes);\n    }\n\n    /**\n     * Creates a minimal audio format.\n     * @param mime The mime type of the content.\n     * @param sampleRate The sampling rate of the content.\n     * @param channelCount The number of audio channels in the content.\n     */\n    public static final MediaFormat createAudioFormat(\n            String mime,\n            int sampleRate,\n            int channelCount) {\n        MediaFormat format = new MediaFormat();\n        format.setString(KEY_MIME, mime);\n        format.setInteger(KEY_SAMPLE_RATE, sampleRate);\n        format.setInteger(KEY_CHANNEL_COUNT, channelCount);\n\n        return format;\n    }\n\n    /**\n     * Creates a minimal subtitle format.\n     * @param title The content of the Subtitle\n     * @param language The language of the content, using either ISO 639-1 or 639-2/T\n     *        codes.  Specify null or \"und\" if language information is only included\n     *        in the content.  (This will also work if there are multiple language\n     *        tracks in the content.)\n     */\n    public static final MediaFormat createSubtitleFormat(\n            String title,\n            String language) {\n        MediaFormat format = new MediaFormat();\n        format.setString(KEY_TITLE, title);\n        format.setString(KEY_LANGUAGE, language);\n\n        return format;\n    }\n\n    /**\n     * Creates a minimal video format.\n     * @param mime The mime type of the content.\n     * @param width The width of the content (in pixels)\n     * @param height The height of the content (in pixels)\n     */\n    public static final MediaFormat createVideoFormat(\n            String mime,\n            int width,\n            int height) {\n        MediaFormat format = new MediaFormat();\n        format.setString(KEY_MIME, mime);\n        format.setInteger(KEY_WIDTH, width);\n        format.setInteger(KEY_HEIGHT, height);\n\n        return format;\n    }\n\n    @Override\n    public String toString() {\n        return mMap.toString();\n    }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/MediaMetadataRetriever.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.res.AssetFileDescriptor;\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.util.Log;\n\nimport io.vov.vitamio.utils.FileUtils;\n\nimport java.io.FileDescriptor;\nimport java.io.IOException;\n\n/**\n * MediaMetadataRetriever is used to get meta data from any media file\n * <p/>\n * \n * <pre>\n * MediaMetadataRetriever mmr = new MediaMetadataRetriever(this);\n * mmr.setDataSource(this, mediaUri);\n * String title = mmr.extractMetadata(METADATA_KEY_TITLE);\n * Bitmap frame = mmr.getFrameAtTime(-1);\n * mmr.release();\n * </pre>\n */\npublic class MediaMetadataRetriever {\n  private AssetFileDescriptor mFD = null;\n\n  static {\n    String LIB_ROOT = Vitamio.getLibraryPath();\n    Log.i(\"LIB ROOT: %s\", LIB_ROOT);\n    System.load( LIB_ROOT + \"libstlport_shared.so\");\n    System.load(  LIB_ROOT +\"libvscanner.so\");\n    loadFFmpeg_native( LIB_ROOT + \"libffmpeg.so\");\n    native_init();\n  }\n\n  private int mNativeContext;\n\n  public MediaMetadataRetriever(Context ctx) {\n    native_setup();\n  }\n\n  private static native boolean loadFFmpeg_native(String ffmpegPath);\n\n  public void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException,\n      SecurityException, IllegalStateException {\n    if (context == null || uri == null)\n      throw new IllegalArgumentException();\n    String scheme = uri.getScheme();\n    if (scheme == null || scheme.equals(\"file\")) {\n      setDataSource(FileUtils.getPath(uri.toString()));\n      return;\n    }\n\n    try {\n      ContentResolver resolver = context.getContentResolver();\n      mFD = resolver.openAssetFileDescriptor(uri, \"r\");\n      if (mFD == null)\n        return;\n      setDataSource(mFD.getParcelFileDescriptor().getFileDescriptor());\n      return;\n    } catch (Exception e) {\n      closeFD();\n    }\n    Log.e(\"Couldn't open file on client side, trying server side %s\", uri.toString());\n    setDataSource(uri.toString());\n    return;\n  }\n\n  public native void setDataSource(String path) throws IOException, IllegalArgumentException,\n      IllegalStateException;\n\n  public native void setDataSource(FileDescriptor fd) throws IOException, IllegalArgumentException,\n      IllegalStateException;\n\n  /**\n   * Call this method after setDataSource(). This method retrieves the meta data\n   * value associated with the keyCode.\n   * \n   * The keyCode currently supported is listed below as METADATA_XXX constants.\n   * With any other value, it returns a null pointer.\n   * \n   * @param keyCode One of the constants listed below at the end of the class.\n   * @return The meta data value associate with the given keyCode on success;\n   * null on failure.\n   */\n  public native String extractMetadata(String keyCode) throws IllegalStateException;\n\n  public native Bitmap getFrameAtTime(long timeUs) throws IllegalStateException;\n\n  /**\n   * Call this method after setDataSource(). This method finds the optional\n   * graphic or album/cover art associated associated with the data source. If\n   * there are more than one pictures, (any) one of them is returned.\n   * \n   * @return null if no such graphic is found.\n   */\n  public native byte[] getEmbeddedPicture() throws IllegalStateException;\n\n  private native void _release();\n\n  private native void native_setup();\n\n  private static native final void native_init();\n\n  private native final void native_finalize();\n\n  public void release() {\n    _release();\n    closeFD();\n  }\n\n  @Override\n  protected void finalize() throws Throwable {\n    try {\n      native_finalize();\n    } finally {\n      super.finalize();\n    }\n  }\n\n  private void closeFD() {\n    if (mFD != null) {\n      try {\n        mFD.close();\n      } catch (IOException e) {\n      }\n      mFD = null;\n    }\n  }\n\n  /*\n   * Do not change these metadata key values without updating their\n   * counterparts in c file\n   */\n\n  /**\n   * The metadata key to retrieve the information about the album title of the\n   * data source.\n   */\n  public static final String METADATA_KEY_ALBUM = \"album\";\n  /**\n   * The metadata key to retrieve the main creator of the set/album, if\n   * different from artist. e.g. \"Various Artists\" for compilation albums.\n   */\n  public static final String METADATA_KEY_ALBUM_ARTIST = \"album_artist\";\n  /**\n   * The metadata key to retrieve the information about the artist of the data\n   * source.\n   */\n  public static final String METADATA_KEY_ARTIST = \"artist\";\n\n  /**\n   * The metadata key to retrieve the any additional description of the file.\n   */\n  public static final String METADATA_KEY_COMMENT = \"comment\";\n  /**\n   * The metadata key to retrieve the information about the author of the data\n   * source.\n   */\n  public static final String METADATA_KEY_AUTHOR = \"author\";\n  /**\n   * The metadata key to retrieve the information about the composer of the data\n   * source.\n   */\n  public static final String METADATA_KEY_COMPOSER = \"composer\";\n  /**\n   * The metadata key to retrieve the name of copyright holder.\n   */\n  public static final String METADATA_KEY_COPYRIGHT = \"copyright\";\n  /**\n   * The metadata key to retrieve the date when the file was created, preferably\n   * in ISO 8601.\n   */\n  public static final String METADATA_KEY_CREATION_TIME = \"creation_time\";\n  /**\n   * The metadata key to retrieve the date when the work was created, preferably\n   * in ISO 8601.\n   */\n  public static final String METADATA_KEY_DATE = \"date\";\n  /**\n   * The metadata key to retrieve the number of a subset, e.g. disc in a\n   * multi-disc collection.\n   */\n  public static final String METADATA_KEY_DISC = \"disc\";\n  /**\n   * The metadata key to retrieve the name/settings of the software/hardware\n   * that produced the file.\n   */\n  public static final String METADATA_KEY_ENCODER = \"encoder\";\n  /**\n   * The metadata key to retrieve the person/group who created the file.\n   */\n  public static final String METADATA_KEY_ENCODED_BY = \"encoded_by\";\n  /**\n   * The metadata key to retrieve the original name of the file.\n   */\n  public static final String METADATA_KEY_FILENAME = \"filename\";\n  /**\n   * The metadata key to retrieve the content type or genre of the data source.\n   */\n  public static final String METADATA_KEY_GENRE = \"genre\";\n  /**\n   * The metadata key to retrieve the main language in which the work is\n   * performed, preferably in ISO 639-2 format. Multiple languages can be\n   * specified by separating them with commas.\n   */\n  public static final String METADATA_KEY_LANGUAGE = \"language\";\n  /**\n   * The metadata key to retrieve the artist who performed the work, if\n   * different from artist. E.g for \"Also sprach Zarathustra\", artist would be\n   * \"Richard Strauss\" and performer \"London Philharmonic Orchestra\".\n   */\n  public static final String METADATA_KEY_PERFORMER = \"performer\";\n  /**\n   * The metadata key to retrieve the name of the label/publisher.\n   */\n  public static final String METADATA_KEY_PUBLISHER = \"publisher\";\n  /**\n   * The metadata key to retrieve the name of the service in broadcasting\n   * (channel name).\n   */\n  public static final String METADATA_KEY_SERVICE_NAME = \"service_name\";\n  /**\n   * The metadata key to retrieve the name of the service provider in\n   * broadcasting.\n   */\n  public static final String METADATA_KEY_SERVICE_PROVIDER = \"service_provider\";\n  /**\n   * The metadata key to retrieve the data source title.\n   */\n  public static final String METADATA_KEY_TITLE = \"title\";\n  /**\n   * The metadata key to retrieve the number of this work in the set, can be in\n   * form current/total.\n   */\n  public static final String METADATA_KEY_TRACK = \"track\";\n  /**\n   * The metadata key to retrieve the total bitrate of the bitrate variant that\n   * the current stream is part of.\n   */\n  public static final String METADATA_KEY_VARIANT_BITRATE = \"bitrate\";\n  /**\n   * The metadata key to retrieve the playback duration of the data source.\n   */\n  public static final String METADATA_KEY_DURATION = \"duration\";\n  /**\n   * The metadata key to retrieve the audio codec of the work.\n   */\n  public static final String METADATA_KEY_AUDIO_CODEC = \"audio_codec\";\n  /**\n   * The metadata key to retrieve the video codec of the work.\n   */\n  public static final String METADATA_KEY_VIDEO_CODEC = \"video_codec\";\n  /**\n   * This key retrieves the video rotation angle in degrees, if available. The\n   * video rotation angle may be 0, 90, 180, or 270 degrees.\n   */\n  public static final String METADATA_KEY_VIDEO_ROTATION = \"rotate\";\n  /**\n   * If the media contains video, this key retrieves its width.\n   */\n  public static final String METADATA_KEY_VIDEO_WIDTH = \"width\";\n  /**\n   * If the media contains video, this key retrieves its height.\n   */\n  public static final String METADATA_KEY_VIDEO_HEIGHT = \"height\";\n  /**\n   * The metadata key to retrieve the number of tracks, such as audio, video,\n   * text, in the data source, such as a mp4 or 3gpp file.\n   */\n  public static final String METADATA_KEY_NUM_TRACKS = \"num_tracks\";\n  /**\n   * If this key exists the media contains audio content. if has audio, return\n   * 1.\n   */\n  public static final String METADATA_KEY_HAS_AUDIO = \"has_audio\";\n  /**\n   * If this key exists the media contains video content. if has video, return\n   * 1.\n   */\n  public static final String METADATA_KEY_HAS_VIDEO = \"has_video\";\n\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/MediaPlayer.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\nimport android.annotation.SuppressLint;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.res.AssetFileDescriptor;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.media.AudioFormat;\nimport android.media.AudioManager;\nimport android.media.AudioTrack;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.os.PowerManager;\nimport android.text.TextUtils;\nimport android.util.SparseArray;\nimport android.view.Surface;\nimport android.view.SurfaceHolder;\n\nimport io.vov.vitamio.utils.ContextUtils;\nimport io.vov.vitamio.utils.FileUtils;\nimport io.vov.vitamio.utils.Log;\n\nimport java.io.File;\nimport java.io.FileDescriptor;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n\n/**\n * MediaPlayer class can be used to control playback of audio/video files and\n * streams. An example on how to use the methods in this class can be found in\n * {@link io.vov.vitamio.widget.VideoView}. This class will function the same as\n * android.media.MediaPlayer in most cases. Please see <a\n * href=\"http://developer.android.com/guide/topics/media/index.html\">Audio and\n * Video</a> for additional help using MediaPlayer.\n */\npublic class MediaPlayer {\n  public static final int CACHE_TYPE_NOT_AVAILABLE = 1;\n  public static final int CACHE_TYPE_START = 2;\n  public static final int CACHE_TYPE_UPDATE = 3;\n  public static final int CACHE_TYPE_SPEED = 4;\n  public static final int CACHE_TYPE_COMPLETE = 5;\n  public static final int CACHE_INFO_NO_SPACE = 1;\n  public static final int CACHE_INFO_STREAM_NOT_SUPPORT = 2;\n  public static final int MEDIA_ERROR_UNKNOWN = 1;\n  public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;\n  \n  /** File or network related operation errors. */\n  public static final int MEDIA_ERROR_IO = -5;\n  /** Bitstream is not conforming to the related coding standard or file spec. */\n  public static final int MEDIA_ERROR_MALFORMED = -1007;\n  /** Bitstream is conforming to the related coding standard or file spec, but\n   * the media framework does not support the feature. */\n  public static final int MEDIA_ERROR_UNSUPPORTED = -1010;\n  /** Some operation takes too long to complete, usually more than 3-5 seconds. */\n  public static final int MEDIA_ERROR_TIMED_OUT = -110;\n  /**\n   * The video is too complex for the decoder: it can't decode frames fast\n   * enough. Possibly only the audio plays fine at this stage.\n   *\n   * @see io.vov.vitamio.MediaPlayer.OnInfoListener\n   */\n  public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;\n  /**\n   * MediaPlayer is temporarily pausing playback internally in order to buffer\n   * more data.\n   */\n  public static final int MEDIA_INFO_BUFFERING_START = 701;\n  /**\n   * MediaPlayer is resuming playback after filling buffers.\n   */\n  public static final int MEDIA_INFO_BUFFERING_END = 702;\n  \n  public static final int MEDIA_INFO_FILE_OPEN_OK = 704;\n  public static final int MEDIA_INFO_UNKNOW_TYPE = 1001;\n  public static final int MEDIA_INFO_GET_CODEC_INFO_ERROR = 1002;\n  \n  /**\n   * The media cannot be seeked (e.g live stream)\n   *\n   * @see io.vov.vitamio.MediaPlayer.OnInfoListener\n   */\n  public static final int MEDIA_INFO_NOT_SEEKABLE = 801;\n  /**\n   * The rate in KB/s of av_read_frame()\n   *\n   * @see io.vov.vitamio.MediaPlayer.OnInfoListener\n   */\n  public static final int MEDIA_INFO_DOWNLOAD_RATE_CHANGED = 901;\n  public static final int VIDEOQUALITY_LOW = -16;\n  public static final int VIDEOQUALITY_MEDIUM = 0;\n  public static final int VIDEOQUALITY_HIGH = 16;\n\n  public static final int VIDEOCHROMA_RGB565 = 0;\n  public static final int VIDEOCHROMA_RGBA = 1;\n  /**\n   * The subtitle displayed is embeded in the movie\n   */\n  public static final int SUBTITLE_INTERNAL = 0;\n  /**\n   * The subtitle displayed is an external file\n   */\n  public static final int SUBTITLE_EXTERNAL = 1;\n  /**\n   * The external subtitle types which Vitamio supports.\n   */\n  public static final String[] SUB_TYPES = {\".srt\", \".ssa\", \".smi\", \".txt\", \".sub\", \".ass\", \".webvtt\"};\n  private static final int MEDIA_NOP = 0;\n  private static final int MEDIA_PREPARED = 1;\n  private static final int MEDIA_PLAYBACK_COMPLETE = 2;\n  private static final int MEDIA_BUFFERING_UPDATE = 3;\n  private static final int MEDIA_SEEK_COMPLETE = 4;\n  private static final int MEDIA_SET_VIDEO_SIZE = 5;\n  private static final int MEDIA_ERROR = 100;\n  private static final int MEDIA_INFO = 200;\n  private static final int MEDIA_CACHE = 300;\n  private static final int MEDIA_HW_ERROR = 400;\n  private static final int MEDIA_TIMED_TEXT = 1000;\n  private static final int MEDIA_CACHING_UPDATE = 2000;\n  private static final String MEDIA_CACHING_SEGMENTS = \"caching_segment\";\n  private static final String MEDIA_CACHING_TYPE = \"caching_type\";\n  private static final String MEDIA_CACHING_INFO = \"caching_info\";\n  private static final String MEDIA_SUBTITLE_STRING = \"sub_string\";\n  private static final String MEDIA_SUBTITLE_BYTES = \"sub_bytes\";\n  private static final String MEDIA_SUBTITLE_TYPE = \"sub_type\";\n  private static final int SUBTITLE_TEXT = 0;\n  private static final int SUBTITLE_BITMAP = 1;\n  private static AtomicBoolean NATIVE_OMX_LOADED = new AtomicBoolean(false);\n  private Context mContext;\n  private Surface mSurface;\n  private SurfaceHolder mSurfaceHolder;\n  private EventHandler mEventHandler;\n  private PowerManager.WakeLock mWakeLock = null;\n  private boolean mScreenOnWhilePlaying;\n  private boolean mStayAwake;\n  private Metadata mMeta;\n  private TrackInfo[] mInbandTracks;\n  private TrackInfo mOutOfBandTracks;\n  private AssetFileDescriptor mFD = null;\n  private OnHWRenderFailedListener mOnHWRenderFailedListener;\n  private OnPreparedListener mOnPreparedListener;\n  private OnCompletionListener mOnCompletionListener;\n  private OnBufferingUpdateListener mOnBufferingUpdateListener;\n  private OnCachingUpdateListener mOnCachingUpdateListener;\n  private OnSeekCompleteListener mOnSeekCompleteListener;\n  private OnVideoSizeChangedListener mOnVideoSizeChangedListener;\n  private OnErrorListener mOnErrorListener;\n  /**\n   * Register a callback to be invoked when an info/warning is available.\n   *\n   * @param listener\n   * the callback that will be run\n   */\n  private OnInfoListener mOnInfoListener;\n  private OnTimedTextListener mOnTimedTextListener;\n  private AudioTrack mAudioTrack;\n  private int mAudioTrackBufferSize;\n  private Surface mLocalSurface;\n  private Bitmap mBitmap;\n  private ByteBuffer mByteBuffer;\n  \n  /**\n   * Default constructor. The same as Android's MediaPlayer().\n   * <p>\n   * When done with the MediaPlayer, you should call {@link #release()}, to free\n   * the resources. If not released, too many MediaPlayer instances may result\n   * in an exception.\n   * </p>\n   */\n  public MediaPlayer(Context ctx) {\n    this(ctx, false);\n  }\n\n  private static String path;\n  \n  /**\n   * Default constructor. The same as Android's MediaPlayer().\n   * <p>\n   * When done with the MediaPlayer, you should call {@link #release()}, to free\n   * the resources. If not released, too many MediaPlayer instances may result\n   * in an exception.\n   * </p>\n   *\n   * @param preferHWDecoder MediaPlayer will try to use hardware accelerated decoder if true\n   */\n  public MediaPlayer(Context ctx, boolean preferHWDecoder) {\n    mContext = ctx;\n\n    String LIB_ROOT = Vitamio.getLibraryPath();\n\n    if (preferHWDecoder) {\n      if (!NATIVE_OMX_LOADED.get()) {\n        if (Build.VERSION.SDK_INT > 17)\n          loadOMX_native( LIB_ROOT + \"libOMX.18.so\");\n        \n        else if (Build.VERSION.SDK_INT > 13)\n          loadOMX_native( LIB_ROOT +  \"libOMX.14.so\");\n        else if (Build.VERSION.SDK_INT > 10)\n          loadOMX_native( LIB_ROOT + \"libOMX.11.so\");\n        else\n          loadOMX_native( LIB_ROOT +  \"libOMX.9.so\");\n        NATIVE_OMX_LOADED.set(true);\n      }\n    } else {\n      try {\n        unloadOMX_native();\n      } catch (UnsatisfiedLinkError e) {\n        Log.e(\"unloadOMX failed %s\", e.toString());\n      }\n      NATIVE_OMX_LOADED.set(false);\n    }\n\n    Looper looper;\n    if ((looper = Looper.myLooper()) != null)\n      mEventHandler = new EventHandler(this, looper);\n    else if ((looper = Looper.getMainLooper()) != null)\n      mEventHandler = new EventHandler(this, looper);\n    else\n      mEventHandler = null;\n\n    native_init();\n  }\n\n  static {\n\tString LIB_ROOT = Vitamio.getLibraryPath();\n    try {\n    \n  \n      System.load(  LIB_ROOT +  \"libstlport_shared.so\");\n      System.load(  LIB_ROOT +  \"libvplayer.so\");\n      loadFFmpeg_native( LIB_ROOT + \"libffmpeg.so\");\n      boolean vvo_loaded = false;\n      if (Build.VERSION.SDK_INT > 8)\n        vvo_loaded = loadVVO_native( LIB_ROOT +  \"libvvo.9.so\");\n      else if (Build.VERSION.SDK_INT > 7)\n        vvo_loaded = loadVVO_native(  LIB_ROOT + \"libvvo.8.so\");\n      else\n        vvo_loaded = loadVVO_native(  LIB_ROOT + \"libvvo.7.so\");\n      if (!vvo_loaded) {\n        vvo_loaded = loadVVO_native(  LIB_ROOT +  \"libvvo.j.so\");\n        Log.d(\"FALLBACK TO VVO JNI \" + vvo_loaded);\n      }\n      loadVAO_native( LIB_ROOT +  \"libvao.0.so\");\n    } catch (java.lang.UnsatisfiedLinkError e) {\n      Log.e(\"Error loading libs\", e);\n    }\n  }\n \n  private static void postEventFromNative(Object mediaplayer_ref, int what, int arg1, int arg2, Object obj) {\n    MediaPlayer mp = (MediaPlayer) (mediaplayer_ref);\n    if (mp == null)\n      return;\n\n    try {\n        //synchronized (mp.mEventHandler) {\n        if (mp.mEventHandler != null) {\n          Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);\n          mp.mEventHandler.sendMessage(m);\n        }\n    } catch (Exception e) {\n        Log.e(\"exception: \" + e);\n    }\n  }\n\n  private static native boolean loadVAO_native(String vaoPath);\n\n  private static native boolean loadVVO_native(String vvoPath);\n\n  private static native boolean loadOMX_native(String omxPath);\n\n  private static native void unloadOMX_native();\n\n  private static native boolean loadFFmpeg_native(String ffmpegPath);\n\n  private native void _setVideoSurface(Surface surface);\n\n  /**\n   * Sets the SurfaceHolder to use for displaying the video portion of the\n   * media. This call is optional. Not calling it when playing back a video will\n   * result in only the audio track being played.\n   *\n   * @param sh the SurfaceHolder to use for video display\n   */\n  public void setDisplay(SurfaceHolder sh) {\n    if (sh == null) {\n      releaseDisplay();\n    } else {\n      mSurfaceHolder = sh;\n      mSurface = sh.getSurface();\n      _setVideoSurface(mSurface);\n      updateSurfaceScreenOn();\n    }\n  }\n\n  /**\n   * Sets the Surface to use for displaying the video portion of the media. This\n   * is similar to {@link #setDisplay(SurfaceHolder)}.\n   *\n   * @param surface the Surface to use for video display\n   */\n  public void setSurface(Surface surface) {\n    if (surface == null) {\n      releaseDisplay();\n    } else {\n      mSurfaceHolder = null;\n      mSurface = surface;\n      _setVideoSurface(mSurface);\n      updateSurfaceScreenOn();\n    }\n  }\n\n  /**\n   * Sets the data source (file-path or http/rtsp URL) to use.\n   *\n   * @param path the path of the file, or the http/rtsp URL of the stream you want\n   *             to play\n   * @throws IllegalStateException if it is called in an invalid state\n   *                               <p/>\n   *                               <p/>\n   *                               When <code>path</code> refers to a local file, the file may\n   *                               actually be opened by a process other than the calling\n   *                               application. This implies that the pathname should be an absolute\n   *                               path (as any other process runs with unspecified current working\n   *                               directory), and that the pathname should reference a\n   *                               world-readable file. As an alternative, the application could\n   *                               first open the file for reading, and then use the file descriptor\n   *                               form {@link #setDataSource(FileDescriptor)}.\n   */\n  public void setDataSource(String path) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {\n    _setDataSource(path, null, null);\n  }\n\n  /**\n   * Sets the data source as a content Uri.\n   *\n   * @param context the Context to use when resolving the Uri\n   * @param uri     the Content URI of the data you want to play\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n  public void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {\n    setDataSource(context, uri, null);\n  }\n\n  public void setDataSource(Context context, Uri uri, Map<String, String> headers) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {\n    if (context == null || uri == null)\n      throw new IllegalArgumentException();\n    String scheme = uri.getScheme();\n    if (scheme == null || scheme.equals(\"file\")) {\n      setDataSource(FileUtils.getPath(uri.toString()));\n      return;\n    }\n\n    try {\n      ContentResolver resolver = context.getContentResolver();\n      mFD = resolver.openAssetFileDescriptor(uri, \"r\");\n      if (mFD == null)\n        return;\n      setDataSource(mFD.getParcelFileDescriptor().getFileDescriptor());\n      return;\n    } catch (Exception e) {\n      closeFD();\n    }\n    setDataSource(uri.toString(), headers);\n  }\n  \n  /**\n   * Sets the data source (file-path or http/rtsp URL) to use.\n   *\n   * @param path the path of the file, or the http/rtsp URL of the stream you want to play\n   * @param headers the headers associated with the http request for the stream you want to play\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n  public void setDataSource(String path, Map<String, String> headers)\n          throws IOException, IllegalArgumentException, SecurityException, IllegalStateException\n  {\n      String[] keys = null;\n      String[] values = null;\n\n      if (headers != null) {\n          keys = new String[headers.size()];\n          values = new String[headers.size()];\n\n          int i = 0;\n          for (Map.Entry<String, String> entry: headers.entrySet()) {\n              keys[i] = entry.getKey();\n              values[i] = entry.getValue();\n              ++i;\n          }\n      }\n      setDataSource(path, keys, values);\n  }\n  \n  /**\n   * Sets the data source (file-path or http/rtsp URL) to use.\n   *\n   * @param path the path of the file, or the http/rtsp URL of the stream you want to play\n   * @param keys   AVOption key\n   * @param values AVOption value\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n\tpublic void setDataSource(String path, String[] keys, String[] values) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {\n\t\tfinal Uri uri = Uri.parse(path);\n\t\tif (\"file\".equals(uri.getScheme())) {\n\t\t\tpath = uri.getPath();\n\t\t}\n\n\t\tfinal File file = new File(path);\n\t\tif (file.exists()) {\n\t\t\tFileInputStream is = new FileInputStream(file);\n\t\t\tFileDescriptor fd = is.getFD();\n\t\t\tsetDataSource(fd);\n\t\t\tis.close();\n\t\t} else {\n\t\t\t_setDataSource(path, keys, values);\n\t\t}\n\t}\n\n  /**\n   * Set the segments source url\n   * @param segments the array path of the url e.g. Segmented video list\n   * @param cacheDir e.g. getCacheDir().toString()\n   */\n  public void setDataSegments(String[] uris, String cacheDir) {\n  \t_setDataSegmentsSource(uris, cacheDir);\n  }\n\n  public void setOnHWRenderFailedListener(OnHWRenderFailedListener l) {\n    mOnHWRenderFailedListener = l;\n  }\n\n  /**\n   * Sets the data source (file-path or http/rtsp/mms URL) to use.\n   *\n   * @param path    the path of the file, or the http/rtsp/mms URL of the stream you\n   *                want to play\n   * @param keys   AVOption key\n   * @param values AVOption value\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n  private native void _setDataSource(String path, String[] keys, String[] values) throws IOException, IllegalArgumentException, IllegalStateException;\n\n  /**\n   * Sets the data source (FileDescriptor) to use. It is the caller's\n   * responsibility to close the file descriptor. It is safe to do so as soon as\n   * this call returns.\n   *\n   * @param fd the FileDescriptor for the file you want to play\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n  public native void setDataSource(FileDescriptor fd) throws IOException, IllegalArgumentException, IllegalStateException;\n  \n  /**\n   * Set the segments source url\n   * @param segments the array path of the url\n   * @param cacheDir e.g. getCacheDir().toString()\n   */\n  private native void _setDataSegmentsSource(String[] segments, String cacheDir);\n\n  /**\n   * Prepares the player for playback, synchronously.\n   * <p/>\n   * After setting the datasource and the display surface, you need to either\n   * call prepare() or prepareAsync(). For files, it is OK to call prepare(),\n   * which blocks until MediaPlayer is ready for playback.\n   *\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n  public native void prepare() throws IOException, IllegalStateException;\n\n  /**\n   * Prepares the player for playback, asynchronously.\n   * <p/>\n   * After setting the datasource and the display surface, you need to either\n   * call prepare() or prepareAsync(). For streams, you should call\n   * prepareAsync(), which returns immediately, rather than blocking until\n   * enough data has been buffered.\n   *\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n  public native void prepareAsync() throws IllegalStateException;\n\n  /**\n   * Starts or resumes playback. If playback had previously been paused,\n   * playback will continue from where it was paused. If playback had been\n   * stopped, or never started before, playback will start at the beginning.\n   *\n   * @throws IllegalStateException if it is called in an invalid state\n   */\n  public void start() throws IllegalStateException {\n    stayAwake(true);\n    if (mInBuffering) {\n        //Log.i(\"MiuiVideo: now is in buffering, and will start after buffering\");\n        mNeedResume = true;\n    } else {\n        //Log.i(\"MiuiVideo: start player\");\n        _start();\n    }\n  }\n\n  private native void _start() throws IllegalStateException;\n\n  /**\n   * The same as {@link #pause()}\n   *\n   * @throws IllegalStateException if the internal player engine has not been initialized.\n   */\n  public void stop() throws IllegalStateException {\n    stayAwake(false);\n    _stop();\n    mInBuffering = false;\n    mNeedResume = false;\n  }\n\n  private native void _stop() throws IllegalStateException;\n\n  /**\n   * Pauses playback. Call start() to resume.\n   *\n   * @throws IllegalStateException if the internal player engine has not been initialized.\n   */\n  public void pause() throws IllegalStateException {\n    stayAwake(false);\n    mNeedResume = false;\n    //Log.i(\"MiuiVideo: pause player\");\n    _pause();\n  }\n\n  private native void _pause() throws IllegalStateException;\n\n  /**\n   * Set the low-level power management behavior for this MediaPlayer. This can\n   * be used when the MediaPlayer is not playing through a SurfaceHolder set\n   * with {@link #setDisplay(SurfaceHolder)} and thus can use the high-level\n   * {@link #setScreenOnWhilePlaying(boolean)} feature.\n   * <p/>\n   * This function has the MediaPlayer access the low-level power manager\n   * service to control the device's power usage while playing is occurring. The\n   * parameter is a combination of {@link android.os.PowerManager} wake flags.\n   * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}\n   * permission. By default, no attempt is made to keep the device awake during\n   * playback.\n   *\n   * @param context the Context to use\n   * @param mode    the power/wake mode to set\n   * @see android.os.PowerManager\n   */\n  @SuppressLint(\"Wakelock\")\n  public void setWakeMode(Context context, int mode) {\n    boolean washeld = false;\n    if (mWakeLock != null) {\n      if (mWakeLock.isHeld()) {\n        washeld = true;\n        mWakeLock.release();\n      }\n      mWakeLock = null;\n    }\n\n    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);\n    mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName());\n    mWakeLock.setReferenceCounted(false);\n    if (washeld) {\n      mWakeLock.acquire();\n    }\n  }\n\n  /**\n   * Control whether we should use the attached SurfaceHolder to keep the screen\n   * on while video playback is occurring. This is the preferred method over\n   * {@link #setWakeMode} where possible, since it doesn't require that the\n   * application have permission for low-level wake lock access.\n   *\n   * @param screenOn Supply true to keep the screen on, false to allow it to turn off.\n   */\n  public void setScreenOnWhilePlaying(boolean screenOn) {\n    if (mScreenOnWhilePlaying != screenOn) {\n      mScreenOnWhilePlaying = screenOn;\n      updateSurfaceScreenOn();\n    }\n  }\n\n  @SuppressLint(\"Wakelock\")\n  private void stayAwake(boolean awake) {\n    if (mWakeLock != null) {\n      if (awake && !mWakeLock.isHeld()) {\n        mWakeLock.acquire();\n      } else if (!awake && mWakeLock.isHeld()) {\n        mWakeLock.release();\n      }\n    }\n    mStayAwake = awake;\n    updateSurfaceScreenOn();\n  }\n\n  private void updateSurfaceScreenOn() {\n    if (mSurfaceHolder != null)\n      mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);\n  }\n\n  /**\n   * Returns the width of the video.\n   *\n   * @return the width of the video, or 0 if there is no video, or the width has\n   *         not been determined yet. The OnVideoSizeChangedListener can be\n   *         registered via\n   *         {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}\n   *         to provide a notification when the width is available.\n   */\n  public native int getVideoWidth();\n\n  private native int getVideoWidth_a();\n\n  /**\n   * Returns the height of the video.\n   *\n   * @return the height of the video, or 0 if there is no video, or the height\n   *         has not been determined yet. The OnVideoSizeChangedListener can be\n   *         registered via\n   *         {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}\n   *         to provide a notification when the height is available.\n   */\n  public native int getVideoHeight();\n\n  private native int getVideoHeight_a();\n\n  /**\n   * Checks whether the MediaPlayer is playing.\n   *\n   * @return true if currently playing, false otherwise\n   */\n  public native boolean isPlaying();\n  \n  \n  /**\n   * Set whether cache the online playback file\n   * @param cache\n   */\n  public native void setUseCache(boolean cache);\n  \n  /**\n   * set cache file dir\n   * @param directory\n   */\n  public native void setCacheDirectory(String directory);\n\n\t/**\n   * Adaptive streaming support, default is false\n   *\n   * @param adaptive true if wanna adaptive steam\n   *\n   */\n  public native void setAdaptiveStream(boolean adaptive);\n\n  /**\n   * Seeks to specified time position.\n   *\n   * @param msec the offset in milliseconds from the start to seek to\n   * @throws IllegalStateException if the internal player engine has not been initialized\n   */\n  public native void seekTo(long msec) throws IllegalStateException;\n\n  /**\n   * Gets the current playback position.\n   *\n   * @return the current position in milliseconds\n   */\n  public native long getCurrentPosition();\n\n  /**\n   * Get the current video frame\n   *\n   * @return bitmap object\n   */\n  public native Bitmap getCurrentFrame();\n\n  /**\n   * Gets the duration of the file.\n   *\n   * @return the duration in milliseconds\n   */\n  public native long getDuration();\n\n  /**\n   * Gets the media metadata.\n   *\n   * @return The metadata, possibly empty. null if an error occurred.\n   */\n  public Metadata getMetadata() {\n    if (mMeta == null) {\n      mMeta = new Metadata();\n      Map<byte[], byte[]> meta = new HashMap<byte[], byte[]>();\n\n      if (!native_getMetadata(meta)) {\n        return null;\n      }\n\n      if (!mMeta.parse(meta, getMetaEncoding())) {\n        return null;\n      }\n    }\n    return mMeta;\n  }\n\n  /**\n   * Releases resources associated with this MediaPlayer object. It is\n   * considered good practice to call this method when you're done using the\n   * MediaPlayer.\n   */\n  public void release() {\n    stayAwake(false);\n    updateSurfaceScreenOn();\n    mOnPreparedListener = null;\n    mOnBufferingUpdateListener = null;\n    mOnCompletionListener = null;\n    mOnSeekCompleteListener = null;\n    mOnErrorListener = null;\n    mOnInfoListener = null;\n    mOnVideoSizeChangedListener = null;\n    mOnCachingUpdateListener = null;\n    mOnHWRenderFailedListener = null;\n    if (mEventHandler != null)\n        mEventHandler.release();\n    //mEventHandler = null;\n    _release();\n    closeFD();\n    mInBuffering = false;\n    mNeedResume = false;\n  }\n\n  private native void _release();\n\n  /**\n   * Resets the MediaPlayer to its uninitialized state. After calling this\n   * method, you will have to initialize it again by setting the data source and\n   * calling prepare().\n   */\n  public void reset() {\n    stayAwake(false);\n    _reset();\n    if (mEventHandler != null)\n        mEventHandler.removeCallbacksAndMessages(null);\n    closeFD();\n    mInBuffering = false;\n    mNeedResume = false;\n  }\n\n  private native void _reset();\n\n  private void closeFD() {\n    if (mFD != null) {\n      try {\n        mFD.close();\n      } catch (IOException e) {\n        Log.e(\"closeFD\", e);\n      }\n      mFD = null;\n    }\n  }\n  \n  /**\n   * Sets the player to be looping or non-looping.\n   *\n   * @param looping whether to loop or not\n   */\n  public native void setLooping(boolean looping);\n\n  /**\n   * Checks whether the MediaPlayer is looping or non-looping.\n   *\n   * @return true if the MediaPlayer is currently looping, false otherwise\n   */\n  public native boolean isLooping();\n  /**\n   * Amplify audio\n   *\n   * @param ratio  e.g. 3.5\n   */\n  public native void setAudioAmplify(float ratio);\n\n  public native void setVolume(float leftVolume, float rightVolume);\n\n  private native final boolean native_getTrackInfo(SparseArray<byte[]> trackSparse);\n\n  private native final boolean native_getMetadata(Map<byte[], byte[]> meta);\n\n  private native final void native_init();\n\n  private native final void native_finalize();\n\n  /**\n   * Returns an array of track information.\n   *\n   * @return Array of track info. The total number of tracks is the array\n   *         length. Must be called again if an external timed text source has\n   *         been added after any of the addTimedTextSource methods are called.\n   */\n  public TrackInfo[] getTrackInfo(String encoding) {\n  \tTrackInfo[] trackInfo = getInbandTrackInfo(encoding);\n    // add out-of-band tracks\n  \tString timedTextPath = getTimedTextPath();\n  \tif (TextUtils.isEmpty(timedTextPath)) {\n  \t\treturn trackInfo;\n  \t}\n    TrackInfo[] allTrackInfo = new TrackInfo[trackInfo.length + 1];\n    System.arraycopy(trackInfo, 0, allTrackInfo, 0, trackInfo.length);\n    int i = trackInfo.length;\n    SparseArray<MediaFormat> trackInfoArray = new SparseArray<MediaFormat>();\n    MediaFormat mediaFormat = new MediaFormat();\n    mediaFormat.setString(MediaFormat.KEY_TITLE, timedTextPath.substring(timedTextPath.lastIndexOf(\"/\")));\n    mediaFormat.setString(MediaFormat.KEY_PATH, timedTextPath);\n    SparseArray<MediaFormat> timedTextSparse = findTrackFromTrackInfo(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT, trackInfo);\n    if (timedTextSparse == null || timedTextSparse.size() == 0)\n    \ttrackInfoArray.put(timedTextSparse.keyAt(0), mediaFormat);\n    else \n    \ttrackInfoArray.put(timedTextSparse.keyAt(timedTextSparse.size() - 1), mediaFormat);\n    mOutOfBandTracks = new TrackInfo(TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE, trackInfoArray);\n    allTrackInfo[i] = mOutOfBandTracks;\n    return allTrackInfo;\n  }\n  \n  private TrackInfo[] getInbandTrackInfo(String encoding) {\n  \tif (mInbandTracks == null) {\n  \t\tSparseArray<byte[]> trackSparse = new SparseArray<byte[]>();\n      if (!native_getTrackInfo(trackSparse)) {\n        return null;\n      }\n\n      int size = trackSparse.size();\n      mInbandTracks = new TrackInfo[size];\n      for (int i = 0; i < size; i++) {\n      \tSparseArray<MediaFormat> sparseArray = parseTrackInfo(trackSparse.valueAt(i), encoding);\n        TrackInfo trackInfo = new TrackInfo(trackSparse.keyAt(i), sparseArray);\n        mInbandTracks[i] = trackInfo;\n      }\n  \t}\n    return mInbandTracks;\n  }\n\n  /**\n   * Use default chartset {@link #getTrackInfo()} method.\n   *\n   * @return array of {@link TrackInfo}\n   */\n  public TrackInfo[] getTrackInfo() {\n    return getTrackInfo(Charset.defaultCharset().name());\n  }\n\n  private SparseArray<MediaFormat> parseTrackInfo(byte[] tracks, String encoding) {\n    SparseArray<MediaFormat> trackSparse = new SparseArray<MediaFormat>();\n    String trackString;\n    int trackNum;\n    try {\n      trackString = new String(tracks, encoding);\n    } catch (Exception e) {\n      Log.e(\"getTrackMap exception\");\n      trackString = new String(tracks);\n    }\n    for (String s : trackString.split(\"!#!\")) {\n      try {\n      \tMediaFormat mediaFormat = null;\n      \tString[] formats = s.split(\"\\\\.\");\n      \tif (formats == null)\n      \t\tcontinue;\n      \ttrackNum = Integer.parseInt(formats[0]);\n      \tif (formats.length == 3) {\n      \t\tmediaFormat = MediaFormat.createSubtitleFormat(formats[2], formats[1]);\n      \t} else if (formats.length == 2) {\n      \t\tmediaFormat = MediaFormat.createSubtitleFormat(\"\", formats[1]);\n      \t}\n        trackSparse.put(trackNum, mediaFormat);\n      } catch (NumberFormatException e) {\n      }\n    }\n\n    return trackSparse;\n  }\n\n  /**\n   * @param mediaTrackType\n   * @param trackInfo\n   * @return {@link TrackInfo#getTrackInfoArray()}\n   */\n  public SparseArray<MediaFormat> findTrackFromTrackInfo(int mediaTrackType, TrackInfo[] trackInfo) {\n    for (int i = 0; i < trackInfo.length; i++) {\n      if (trackInfo[i].getTrackType() == mediaTrackType) {\n        return trackInfo[i].getTrackInfoArray();\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Set the file-path of an external timed text.\n   *\n   * @param path must be a local file\n   */\n  public native void addTimedTextSource(String path);\n\n  /**\n   * Selects a track.\n   * <p>\n   * In any valid state, if it is called multiple times on the same type of\n   * track (ie. Video, Audio, Timed Text), the most recent one will be chosen.\n   * </p>\n   * <p>\n   * The first audio and video tracks are selected by default if available, even\n   * though this method is not called. However, no timed text track will be\n   * selected until this function is called.\n   * </p>\n   *\n   * @param index the index of the track to be selected. The valid range of the\n   *              index is 0..total number of track - 1. The total number of tracks\n   *              as well as the type of each individual track can be found by\n   *              calling {@link #getTrackInfo()} method.\n   * @see io.vov.vitamio.MediaPlayer#getTrackInfo\n   */\n  public void selectTrack(int index) {\n  \tselectOrDeselectBandTrack(index, true /* select */);\n  }\n\n  /**\n   * Deselect a track.\n   * <p>\n   * Currently, the track must be a timed text track and no audio or video\n   * tracks can be deselected.\n   * </p>\n   *\n   * @param index the index of the track to be deselected. The valid range of the\n   *              index is 0..total number of tracks - 1. The total number of tracks\n   *              as well as the type of each individual track can be found by\n   *              calling {@link #getTrackInfo()} method.\n   * @see io.vov.vitamio.MediaPlayer#getTrackInfo\n   */\n  public void deselectTrack(int index) {\n  \tselectOrDeselectBandTrack(index, false /* select */);\n  }\n  \n  private void selectOrDeselectBandTrack(int index, boolean select) {\n  \tif (mOutOfBandTracks != null) {\n  \t\tSparseArray<MediaFormat> mediaSparse = mOutOfBandTracks.getTrackInfoArray();\n  \t\tint trackIndex = mediaSparse.keyAt(0);\n  \t\tMediaFormat mediaFormat = mediaSparse.valueAt(0);\n    \tif (index == trackIndex  && select) {\n    \t\taddTimedTextSource(mediaFormat.getString(MediaFormat.KEY_PATH));\n    \t\treturn;\n    \t}\n  \t}\n  \tselectOrDeselectTrack(index, select);\n  }\n  \n  private native void selectOrDeselectTrack(int index, boolean select);\n\n  @Override\n  protected void finalize() {\n    native_finalize();\n  }\n\n  /**\n   * Register a callback to be invoked when the media source is ready for\n   * playback.\n   *\n   * @param listener the callback that will be run\n   */\n  public void setOnPreparedListener(OnPreparedListener listener) {\n    mOnPreparedListener = listener;\n  }\n\n  /**\n   * Register a callback to be invoked when the end of a media source has been\n   * reached during playback.\n   *\n   * @param listener the callback that will be run\n   */\n  public void setOnCompletionListener(OnCompletionListener listener) {\n    mOnCompletionListener = listener;\n  }\n\n  /**\n   * Register a callback to be invoked when the status of a network stream's\n   * buffer has changed.\n   *\n   * @param listener the callback that will be run.\n   */\n  public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) {\n    mOnBufferingUpdateListener = listener;\n  }\n\n  /**\n   * Register a callback to be invoked when the segments cached on storage has\n   * changed.\n   *\n   * @param listener the callback that will be run.\n   */\n  public void setOnCachingUpdateListener(OnCachingUpdateListener listener) {\n    mOnCachingUpdateListener = listener;\n  }\n\n  private void updateCacheStatus(int type, int info, long[] segments) {\n    if (mEventHandler != null) {\n      Message m = mEventHandler.obtainMessage(MEDIA_CACHING_UPDATE);\n      Bundle b = m.getData();\n      b.putInt(MEDIA_CACHING_TYPE, type);\n      b.putInt(MEDIA_CACHING_INFO, info);\n      b.putLongArray(MEDIA_CACHING_SEGMENTS, segments);\n      mEventHandler.sendMessage(m);\n    }\n  }\n\n  /**\n   * Register a callback to be invoked when a seek operation has been completed.\n   *\n   * @param listener the callback that will be run\n   */\n  public void setOnSeekCompleteListener(OnSeekCompleteListener listener) {\n    mOnSeekCompleteListener = listener;\n  }\n\n  /**\n   * Register a callback to be invoked when the video size is known or updated.\n   *\n   * @param listener the callback that will be run\n   */\n  public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener) {\n    mOnVideoSizeChangedListener = listener;\n  }\n\n  /**\n   * Register a callback to be invoked when an error has happened during an\n   * asynchronous operation.\n   *\n   * @param listener the callback that will be run\n   */\n  public void setOnErrorListener(OnErrorListener listener) {\n    mOnErrorListener = listener;\n  }\n\n  public void setOnInfoListener(OnInfoListener listener) {\n    mOnInfoListener = listener;\n  }\n\n  /**\n   * Register a callback to be invoked when a timed text need to display.\n   *\n   * @param listener the callback that will be run\n   */\n  public void setOnTimedTextListener(OnTimedTextListener listener) {\n    mOnTimedTextListener = listener;\n  }\n\n  private void updateSub(int subType, byte[] bytes, String encoding, int width, int height) {\n    if (mEventHandler != null) {\n      Message m = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, width, height);\n      Bundle b = m.getData();\n      if (subType == SUBTITLE_TEXT) {\n        b.putInt(MEDIA_SUBTITLE_TYPE, SUBTITLE_TEXT);\n        if (encoding == null) {\n          b.putString(MEDIA_SUBTITLE_STRING, new String(bytes));\n        } else {\n          try {\n            b.putString(MEDIA_SUBTITLE_STRING, new String(bytes, encoding.trim()));\n          } catch (UnsupportedEncodingException e) {\n            Log.e(\"updateSub\", e);\n            b.putString(MEDIA_SUBTITLE_STRING, new String(bytes));\n          }\n        }\n      } else if (subType == SUBTITLE_BITMAP) {\n        b.putInt(MEDIA_SUBTITLE_TYPE, SUBTITLE_BITMAP);\n        b.putByteArray(MEDIA_SUBTITLE_BYTES, bytes);\n      }\n      mEventHandler.sendMessage(m);\n    }\n  }\n\n  protected native void _releaseVideoSurface();\n\n  /**\n   * Calling this result in only the audio track being played.\n   */\n  public void releaseDisplay() {\n    _releaseVideoSurface();\n    mSurfaceHolder = null;\n    mSurface = null;\n  }\n\n  /**\n   * Returns the aspect ratio of the video.\n   *\n   * @return the aspect ratio of the video, or 0 if there is no video, or the\n   *         width and height is not available.\n\t *         @see io.vov.vitamio.widget.VideoView#setVideoLayout(int, float)\n   */\n  public native float getVideoAspectRatio();\n\n  /**\n   * Set the quality when play video, if the video is too lag, you may try\n   * VIDEOQUALITY_LOW, default is VIDEOQUALITY_LOW.\n   *\n   * @param quality <ul>\n   *                <li>{@link #VIDEOQUALITY_HIGH}\n   *                <li>{@link #VIDEOQUALITY_MEDIUM}\n   *                <li>{@link #VIDEOQUALITY_LOW}\n   *                </ul>\n   */\n  public native void setVideoQuality(int quality);\n\n  /**\n   * Set the Video Chroma quality when play video, default is VIDEOCHROMA_RGB565\n   * You can set on after {@link #prepareAsync()}.\n   * @param chroma <ul>\n   *                <li>{@link #VIDEOCHROMA_RGB565}\n   *                <li>{@link #VIDEOCHROMA_RGBA}\n   *                </ul>\n   */\n  public native void setVideoChroma(int chroma);\n\n  /**\n   * Set if should deinterlace the video picture\n   *\n   * @param deinterlace\n   */\n  public native void setDeinterlace(boolean deinterlace);\n\n  /**\n   * The buffer to fill before playback, default is 1024*1024 Byte \n   *\n   * @param bufSize buffer size in Byte\n   */\n  public native void setBufferSize(long bufSize);\n\n  public native void audioInitedOk(long bufSize);\n  \n  /**\n   * Set video and audio playback speed\n   *\n   * @param speed e.g. 0.8 or 2.0, default to 1.0, range in [0.5-2]\n   */\n  public native void setPlaybackSpeed(float speed);\n\n  /**\n   * Checks whether the buffer is filled\n   *\n   * @return false if buffer is filled\n   */\n  public native boolean isBuffering();\n\n  /**\n   * @return the percent\n   * @see io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener\n   */\n  public native int getBufferProgress();\n\n  /**\n   * Get the encoding if haven't set with {@link #setMetaEncoding(String)}\n   *\n   * @return the encoding\n   */\n  public native String getMetaEncoding();\n\n  /**\n   * Set the encoding MediaPlayer will use to determine the metadata\n   *\n   * @param encoding e.g. \"UTF-8\"\n   */\n  public native void setMetaEncoding(String encoding);\n\n  /**\n   * Get the audio track number in playback\n   *\n   * @return track number\n   */\n\tpublic native int getAudioTrack();\n\n\t/**\n   * Get the video track number in playback\n\t *\n\t * @return track number\n\t */\n\tpublic native int getVideoTrack();\n\n  /**\n   * Tell the MediaPlayer whether to show timed text\n   *\n   * @param shown true if wanna show\n   */\n  public native void setTimedTextShown(boolean shown);\n\n  /**\n   * Set the encoding to display timed text.\n   *\n   * @param encoding MediaPlayer will detet it if null\n   */\n  public native void setTimedTextEncoding(String encoding);\n\n  /**\n   * @return <ul>\n   *         <li>{@link #SUBTITLE_EXTERNAL}\n   *         <li>{@link #SUBTITLE_INTERNAL}\n   *         </ul>\n   */\n  public native int getTimedTextLocation();\n\n  /**\n   * You can get the file-path of the external subtitle in use.\n   *\n   * @return null if no external subtitle\n   */\n  public native String getTimedTextPath();\n\n  /**\n   * Get the subtitle track number in playback\n   *\n   * @return track number\n   */\n  public native int getTimedTextTrack();\n\n  int sampleRateInHz;\n  int channels;\n  @SuppressLint(\"NewApi\")\n  \nprivate int audioTrackInit(int sampleRateInHz, int channels) {\n   this.sampleRateInHz=sampleRateInHz;\n   this.channels=channels;\n    return 0;\n  }\n  public int audioTrackInit() {\n//\t  Log.e(\"  ffff mediaplayer audiotrackinit start .  sampleRateInHz:=\" + sampleRateInHz + \" channels:=\" + channels );\n\t    audioTrackRelease();\n\t    int channelConfig = channels >= 2 ? AudioFormat.CHANNEL_OUT_STEREO : AudioFormat.CHANNEL_OUT_MONO;\n\t    try {\n\t      mAudioTrackBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT);\n\t      mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, AudioFormat.ENCODING_PCM_16BIT, mAudioTrackBufferSize, AudioTrack.MODE_STREAM);\n\t    } catch (Exception e) {\n\t      mAudioTrackBufferSize = 0;\n\t      Log.e(\"audioTrackInit\", e);\n\t    }\n\t    return mAudioTrackBufferSize;\n\t  }\n  \n  private void audioTrackSetVolume(float leftVolume, float rightVolume) {\n    if (mAudioTrack != null)\n      mAudioTrack.setStereoVolume(leftVolume, rightVolume);\n  }\n\n  private void audioTrackWrite(byte[] audioData, int offsetInBytes, int sizeInBytes) {\n    if (mAudioTrack != null && mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {\n      int written;\n      while (sizeInBytes > 0) {\n        written = sizeInBytes > mAudioTrackBufferSize ? mAudioTrackBufferSize : sizeInBytes;\n        mAudioTrack.write(audioData, offsetInBytes, written);\n        sizeInBytes -= written;\n        offsetInBytes += written;\n      }\n    }\n  }\n\n  private void audioTrackStart() {\n    if (mAudioTrack != null && mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED && mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING)\n      mAudioTrack.play();\n  }\n\n  private void audioTrackPause() {\n    if (mAudioTrack != null && mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED)\n      mAudioTrack.pause();\n  }\n\n  private void audioTrackRelease() {\n    if (mAudioTrack != null) {\n      if (mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED)\n        mAudioTrack.stop();\n      mAudioTrack.release();\n    }\n    mAudioTrack = null;\n  }\n  \n  public int getAudioSessionId() {\n\treturn mAudioTrack.getAudioSessionId();\n  }\n\n  private ByteBuffer surfaceInit() {\n    synchronized (this) {\n      mLocalSurface = mSurface;\n      int w = getVideoWidth_a();\n      int h = getVideoHeight_a();\n      if (mLocalSurface != null && w != 0 && h != 0) {\n        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);\n        mByteBuffer = ByteBuffer.allocateDirect(w * h * 2);\n      } else {\n        mBitmap = null;\n        mByteBuffer = null;\n      }\n      return mByteBuffer;\n    }\n  }\n\n  private void surfaceRender() {\n    synchronized (this) {\n      if (mLocalSurface == null || !mLocalSurface.isValid() || mBitmap == null || mByteBuffer == null)\n        return;\n\n      try {\n        Canvas c = mLocalSurface.lockCanvas(null);\n        mBitmap.copyPixelsFromBuffer(mByteBuffer);\n        c.drawBitmap(mBitmap, 0, 0, null);\n        mLocalSurface.unlockCanvasAndPost(c);\n      } catch (Exception e) {\n        Log.e(\"surfaceRender\", e);\n      }\n    }\n  }\n\n  private void surfaceRelease() {\n    synchronized (this) {\n      mLocalSurface = null;\n      mBitmap = null;\n      mByteBuffer = null;\n    }\n  }\n\n  public interface OnHWRenderFailedListener {\n    public void onFailed();\n  }\n\n  public interface OnPreparedListener {\n    /**\n     * Called when the media file is ready for playback.\n     *\n     * @param mp the MediaPlayer that is ready for playback\n     */\n    void onPrepared(MediaPlayer mp);\n  }\n\n  public interface OnCompletionListener {\n    /**\n     * Called when the end of a media source is reached during playback.\n     *\n     * @param mp the MediaPlayer that reached the end of the file\n     */\n    void onCompletion(MediaPlayer mp);\n  }\n\n  public interface OnBufferingUpdateListener {\n    /**\n     * Called to update status in buffering a media stream. Buffering is storing\n     * data in memory while caching on external storage.\n     *\n     * @param mp      the MediaPlayer the update pertains to\n     * @param percent the percentage (0-100) of the buffer that has been filled thus\n     *                far\n     */\n    void onBufferingUpdate(MediaPlayer mp, int percent);\n  }\n\n  public interface OnCachingUpdateListener {\n    /**\n     * Called to update status in caching a media stream. Caching is storing\n     * data on external storage while buffering in memory.\n     *\n     * @param mp       the MediaPlayer the update pertains to\n     * @param segments the cached segments in bytes, in format [s1begin, s1end,\n     *                 s2begin, s2end], s1begin < s1end < s2begin < s2end. e.g. [124,\n     *                 100423, 4321412, 214323433]\n     */\n    void onCachingUpdate(MediaPlayer mp, long[] segments);\n\n    /**\n     * Cache speed\n     *\n     * @param mp    the MediaPlayer the update pertains to\n     * @param speed the cached speed size kb/s\n     */\n    void onCachingSpeed(MediaPlayer mp, int speed);\n    \n    /**\n     * Cache start\n     * @param mp\n     */\n    void onCachingStart(MediaPlayer mp);\n    \n    /**  \n   \t * Cache compelete  \n   \t */  \n   \tvoid onCachingComplete(MediaPlayer mp); \n\n    /**\n     * Cache not available\n     *\n     * @param mp   the MediaPlayer the update pertains to\n     * @param info the not available info\n     *             <ul>\n     *             <li>{@link #CACHE_INFO_NO_SPACE}\n     *             <li>{@link #CACHE_INFO_STREAM_NOT_SUPPORT}\n     *             </ul>\n     */\n    void onCachingNotAvailable(MediaPlayer mp, int info);\n  }\n\n  public interface OnSeekCompleteListener {\n    /**\n     * Called to indicate the completion of a seek operation.\n     *\n     * @param mp the MediaPlayer that issued the seek operation\n     */\n    public void onSeekComplete(MediaPlayer mp);\n  }\n\n  public interface OnVideoSizeChangedListener {\n    /**\n     * Called to indicate the video size\n     *\n     * @param mp     the MediaPlayer associated with this callback\n     * @param width  the width of the video\n     * @param height the height of the video\n     */\n    public void onVideoSizeChanged(MediaPlayer mp, int width, int height);\n  }\n\n  public interface OnErrorListener {\n    /**\n     * Called to indicate an error.\n     *\n     * @param mp    the MediaPlayer the error pertains to\n     * @param what  the type of error that has occurred:\n     *              <ul>\n     *              <li>{@link #MEDIA_ERROR_UNKNOWN}\n     *              <li>\n     *              {@link #MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK}\n     *              </ul>\n     * @param extra an extra code, specific to the error. Typically implementation\n     *              dependant.\n     * @return True if the method handled the error, false if it didn't.\n     *         Returning false, or not having an OnErrorListener at all, will\n     *         cause the OnCompletionListener to be called.\n     */\n    boolean onError(MediaPlayer mp, int what, int extra);\n  }\n\n  public interface OnInfoListener {\n    /**\n     * Called to indicate an info or a warning.\n     *\n     * @param mp    the MediaPlayer the info pertains to.\n     * @param what  the type of info or warning.\n     *              <ul>\n     *              <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}\n     *              <li>{@link #MEDIA_INFO_BUFFERING_START}\n     *              <li>{@link #MEDIA_INFO_BUFFERING_END}\n     *              <li>{@link #MEDIA_INFO_NOT_SEEKABLE}\n     *              <li>{@link #MEDIA_INFO_DOWNLOAD_RATE_CHANGED}\n     *              </ul>\n     * @param extra an extra code, specific to the info. Typically implementation\n     *              dependant.\n     * @return True if the method handled the info, false if it didn't.\n     *         Returning false, or not having an OnErrorListener at all, will\n     *         cause the info to be discarded.\n     */\n    boolean onInfo(MediaPlayer mp, int what, int extra);\n  }\n\n  public interface OnTimedTextListener {\n    /**\n     * Called to indicate that a text timed text need to display\n     *\n     * @param text the timedText to display\n     */\n    public void onTimedText(String text);\n\n    /**\n     * Called to indicate that an image timed text need to display\n     *\n     * @param pixels the pixels of the timed text image\n     * @param width  the width of the timed text image\n     * @param height the height of the timed text image\n     */\n    public void onTimedTextUpdate(byte[] pixels, int width, int height);\n  }\n\n  /**\n   * Class for MediaPlayer to return each audio/video/subtitle track's metadata.\n   *\n   * @see io.vov.vitamio.MediaPlayer#getTrackInfo\n   */\n  static public class TrackInfo {\n    public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;\n    public static final int MEDIA_TRACK_TYPE_VIDEO = 1;\n    public static final int MEDIA_TRACK_TYPE_AUDIO = 2;\n    public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;\n    public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;\n    final int mTrackType;\n    final SparseArray<MediaFormat> mTrackInfoArray;\n\n    TrackInfo(int trackType, SparseArray<MediaFormat> trackInfoArray) {\n      mTrackType = trackType;\n      mTrackInfoArray = trackInfoArray;\n    }\n\n    /**\n     * Gets the track type.\n     *\n     * @return TrackType which indicates if the track is video, audio, timed\n     *         text.\n     */\n    public int getTrackType() {\n      return mTrackType;\n    }\n\n    /**\n     * Gets the track info\n     *\n     * @return map trackIndex to MediaFormat\n     */\n    public SparseArray<MediaFormat> getTrackInfoArray() {\n      return mTrackInfoArray;\n    }\n  }\n\n  private boolean mNeedResume = false;\n  private boolean mInBuffering = false;\n  @SuppressLint(\"HandlerLeak\")\n  private class EventHandler extends Handler {\n    private MediaPlayer mMediaPlayer;\n    private Bundle mData;\n\n    public EventHandler(MediaPlayer mp, Looper looper) {\n      super(looper);\n      mMediaPlayer = mp;\n    }\n\n    public void release() {\n      mMediaPlayer = null;\n    }\n\n    private void onInfo(Message msg) {\n        switch (msg.arg1) {\n            case MediaPlayer.MEDIA_INFO_BUFFERING_START:\n                {\n                    //Log.i(\"MiuiVideo: receive buffer start, isplaying \" + isPlaying());\n                    mInBuffering = true;\n                    if (isPlaying()) {\n                        //Log.i(\"MiuiVideo: pause player for buffer start\");\n                        _pause();\n                        mNeedResume = true;\n                    }\n                    break;\n                }\n            case MediaPlayer.MEDIA_INFO_BUFFERING_END:\n                {\n                    //Log.i(\"MiuiVideo: receive buffer end, needResume = \" + mNeedResume);\n                    mInBuffering = false;\n                    if (mNeedResume) {\n                        //Log.i(\"MiuiVideo: start player after buffer\");\n                        _start();\n                        mNeedResume = false;\n                    }\n                    break;\n                }\n            default:\n                break;\n        }\n        if (mOnInfoListener != null) {\n            mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);\n        }\n    }\n\n    private void onBufferingUpdate(Message msg) {\n        int percent = msg.arg1;\n        if (mOnBufferingUpdateListener != null)\n            mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);\n        if (percent >= 100 && mInBuffering) {\n            //Log.i(\"MiuiVideo: receive buffer 100, needResume = \" + mNeedResume);\n            mInBuffering = false;\n            if (mNeedResume) {\n                //Log.i(\"MiuiVideo: start player after buffer 100\");\n                _start();\n                mNeedResume = false;\n            }\n            if (mOnInfoListener != null) {\n                //Log.i(\"MiuiVideo: add one buffer end event\");\n                mOnInfoListener.onInfo(mMediaPlayer, MediaPlayer.MEDIA_INFO_BUFFERING_END, percent);\n            }\n        }\n    }\n\n    @Override\n    public void handleMessage(Message msg) {\n      if (mMediaPlayer == null) {\n//        //Log.i(\"MiuiVideo: get message after player released, msg type: \" + msg.what);\n        return;\n      }\n      switch (msg.what) {\n        case MEDIA_PREPARED:\n          if (mOnPreparedListener != null)\n            mOnPreparedListener.onPrepared(mMediaPlayer);\n          return;\n        case MEDIA_PLAYBACK_COMPLETE:\n          if (mOnCompletionListener != null)\n            mOnCompletionListener.onCompletion(mMediaPlayer);\n          stayAwake(false);\n          return;\n        case MEDIA_BUFFERING_UPDATE:\n          onBufferingUpdate(msg);\n          return;\n        case MEDIA_SEEK_COMPLETE:\n          if (isPlaying())\n            stayAwake(true);\n          if (mOnSeekCompleteListener != null)\n            mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);\n          return;\n        case MEDIA_SET_VIDEO_SIZE:\n          if (mOnVideoSizeChangedListener != null)\n            mOnVideoSizeChangedListener.onVideoSizeChanged(mMediaPlayer, msg.arg1, msg.arg2);\n          return;\n        case MEDIA_ERROR:\n          Log.e(\"Error (%d, %d)\", msg.arg1, msg.arg2);\n          boolean error_was_handled = false;\n          if (mOnErrorListener != null)\n            error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);\n          if (mOnCompletionListener != null && !error_was_handled)\n            mOnCompletionListener.onCompletion(mMediaPlayer);\n          stayAwake(false);\n          return;\n        case MEDIA_INFO:\n          Log.i(\"Info (%d, %d)\", msg.arg1, msg.arg2);\n          if (mOnInfoListener != null)\n            mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);\n          return;\n        case MEDIA_CACHE:\n          return;\n        case MEDIA_TIMED_TEXT:\n          mData = msg.getData();\n          if (mData.getInt(MEDIA_SUBTITLE_TYPE) == SUBTITLE_TEXT) {\n            Log.i(\"Subtitle : %s\", mData.getString(MEDIA_SUBTITLE_STRING));\n            if (mOnTimedTextListener != null)\n              mOnTimedTextListener.onTimedText(mData.getString(MEDIA_SUBTITLE_STRING));\n          } else if (mData.getInt(MEDIA_SUBTITLE_TYPE) == SUBTITLE_BITMAP) {\n            Log.i(\"Subtitle : bitmap\");\n            if (mOnTimedTextListener != null)\n              mOnTimedTextListener.onTimedTextUpdate(mData.getByteArray(MEDIA_SUBTITLE_BYTES), msg.arg1, msg.arg2);\n          }\n          return;\n        case MEDIA_CACHING_UPDATE:\n          if (mOnCachingUpdateListener != null) {\n            int cacheType = msg.getData().getInt(MEDIA_CACHING_TYPE);\n            if (cacheType == CACHE_TYPE_NOT_AVAILABLE) {\n              mOnCachingUpdateListener.onCachingNotAvailable(mMediaPlayer, msg.getData().getInt(MEDIA_CACHING_INFO));\n            } else if (cacheType == CACHE_TYPE_UPDATE) {\n              mOnCachingUpdateListener.onCachingUpdate(mMediaPlayer, msg.getData().getLongArray(MEDIA_CACHING_SEGMENTS));\n            } else if (cacheType == CACHE_TYPE_SPEED) {\n              mOnCachingUpdateListener.onCachingSpeed(mMediaPlayer, msg.getData().getInt(MEDIA_CACHING_INFO));\n            } else if (cacheType == CACHE_TYPE_START) {\n            \tmOnCachingUpdateListener.onCachingStart(mMediaPlayer);\n            } else if (cacheType == CACHE_TYPE_COMPLETE) {\n            \tmOnCachingUpdateListener.onCachingComplete(mMediaPlayer);\n            }\n          }\n          return;\n        case MEDIA_NOP:\n          return;\n        case MEDIA_HW_ERROR:\n        \tif (mOnHWRenderFailedListener != null)\n        \t\tmOnHWRenderFailedListener.onFailed();\n        \treturn;\n        default:\n          Log.e(\"Unknown message type \" + msg.what);\n          return;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/MediaScanner.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\nimport android.content.ContentProviderClient;\nimport android.content.ContentUris;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.SQLException;\nimport android.net.Uri;\nimport android.os.RemoteException;\nimport android.text.TextUtils;\n\nimport io.vov.vitamio.provider.MediaStore;\nimport io.vov.vitamio.provider.MediaStore.Video;\nimport io.vov.vitamio.utils.ContextUtils;\nimport io.vov.vitamio.utils.FileUtils;\nimport io.vov.vitamio.utils.Log;\n\nimport java.io.File;\nimport java.util.HashMap;\nimport java.util.Iterator;\n\npublic class MediaScanner {\n  private static final String[] VIDEO_PROJECTION = new String[]{Video.Media._ID, Video.Media.DATA, Video.Media.DATE_MODIFIED,};\n  private static final int ID_VIDEO_COLUMN_INDEX = 0;\n  private static final int PATH_VIDEO_COLUMN_INDEX = 1;\n  private static final int DATE_MODIFIED_VIDEO_COLUMN_INDEX = 2;\n  private Context mContext;\n  private ContentProviderClient mProvider;\n  private boolean mCaseInsensitivePaths;\n  private HashMap<String, FileCacheEntry> mFileCache;\n  private MyMediaScannerClient mClient = new MyMediaScannerClient();\n\n  public MediaScanner(Context ctx) {\n    mContext = ctx;\n    native_init(mClient);\n  }\n\n  private static native boolean loadFFmpeg_native(String ffmpegPath);\n\n  private void initialize() {\n    mCaseInsensitivePaths = true;\n  }\n\n  private void prescan(String filePath) throws RemoteException {\n    mProvider = mContext.getContentResolver().acquireContentProviderClient(MediaStore.AUTHORITY);\n    Cursor c = null;\n    String where = null;\n    String[] selectionArgs = null;\n\n    if (mFileCache == null)\n      mFileCache = new HashMap<String, FileCacheEntry>();\n    else\n      mFileCache.clear();\n\n    try {\n      if (filePath != null) {\n        where = Video.Media.DATA + \"=?\";\n        selectionArgs = new String[]{filePath};\n      }\n\n      c = mProvider.query(Video.Media.CONTENT_URI, VIDEO_PROJECTION, where, selectionArgs, null);\n      if (c != null) {\n        try {\n          while (c.moveToNext()) {\n            long rowId = c.getLong(ID_VIDEO_COLUMN_INDEX);\n            String path = c.getString(PATH_VIDEO_COLUMN_INDEX);\n            long lastModified = c.getLong(DATE_MODIFIED_VIDEO_COLUMN_INDEX);\n            if (path.startsWith(\"/\")) {\n              File tempFile = new File(path);\n              if (!TextUtils.isEmpty(filePath) && !tempFile.exists()) {\n                mProvider.delete(Video.Media.CONTENT_URI, where, selectionArgs);\n                return;\n              }\n              path = FileUtils.getCanonical(tempFile);\n              String key = mCaseInsensitivePaths ? path.toLowerCase() : path;\n              mFileCache.put(key, new FileCacheEntry(Video.Media.CONTENT_URI, rowId, path, lastModified));\n            }\n          }\n        } finally {\n          c.close();\n          c = null;\n        }\n      }\n    } finally {\n      if (c != null) {\n        c.close();\n      }\n    }\n  }\n\n  ;\n\n  private void postscan(String[] directories) throws RemoteException {\n    Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();\n\n    while (iterator.hasNext()) {\n      FileCacheEntry entry = iterator.next();\n      String path = entry.mPath;\n\n      if (!entry.mSeenInFileSystem) {\n        if (inScanDirectory(path, directories) && !new File(path).exists()) {\n          mProvider.delete(ContentUris.withAppendedId(entry.mTableUri, entry.mRowId), null, null);\n          iterator.remove();\n        }\n      }\n    }\n\n    mFileCache.clear();\n    mFileCache = null;\n    mProvider.release();\n    mProvider = null;\n  }\n\n  private boolean inScanDirectory(String path, String[] directories) {\n    for (int i = 0; i < directories.length; i++) {\n      if (path.startsWith(directories[i]))\n        return true;\n    }\n    return false;\n  }\n\n  public void scanDirectories(String[] directories) {\n    try {\n      long start = System.currentTimeMillis();\n      prescan(null);\n      long prescan = System.currentTimeMillis();\n\n      for (int i = 0; i < directories.length; i++) {\n        if (!TextUtils.isEmpty(directories[i])) {\n          directories[i] = ContextUtils.fixLastSlash(directories[i]);\n          processDirectory(directories[i], MediaFile.sFileExtensions);\n        }\n      }\n\n      long scan = System.currentTimeMillis();\n      postscan(directories);\n      long end = System.currentTimeMillis();\n\n      Log.d(\" prescan time: %dms\", prescan - start);\n      Log.d(\"    scan time: %dms\", scan - prescan);\n      Log.d(\"postscan time: %dms\", end - scan);\n      Log.d(\"   total time: %dms\", end - start);\n    } catch (SQLException e) {\n      Log.e(\"SQLException in MediaScanner.scan()\", e);\n    } catch (UnsupportedOperationException e) {\n      Log.e(\"UnsupportedOperationException in MediaScanner.scan()\", e);\n    } catch (RemoteException e) {\n      Log.e(\"RemoteException in MediaScanner.scan()\", e);\n    }\n  }\n\n  public Uri scanSingleFile(String path, String mimeType) {\n    try {\n      prescan(path);\n      File file = new File(path);\n      long lastModifiedSeconds = file.lastModified() / 1000;\n\n      return mClient.doScanFile(path, lastModifiedSeconds, file.length(), true);\n    } catch (RemoteException e) {\n      Log.e(\"RemoteException in MediaScanner.scanFile()\", e);\n      return null;\n    }\n  }\n\n  static {\n    String LIB_ROOT = Vitamio.getLibraryPath();\n    Log.i(\"LIB ROOT: %s\", LIB_ROOT);\n    System.load( LIB_ROOT + \"libstlport_shared.so\");\n    System.load( LIB_ROOT + \"libvscanner.so\");\n    loadFFmpeg_native( LIB_ROOT + \"libffmpeg.so\");\n  }\n\n  private native void processDirectory(String path, String extensions);\n\n  private native boolean processFile(String path, String mimeType);\n\n  private native final void native_init(MediaScannerClient client);\n\n  public native void release();\n\n  private native final void native_finalize();\n\n  @Override\n  protected void finalize() throws Throwable {\n    try {\n      native_finalize();\n    } finally {\n      super.finalize();\n    }\n  }\n\n  private static class FileCacheEntry {\n    Uri mTableUri;\n    long mRowId;\n    String mPath;\n    long mLastModified;\n    boolean mLastModifiedChanged;\n    boolean mSeenInFileSystem;\n\n    FileCacheEntry(Uri tableUri, long rowId, String path, long lastModified) {\n      mTableUri = tableUri;\n      mRowId = rowId;\n      mPath = path;\n      mLastModified = lastModified;\n      mSeenInFileSystem = false;\n      mLastModifiedChanged = false;\n    }\n\n    @Override\n    public String toString() {\n      return mPath;\n    }\n  }\n\n  private class MyMediaScannerClient implements MediaScannerClient {\n    private String mMimeType;\n    private int mFileType;\n    private String mPath;\n    private long mLastModified;\n    private long mFileSize;\n    private String mTitle;\n    private String mArtist;\n    private String mAlbum;\n    private String mLanguage;\n    private long mDuration;\n    private int mWidth;\n    private int mHeight;\n\n    public FileCacheEntry beginFile(String path, long lastModified, long fileSize) {\n      int lastSlash = path.lastIndexOf('/');\n      if (lastSlash >= 0 && lastSlash + 2 < path.length()) {\n        if (path.regionMatches(lastSlash + 1, \"._\", 0, 2))\n          return null;\n\n        if (path.regionMatches(true, path.length() - 4, \".jpg\", 0, 4)) {\n          if (path.regionMatches(true, lastSlash + 1, \"AlbumArt_{\", 0, 10) || path.regionMatches(true, lastSlash + 1, \"AlbumArt.\", 0, 9)) {\n            return null;\n          }\n          int length = path.length() - lastSlash - 1;\n          if ((length == 17 && path.regionMatches(true, lastSlash + 1, \"AlbumArtSmall\", 0, 13)) || (length == 10 && path.regionMatches(true, lastSlash + 1, \"Folder\", 0, 6))) {\n            return null;\n          }\n        }\n      }\n\n      MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);\n      if (mediaFileType != null) {\n        mFileType = mediaFileType.fileType;\n        mMimeType = mediaFileType.mimeType;\n      }\n\n      String key = FileUtils.getCanonical(new File(path));\n      if (mCaseInsensitivePaths)\n        key = path.toLowerCase();\n      FileCacheEntry entry = mFileCache.get(key);\n      if (entry == null) {\n        entry = new FileCacheEntry(null, 0, path, 0);\n        mFileCache.put(key, entry);\n      }\n      entry.mSeenInFileSystem = true;\n\n      long delta = lastModified - entry.mLastModified;\n      if (delta > 1 || delta < -1) {\n        entry.mLastModified = lastModified;\n        entry.mLastModifiedChanged = true;\n      }\n\n      mPath = path;\n      mLastModified = lastModified;\n      mFileSize = fileSize;\n      mTitle = null;\n      mDuration = 0;\n\n      return entry;\n    }\n\n    public void scanFile(String path, long lastModified, long fileSize) {\n      Log.i(\"scanFile: %s\", path);\n      doScanFile(path, lastModified, fileSize, false);\n    }\n\n    public Uri doScanFile(String path, long lastModified, long fileSize, boolean scanAlways) {\n      Uri result = null;\n      try {\n        FileCacheEntry entry = beginFile(path, lastModified, fileSize);\n        if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {\n          if (processFile(path, null)) {\n            result = endFile(entry);\n          } else {\n            if (mCaseInsensitivePaths)\n              mFileCache.remove(path.toLowerCase());\n            else\n              mFileCache.remove(path);\n          }\n        }\n      } catch (RemoteException e) {\n        Log.e(\"RemoteException in MediaScanner.scanFile()\", e);\n      }\n      return result;\n    }\n\n    private int parseSubstring(String s, int start, int defaultValue) {\n      int length = s.length();\n      if (start == length)\n        return defaultValue;\n\n      char ch = s.charAt(start++);\n      if (ch < '0' || ch > '9')\n        return defaultValue;\n\n      int result = ch - '0';\n      while (start < length) {\n        ch = s.charAt(start++);\n        if (ch < '0' || ch > '9')\n          return result;\n        result = result * 10 + (ch - '0');\n      }\n\n      return result;\n    }\n\n    public void handleStringTag(String name, byte[] valueBytes, String valueEncoding) {\n      String value;\n      try {\n        value = new String(valueBytes, valueEncoding);\n      } catch (Exception e) {\n        Log.e(\"handleStringTag\", e);\n        value = new String(valueBytes);\n      }\n      Log.i(\"%s : %s\", name, value);\n\n      if (name.equalsIgnoreCase(\"title\")) {\n        mTitle = value;\n      } else if (name.equalsIgnoreCase(\"artist\")) {\n        mArtist = value.trim();\n      } else if (name.equalsIgnoreCase(\"albumartist\")) {\n        if (TextUtils.isEmpty(mArtist))\n          mArtist = value.trim();\n      } else if (name.equalsIgnoreCase(\"album\")) {\n        mAlbum = value.trim();\n      } else if (name.equalsIgnoreCase(\"language\")) {\n        mLanguage = value.trim();\n      } else if (name.equalsIgnoreCase(\"duration\")) {\n        mDuration = parseSubstring(value, 0, 0);\n      } else if (name.equalsIgnoreCase(\"width\")) {\n        mWidth = parseSubstring(value, 0, 0);\n      } else if (name.equalsIgnoreCase(\"height\")) {\n        mHeight = parseSubstring(value, 0, 0);\n      }\n    }\n\n    public void setMimeType(String mimeType) {\n      Log.i(\"setMimeType: %s\", mimeType);\n      mMimeType = mimeType;\n      mFileType = MediaFile.getFileTypeForMimeType(mimeType);\n    }\n\n    private ContentValues toValues() {\n      ContentValues map = new ContentValues();\n\n      map.put(MediaStore.MediaColumns.DATA, mPath);\n      map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);\n      map.put(MediaStore.MediaColumns.SIZE, mFileSize);\n      map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);\n      map.put(MediaStore.MediaColumns.TITLE, mTitle);\n\n      if (MediaFile.isVideoFileType(mFileType)) {\n        map.put(Video.Media.DURATION, mDuration);\n        map.put(Video.Media.LANGUAGE, mLanguage);\n        map.put(Video.Media.ALBUM, mAlbum);\n        map.put(Video.Media.ARTIST, mArtist);\n        map.put(Video.Media.WIDTH, mWidth);\n        map.put(Video.Media.HEIGHT, mHeight);\n      }\n\n      return map;\n    }\n\n    private Uri endFile(FileCacheEntry entry) throws RemoteException {\n      Uri tableUri;\n      boolean isVideo = MediaFile.isVideoFileType(mFileType) && mWidth > 0 && mHeight > 0;\n      if (isVideo) {\n        tableUri = Video.Media.CONTENT_URI;\n      } else {\n        return null;\n      }\n      entry.mTableUri = tableUri;\n\n      ContentValues values = toValues();\n      String title = values.getAsString(MediaStore.MediaColumns.TITLE);\n      if (TextUtils.isEmpty(title)) {\n        title = values.getAsString(MediaStore.MediaColumns.DATA);\n        int lastSlash = title.lastIndexOf('/');\n        if (lastSlash >= 0) {\n          lastSlash++;\n          if (lastSlash < title.length())\n            title = title.substring(lastSlash);\n        }\n        int lastDot = title.lastIndexOf('.');\n        if (lastDot > 0)\n          title = title.substring(0, lastDot);\n        values.put(MediaStore.MediaColumns.TITLE, title);\n      }\n\n      long rowId = entry.mRowId;\n\n      Uri result = null;\n      if (rowId == 0) {\n        result = mProvider.insert(tableUri, values);\n        if (result != null) {\n          rowId = ContentUris.parseId(result);\n          entry.mRowId = rowId;\n        }\n      } else {\n        result = ContentUris.withAppendedId(tableUri, rowId);\n        mProvider.update(result, values, null, null);\n      }\n\n      return result;\n    }\n\n    public void addNoMediaFolder(String path) {\n      ContentValues values = new ContentValues();\n      values.put(MediaStore.MediaColumns.DATA, \"\");\n      String[] pathSpec = new String[]{path + '%'};\n      try {\n        mProvider.update(Video.Media.CONTENT_URI, values, MediaStore.MediaColumns.DATA + \" LIKE ?\", pathSpec);\n      } catch (RemoteException e) {\n        throw new RuntimeException();\n      }\n    }\n  }\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/MediaScannerClient.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\n/**\n * DON'T TOUCH THIS FILE IF YOU DON'T KNOW THE MediaScanner PROCEDURE!!!\n */\npublic interface MediaScannerClient {\n  public void scanFile(String path, long lastModified, long fileSize);\n\n  public void addNoMediaFolder(String path);\n\n  public void handleStringTag(String name, byte[] value, String valueEncoding);\n\n  public void setMimeType(String mimeType);\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/Metadata.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\nimport android.util.SparseArray;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.Locale;\nimport java.util.Map;\n\n/**\n * See {@link io.vov.vitamio.MediaPlayer#getMetadata()}\n */\npublic class Metadata {\n  public static final int ANY = 0;\n  public static final int TITLE = 1; // String\n  public static final int COMMENT = 2; // String\n  public static final int COPYRIGHT = 3; // String\n  public static final int ALBUM = 4; // String\n  public static final int ARTIST = 5; // String\n  public static final int AUTHOR = 6; // String\n  public static final int COMPOSER = 7; // String\n  public static final int GENRE = 8; // String\n  public static final int DATE = 9; // Date\n  public static final int DURATION = 10; // Integer(milliseconds)\n  public static final int CD_TRACK_NUM = 11; // Integer (1-based)\n  public static final int CD_TRACK_MAX = 12; // Integer\n  public static final int RATING = 13; // String\n  public static final int ALBUM_ART = 14; // byte[]\n  public static final int VIDEO_FRAME = 15; // Bitmap\n  public static final int LENGTH = 16; // Integer (bytes)\n  public static final int BIT_RATE = 17; // Integer\n  public static final int AUDIO_BIT_RATE = 18; // Integer\n  public static final int VIDEO_BIT_RATE = 19; // Integer\n  public static final int AUDIO_SAMPLE_RATE = 20; // Integer\n  public static final int VIDEO_FRAME_RATE = 21; // Float\n  // See RFC2046 and RFC4281.\n  public static final int MIME_TYPE = 22; // String\n  public static final int AUDIO_CODEC = 23; // String\n  public static final int VIDEO_CODEC = 24; // String\n  public static final int VIDEO_HEIGHT = 25; // Integer\n  public static final int VIDEO_WIDTH = 26; // Integer\n  public static final int NUM_TRACKS = 27; // Integer\n  public static final int DRM_CRIPPLED = 28; // Boolean\n  public static final int PAUSE_AVAILABLE = 29; // Boolean\n  public static final int SEEK_BACKWARD_AVAILABLE = 30; // Boolean\n  public static final int SEEK_FORWARD_AVAILABLE = 31; // Boolean\n  public static final int SEEK_AVAILABLE = 32; // Boolean\n  private static final int LAST_SYSTEM = 32;\n  private static final int FIRST_CUSTOM = 8192;\n  private SparseArray<byte[]> mMeta = new SparseArray<byte[]>();\n  private String mEncoding = \"UTF-8\";\n\n  public boolean parse(Map<byte[], byte[]> meta, String encoding) {\n    String key = null;\n    byte[] value = null;\n    mEncoding = encoding;\n    for (byte[] keyBytes : meta.keySet()) {\n      try {\n        key = new String(keyBytes, mEncoding).trim().toLowerCase(Locale.US);\n      } catch (UnsupportedEncodingException e) {\n        key = new String(keyBytes).trim().toLowerCase(Locale.US);\n      }\n      value = meta.get(keyBytes);\n      if (key.equals(\"title\")) {\n        mMeta.put(TITLE, value);\n      } else if (key.equals(\"comment\")) {\n        mMeta.put(COMMENT, value);\n      } else if (key.equals(\"copyright\")) {\n        mMeta.put(COPYRIGHT, value);\n      } else if (key.equals(\"album\")) {\n        mMeta.put(ALBUM, value);\n      } else if (key.equals(\"artist\")) {\n        mMeta.put(ARTIST, value);\n      } else if (key.equals(\"author\")) {\n        mMeta.put(AUTHOR, value);\n      } else if (key.equals(\"composer\")) {\n        mMeta.put(COMPOSER, value);\n      } else if (key.equals(\"genre\")) {\n        mMeta.put(GENRE, value);\n      } else if (key.equals(\"creation_time\") || key.equals(\"date\")) {\n        mMeta.put(DATE, value);\n      } else if (key.equals(\"duration\")) {\n        mMeta.put(DURATION, value);\n      } else if (key.equals(\"length\")) {\n        mMeta.put(LENGTH, value);\n      } else if (key.equals(\"bit_rate\")) {\n        mMeta.put(BIT_RATE, value);\n      } else if (key.equals(\"audio_bit_rate\")) {\n        mMeta.put(AUDIO_BIT_RATE, value);\n      } else if (key.equals(\"video_bit_rate\")) {\n        mMeta.put(VIDEO_BIT_RATE, value);\n      } else if (key.equals(\"audio_sample_rate\")) {\n        mMeta.put(AUDIO_SAMPLE_RATE, value);\n      } else if (key.equals(\"video_frame_rate\")) {\n        mMeta.put(VIDEO_FRAME_RATE, value);\n      } else if (key.equals(\"format\")) {\n        mMeta.put(MIME_TYPE, value);\n      } else if (key.equals(\"audio_codec\")) {\n        mMeta.put(AUDIO_CODEC, value);\n      } else if (key.equals(\"video_codec\")) {\n        mMeta.put(VIDEO_CODEC, value);\n      } else if (key.equals(\"video_height\")) {\n        mMeta.put(VIDEO_HEIGHT, value);\n      } else if (key.equals(\"video_width\")) {\n        mMeta.put(VIDEO_WIDTH, value);\n      } else if (key.equals(\"num_tracks\")) {\n        mMeta.put(NUM_TRACKS, value);\n      } else if (key.equals(\"cap_pause\")) {\n        mMeta.put(PAUSE_AVAILABLE, value);\n      } else if (key.equals(\"cap_seek\")) {\n        mMeta.put(SEEK_AVAILABLE, value);\n      }\n    }\n\n    if (BuildConfig.DEBUG) {\n      android.util.Log.i(\"Vitamio[Metadata]\", \"title:\\t\\t\" + getString(TITLE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"comment:\\t\\t\" + getString(COMMENT));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"copyright:\\t\\t\" + getString(COPYRIGHT));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"album:\\t\\t\" + getString(ALBUM));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"artist:\\t\\t\" + getString(ARTIST));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"composer:\\t\\t\" + getString(COMPOSER));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"genre:\\t\\t\" + getString(GENRE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"date:\\t\\t\" + getString(DATE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"duration:\\t\\t\" + getLong(DURATION));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"length:\\t\\t\" + getLong(LENGTH));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"bit_rate:\\t\\t\" + getInt(BIT_RATE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"audio_bit_rate:\\t\" + getInt(AUDIO_BIT_RATE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"video_bit_rate:\\t\" + getInt(VIDEO_BIT_RATE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"audio_sample_rate:\\t\" + getInt(AUDIO_SAMPLE_RATE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"video_frame_rate:\\t\" + getDouble(VIDEO_FRAME_RATE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"format:\\t\\t\" + getString(MIME_TYPE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"audio_codec:\\t\" + getString(AUDIO_CODEC));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"video_codec:\\t\" + getString(VIDEO_CODEC));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"video_height:\\t\" + getInt(VIDEO_HEIGHT));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"video_width:\\t\" + getInt(VIDEO_WIDTH));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"num_tracks:\\t\\t\" + getInt(NUM_TRACKS));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"cap_pause:\\t\\t\" + getBoolean(PAUSE_AVAILABLE));\n      android.util.Log.i(\"Vitamio[Metadata]\", \"cap_seek:\\t\\t\" + getBoolean(SEEK_AVAILABLE));\n    }\n\n    return true;\n  }\n\n  public boolean has(final int metadataId) {\n    if (!checkMetadataId(metadataId)) {\n      throw new IllegalArgumentException(\"Invalid key: \" + metadataId);\n    }\n    return mMeta.indexOfKey(metadataId) >= 0;\n  }\n\n  public String getString(final int key) {\n    byte[] value = mMeta.get(key);\n    if (value == null) {\n      return null;\n    }\n    try {\n      return new String(value, mEncoding);\n    } catch (UnsupportedEncodingException e) {\n      return new String(value);\n    }\n  }\n\n  public int getInt(final int key) {\n    try {\n      return Integer.parseInt(getString(key));\n    } catch (Exception e) {\n      return -1;\n    }\n  }\n\n  public boolean getBoolean(final int key) {\n    try {\n      return Boolean.parseBoolean(getString(key));\n    } catch (Exception e) {\n      return false;\n    }\n  }\n\n  public long getLong(final int key) {\n    try {\n      return Long.parseLong(getString(key));\n    } catch (Exception e) {\n      return -1;\n    }\n  }\n\n  public double getDouble(final int key) {\n    try {\n      return Double.parseDouble(getString(key));\n    } catch (Exception e) {\n      return -1;\n    }\n  }\n\n  public byte[] getByteArray(final int key) {\n    return mMeta.get(key);\n  }\n\n  private boolean checkMetadataId(final int val) {\n    if (val <= ANY || (LAST_SYSTEM < val && val < FIRST_CUSTOM)) {\n      return false;\n    }\n    return true;\n  }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/ThumbnailUtils.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Matrix;\nimport android.graphics.Rect;\nimport io.vov.vitamio.provider.MediaStore.Video;\n\n/**\n * ThumbnailUtils is a wrapper of MediaMetadataRetriever to retrive a thumbnail\n * of video file.\n * <p/>\n * <pre>\n * Bitmap thumb = ThumbnailUtils.createVideoThumbnail(this, videoPath, MINI_KIND);\n * </pre>\n */\npublic class ThumbnailUtils {\n  private static final int OPTIONS_NONE = 0x0;\n  private static final int OPTIONS_SCALE_UP = 0x1;\n  public static final int OPTIONS_RECYCLE_INPUT = 0x2;\n  public static final int TARGET_SIZE_MINI_THUMBNAIL_WIDTH = 426;\n  public static final int TARGET_SIZE_MINI_THUMBNAIL_HEIGHT = 320;\n  public static final int TARGET_SIZE_MICRO_THUMBNAIL_WIDTH = 212;\n  public static final int TARGET_SIZE_MICRO_THUMBNAIL_HEIGHT = 160;\n  \n\n  public static Bitmap createVideoThumbnail(Context ctx, String filePath, int kind) {\n    if (!Vitamio.isInitialized(ctx)) {\n      return null;\n    }\n    Bitmap bitmap = null;\n    MediaMetadataRetriever retriever = null;\n    try {\n      retriever = new MediaMetadataRetriever(ctx);\n      retriever.setDataSource(filePath);\n      bitmap = retriever.getFrameAtTime(-1);\n    } catch (Exception ex) {\n    } finally {\n      try {\n        retriever.release();\n      } catch (RuntimeException ex) {\n      }\n    }\n\n    if (bitmap != null) {\n      if (kind == Video.Thumbnails.MICRO_KIND)\n        bitmap = extractThumbnail(bitmap, TARGET_SIZE_MICRO_THUMBNAIL_WIDTH, TARGET_SIZE_MICRO_THUMBNAIL_HEIGHT, OPTIONS_RECYCLE_INPUT);\n      else if (kind == Video.Thumbnails.MINI_KIND)\n        bitmap = extractThumbnail(bitmap, TARGET_SIZE_MINI_THUMBNAIL_WIDTH, TARGET_SIZE_MINI_THUMBNAIL_HEIGHT, OPTIONS_RECYCLE_INPUT);\n    }\n    return bitmap;\n  }\n\n  public static Bitmap extractThumbnail(Bitmap source, int width, int height) {\n    return extractThumbnail(source, width, height, OPTIONS_NONE);\n  }\n\n  public static Bitmap extractThumbnail(Bitmap source, int width, int height, int options) {\n    if (source == null)\n      return null;\n\n    float scale;\n    if (source.getWidth() < source.getHeight())\n      scale = width / (float) source.getWidth();\n    else\n      scale = height / (float) source.getHeight();\n    Matrix matrix = new Matrix();\n    matrix.setScale(scale, scale);\n    Bitmap thumbnail = transform(matrix, source, width, height, OPTIONS_SCALE_UP | options);\n    return thumbnail;\n  }\n\n  private static Bitmap transform(Matrix scaler, Bitmap source, int targetWidth, int targetHeight, int options) {\n    boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0;\n    boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0;\n\n    int deltaX = source.getWidth() - targetWidth;\n    int deltaY = source.getHeight() - targetHeight;\n    if (!scaleUp && (deltaX < 0 || deltaY < 0)) {\n      Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);\n      Canvas c = new Canvas(b2);\n\n      int deltaXHalf = Math.max(0, deltaX / 2);\n      int deltaYHalf = Math.max(0, deltaY / 2);\n      Rect src = new Rect(deltaXHalf, deltaYHalf, deltaXHalf + Math.min(targetWidth, source.getWidth()), deltaYHalf + Math.min(targetHeight, source.getHeight()));\n      int dstX = (targetWidth - src.width()) / 2;\n      int dstY = (targetHeight - src.height()) / 2;\n      Rect dst = new Rect(dstX, dstY, targetWidth - dstX, targetHeight - dstY);\n      c.drawBitmap(source, src, dst, null);\n      if (recycle)\n        source.recycle();\n      return b2;\n    }\n\n    float bitmapWidthF = source.getWidth();\n    float bitmapHeightF = source.getHeight();\n    float bitmapAspect = bitmapWidthF / bitmapHeightF;\n    float viewAspect = (float) targetWidth / targetHeight;\n\n    float scale = bitmapAspect > viewAspect ? targetHeight / bitmapHeightF : targetWidth / bitmapWidthF;\n    if (scale < .9F || scale > 1F)\n      scaler.setScale(scale, scale);\n    else\n      scaler = null;\n\n    Bitmap b1;\n    if (scaler != null)\n      b1 = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), scaler, true);\n    else\n      b1 = source;\n\n    if (recycle && b1 != source)\n      source.recycle();\n\n    int dx1 = Math.max(0, b1.getWidth() - targetWidth);\n    int dy1 = Math.max(0, b1.getHeight() - targetHeight);\n\n    Bitmap b2 = Bitmap.createBitmap(b1, dx1 / 2, dy1 / 2, targetWidth, targetHeight);\n\n    if (b2 != b1 && (recycle || b1 != source))\n      b1.recycle();\n\n    return b2;\n  }\n\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/VIntent.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\n/**\n * Common intent actions used by Vitamio component.\n */\npublic class VIntent {\n  public static final String ACTION_MEDIA_SCANNER_SCAN_DIRECTORY = \"com.yixia.vitamio.action.MEDIA_SCANNER_SCAN_DIRECTORY\";\n  public static final String ACTION_MEDIA_SCANNER_SCAN_FILE = \"com.yixia.vitamio.action.MEDIA_SCANNER_SCAN_FILE\";\n  public static final String ACTION_MEDIA_SCANNER_STARTED = \"com.yixia.vitamio.action.MEDIA_SCANNER_STARTED\";\n  public static final String ACTION_MEDIA_SCANNER_FINISHED = \"com.yixia.vitamio.action.MEDIA_SCANNER_FINISHED\";\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/Vitamio.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\n\nimport android.content.Context;\n\nimport io.vov.vitamio.utils.CPU;\nimport io.vov.vitamio.utils.ContextUtils;\nimport io.vov.vitamio.utils.IOUtils;\nimport io.vov.vitamio.utils.Log;\n\n\n\n/**\n * Inspect this class before using any other Vitamio classes.\n * <p/>\n * Don't modify this class, or the full Vitamio library may be broken.\n */\npublic class Vitamio {\n  private static String vitamioPackage;\n  private static String vitamioLibraryPath;\n\n  /**\n   * Check if Vitamio is initialized at this device\n   *\n   * @param ctx Android Context\n   * @return true if the Vitamio has been initialized.\n   */\n  public static boolean isInitialized(Context ctx) {\n    vitamioPackage = ctx.getPackageName();\n    vitamioLibraryPath = ContextUtils.getDataDir(ctx) + \"lib/\";\n    return true;\n  }\n\n  public static String getVitamioPackage() {\n    return vitamioPackage;\n  }\n\n\n  public static final String getLibraryPath() {\n    return vitamioLibraryPath;\n  }\n\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/VitamioLicense.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio;\n\n/**\n * DON'T MODIFY THIS FILE IF YOU WANT TO USE VITAMIO\n */\npublic class VitamioLicense {\n  public static final String LICENSE = \"Copyright (c) YIXIA (http://yixia.com).\\nTHIS SOFTWARE (Vitamio) IS WORK OF YIXIA (http://yixia.com)\";\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/activity/InitActivity.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.activity;\n\nimport android.app.Activity;\nimport android.app.ProgressDialog;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.AsyncTask;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.view.WindowManager;\n\nimport io.vov.vitamio.Vitamio;\n\nimport java.lang.ref.WeakReference;\n\npublic class InitActivity extends Activity {\n  public static final String FROM_ME = \"fromVitamioInitActivity\";\n  private ProgressDialog mPD;\n  private UIHandler uiHandler;\n\n  protected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n    uiHandler = new UIHandler(this);\n\n    new AsyncTask<Object, Object, Boolean>() {\n      @Override\n      protected void onPreExecute() {\n        mPD = new ProgressDialog(InitActivity.this);\n        mPD.setCancelable(false);\n        mPD.setMessage(InitActivity.this.getString(getResources().getIdentifier(\"vitamio_init_decoders\", \"string\", getPackageName())));\n        mPD.show();\n      }\n\n\n      @Override\n      protected void onPostExecute(Boolean inited) {\n        if (inited) {\n          uiHandler.sendEmptyMessage(0);\n        }\n      }\n\n\n\t@Override\n\tprotected Boolean doInBackground(Object... arg0) {\n\t\t// TODO Auto-generated method stub\n\t\treturn null;\n\t}\n\n    }.execute();\n  }\n\n  private static class UIHandler extends Handler {\n    private WeakReference<Context> mContext;\n\n    public UIHandler(Context c) {\n      mContext = new WeakReference<Context>(c);\n    }\n\n    public void handleMessage(Message msg) {\n      InitActivity ctx = (InitActivity) mContext.get();\n      switch (msg.what) {\n        case 0:\n          ctx.mPD.dismiss();\n          Intent src = ctx.getIntent();\n          Intent i = new Intent();\n          i.setClassName(src.getStringExtra(\"package\"), src.getStringExtra(\"className\"));\n          i.setData(src.getData());\n          i.putExtras(src);\n          i.putExtra(FROM_ME, true);\n          ctx.startActivity(i);\n          ctx.finish();\n          break;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/provider/MediaStore.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.provider;\n\nimport android.content.ContentResolver;\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteException;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.net.Uri;\nimport android.os.ParcelFileDescriptor;\nimport android.provider.BaseColumns;\n\nimport io.vov.vitamio.utils.Log;\n\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\n\npublic final class MediaStore {\n  public static final String AUTHORITY = \"me.abitno.vplayer.mediaprovider\";\n  public static final String MEDIA_SCANNER_VOLUME = \"volume\";\n  public static final String CONTENT_AUTHORITY_SLASH = \"content://\" + AUTHORITY + \"/\";\n  public static final Uri CONTENT_URI = Uri.parse(CONTENT_AUTHORITY_SLASH);\n  private static final String BASE_SQL_FIELDS = MediaColumns._ID + \" INTEGER PRIMARY KEY,\" + //\n      MediaColumns.DATA + \" TEXT NOT NULL,\" + //\n      MediaColumns.DIRECTORY + \" TEXT NOT NULL,\" + //\n      MediaColumns.DIRECTORY_NAME + \" TEXT NOT NULL,\" + //\n      MediaColumns.SIZE + \" INTEGER,\" + //\n      MediaColumns.DISPLAY_NAME + \" TEXT,\" + //\n      MediaColumns.TITLE + \" TEXT,\" + //\n      MediaColumns.TITLE_KEY + \" TEXT,\" + //\n      MediaColumns.DATE_ADDED + \" INTEGER,\" + //\n      MediaColumns.DATE_MODIFIED + \" INTEGER,\" + //\n      MediaColumns.MIME_TYPE + \" TEXT,\" + //\n      MediaColumns.AVAILABLE_SIZE + \" INTEGER default 0,\" + //\n      MediaColumns.PLAY_STATUS + \" INTEGER ,\";\n\n  public static Uri getMediaScannerUri() {\n    return Uri.parse(CONTENT_AUTHORITY_SLASH + \"media_scanner\");\n  }\n\n  public static Uri getVolumeUri() {\n    return Uri.parse(CONTENT_AUTHORITY_SLASH + MEDIA_SCANNER_VOLUME);\n  }\n\n  public interface MediaColumns extends BaseColumns {\n    public static final String DATA = \"_data\";\n    public static final String DIRECTORY = \"_directory\";\n    public static final String DIRECTORY_NAME = \"_directory_name\";\n    public static final String SIZE = \"_size\";\n    public static final String DISPLAY_NAME = \"_display_name\";\n    public static final String TITLE = \"title\";\n    public static final String TITLE_KEY = \"title_key\";\n    public static final String DATE_ADDED = \"date_added\";\n    public static final String DATE_MODIFIED = \"date_modified\";\n    public static final String MIME_TYPE = \"mime_type\";\n    public static final String AVAILABLE_SIZE = \"available_size\";\n    public static final String PLAY_STATUS = \"play_status\";\n\n  }\n\n  public static final class Audio {\n    public interface AudioColumns extends MediaColumns {\n      public static final String DURATION = \"duration\";\n      public static final String BOOKMARK = \"bookmark\";\n      public static final String ARTIST = \"artist\";\n      public static final String COMPOSER = \"composer\";\n      public static final String ALBUM = \"album\";\n      public static final String TRACK = \"track\";\n      public static final String YEAR = \"year\";\n    }\n\n    public static final class Media implements AudioColumns {\n      public static final Uri CONTENT_URI = Uri.parse(CONTENT_AUTHORITY_SLASH + \"audios/media\");\n      public static final String CONTENT_TYPE = \"vnd.android.cursor.dir/audio\";\n    }\n  }\n\n  public static final class Video {\n\n    public interface VideoColumns extends MediaColumns {\n      public static final String DURATION = \"duration\";\n      public static final String ARTIST = \"artist\";\n      public static final String ALBUM = \"album\";\n      public static final String WIDTH = \"width\";\n      public static final String HEIGHT = \"height\";\n      public static final String DESCRIPTION = \"description\";\n      public static final String LANGUAGE = \"language\";\n      public static final String LATITUDE = \"latitude\";\n      public static final String LONGITUDE = \"longitude\";\n      public static final String DATE_TAKEN = \"datetaken\";\n      public static final String BOOKMARK = \"bookmark\";\n      public static final String MINI_THUMB_MAGIC = \"mini_thumb_magic\";\n      public static final String HIDDEN = \"hidden\";\n      public static final String SUBTRACK = \"sub_track\";\n      public static final String AUDIO_TRACK = \"audio_track\";\n    }\n\n    public static final class Media implements VideoColumns {\n      public static final Uri CONTENT_URI = Uri.parse(CONTENT_AUTHORITY_SLASH + \"videos/media\");\n      public static final String CONTENT_TYPE = \"vnd.android.cursor.dir/video\";\n      protected static final String TABLE_NAME = \"videos\";\n      protected static final String SQL_FIELDS = BASE_SQL_FIELDS + //\n          DURATION + \" INTEGER,\" + //\n          ARTIST + \" TEXT,\" + //\n          ALBUM + \" TEXT,\" + //\n          WIDTH + \" INTEGER,\" + //\n          HEIGHT + \" INTEGER,\" + //\n          DESCRIPTION + \" TEXT,\" + //\n          LANGUAGE + \" TEXT,\" + //\n          LATITUDE + \" DOUBLE,\" + //\n          LONGITUDE + \" DOUBLE,\" + //\n          DATE_TAKEN + \" INTEGER,\" + //\n          BOOKMARK + \" INTEGER,\" + //\n          MINI_THUMB_MAGIC + \" INTEGER,\" + //\n          HIDDEN + \" INTEGER default 0,\" + // 1 hidden , 0 visible\n          SUBTRACK + \" TEXT,\" + //\n          AUDIO_TRACK + \" INTEGER\";\n      protected static final String SQL_TRIGGER_VIDEO_CLEANUP = \"CREATE TRIGGER \" + //\n          \"IF NOT EXISTS video_cleanup AFTER DELETE ON \" + TABLE_NAME + \" \" + //\n          \"BEGIN \" + //\n          \"SELECT _DELETE_FILE(old._data);\" + //\n          \"SELECT _DELETE_FILE(old._data || '.ssi');\" + // The cache index\n          \"END\";\n      protected static final String SQL_TRIGGER_VIDEO_UPDATE = \"CREATE TRIGGER \" + //\n          \"IF NOT EXISTS video_update AFTER UPDATE ON \" + TABLE_NAME + \" \" + //\n          \"WHEN new._data <> old._data \" + //\n          \"BEGIN \" + //\n          \"SELECT _DELETE_FILE(old._data || '.ssi');\" + //\n          \"END\";\n    }\n\n    public static class Thumbnails implements BaseColumns {\n      public static final int MINI_KIND = 1;\n      public static final int MICRO_KIND = 3;\n      public static final Uri CONTENT_URI = Uri.parse(CONTENT_AUTHORITY_SLASH + \"videos/thumbnails\");\n      public static final String THUMBNAILS_DIRECTORY = \"Android/data/me.abitno.vplayer.t/thumbnails\";\n      public static final String DATA = \"_data\";\n      public static final String VIDEO_ID = \"video_id\";\n      public static final String KIND = \"kind\";\n      public static final String WIDTH = \"width\";\n      public static final String HEIGHT = \"height\";\n      protected static final String TABLE_NAME = \"videothumbnails\";\n      protected static final String SQL_FIELDS = _ID + \" INTEGER PRIMARY KEY,\" + //\n          DATA + \" TEXT,\" + //\n          VIDEO_ID + \" INTEGER,\" + //\n          KIND + \" INTEGER,\" + //\n          WIDTH + \" INTEGER,\" + //\n          HEIGHT + \" INTEGER\";\n      protected static final String SQL_INDEX_VIDEO_ID = \"CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);\";\n      protected static final String SQL_TRIGGER_VIDEO_THUMBNAILS_CLEANUP = \"CREATE TRIGGER \" + //\n          \"IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails \" + //\n          \"BEGIN \" + //\n          \"SELECT _DELETE_FILE(old._data);\" + //\n          \"END\";\n\n      public static void cancelThumbnailRequest(ContentResolver cr, long origId) {\n        InternalThumbnails.cancelThumbnailRequest(cr, origId, CONTENT_URI, InternalThumbnails.DEFAULT_GROUP_ID);\n      }\n\n      public static Bitmap getThumbnail(Context ctx, ContentResolver cr, long origId, int kind, BitmapFactory.Options options) {\n        return InternalThumbnails.getThumbnail(ctx, cr, origId, InternalThumbnails.DEFAULT_GROUP_ID, kind, options, CONTENT_URI);\n      }\n\n      public static Bitmap getThumbnail(Context ctx, ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options) {\n        return InternalThumbnails.getThumbnail(ctx, cr, origId, groupId, kind, options, CONTENT_URI);\n      }\n\n      public static String getThumbnailPath(Context ctx, ContentResolver cr, long origId) {\n        return InternalThumbnails.getThumbnailPath(ctx, cr, origId, CONTENT_URI);\n      }\n\n      public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {\n        InternalThumbnails.cancelThumbnailRequest(cr, origId, CONTENT_URI, groupId);\n      }\n    }\n  }\n\n  private static class InternalThumbnails implements BaseColumns {\n    static final int DEFAULT_GROUP_ID = 0;\n    private static final int MINI_KIND = 1;\n    private static final int MICRO_KIND = 3;\n    private static final String[] PROJECTION = new String[]{_ID, MediaColumns.DATA};\n    private static final Object sThumbBufLock = new Object();\n    private static byte[] sThumbBuf;\n\n    private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {\n      Bitmap bitmap = null;\n      Uri thumbUri = null;\n      try {\n        long thumbId = c.getLong(0);\n        thumbUri = ContentUris.withAppendedId(baseUri, thumbId);\n        ParcelFileDescriptor pfdInput = cr.openFileDescriptor(thumbUri, \"r\");\n        bitmap = BitmapFactory.decodeFileDescriptor(pfdInput.getFileDescriptor(), null, options);\n        pfdInput.close();\n      } catch (FileNotFoundException ex) {\n        Log.e(\"getMiniThumbFromFile\", ex);\n      } catch (IOException ex) {\n        Log.e(\"getMiniThumbFromFile\", ex);\n      } catch (OutOfMemoryError ex) {\n        Log.e(\"getMiniThumbFromFile\", ex);\n      }\n      return bitmap;\n    }\n\n    static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri, long groupId) {\n      Uri cancelUri = baseUri.buildUpon().appendQueryParameter(\"cancel\", \"1\").appendQueryParameter(\"orig_id\", String.valueOf(origId)).appendQueryParameter(\"group_id\", String.valueOf(groupId)).build();\n      Cursor c = null;\n      try {\n        c = cr.query(cancelUri, PROJECTION, null, null, null);\n      } finally {\n        if (c != null)\n          c.close();\n      }\n    }\n\n    static String getThumbnailPath(Context ctx, ContentResolver cr, long origId, Uri baseUri) {\n      String column = \"video_id=\";\n      String path = \"\";\n      Cursor c = null;\n      try {\n        c = cr.query(baseUri, PROJECTION, column + origId, null, null);\n        if (c != null && c.moveToFirst()) {\n          path = c.getString(c.getColumnIndex(MediaColumns.DATA));\n        }\n      } finally {\n        if (c != null)\n          c.close();\n      }\n      return path;\n    }\n\n    static Bitmap getThumbnail(Context ctx, ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options, Uri baseUri) {\n      Bitmap bitmap = null;\n      MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);\n      long magic = thumbFile.getMagic(origId);\n      if (magic != 0) {\n        if (kind == MICRO_KIND) {\n          synchronized (sThumbBufLock) {\n            if (sThumbBuf == null)\n              sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];\n            if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {\n              bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);\n              if (bitmap == null)\n                Log.d(\"couldn't decode byte array.\");\n            }\n          }\n          return bitmap;\n        } else if (kind == MINI_KIND) {\n          String column = \"video_id=\";\n          Cursor c = null;\n          try {\n            c = cr.query(baseUri, PROJECTION, column + origId, null, null);\n            if (c != null && c.moveToFirst()) {\n              bitmap = getMiniThumbFromFile(c, baseUri, cr, options);\n              if (bitmap != null)\n                return bitmap;\n            }\n          } finally {\n            if (c != null)\n              c.close();\n          }\n        }\n      }\n\n      Cursor c = null;\n      try {\n        Uri blockingUri = baseUri.buildUpon().appendQueryParameter(\"blocking\", \"1\").appendQueryParameter(\"orig_id\", String.valueOf(origId)).appendQueryParameter(\"group_id\", String.valueOf(groupId)).build();\n        c = cr.query(blockingUri, PROJECTION, null, null, null);\n        if (c == null)\n          return null;\n\n        if (kind == MICRO_KIND) {\n          synchronized (sThumbBufLock) {\n            if (sThumbBuf == null)\n              sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];\n            if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {\n              bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);\n              if (bitmap == null)\n                Log.d(\"couldn't decode byte array.\");\n            }\n          }\n        } else if (kind == MINI_KIND) {\n          if (c.moveToFirst())\n            bitmap = getMiniThumbFromFile(c, baseUri, cr, options);\n        } else {\n          throw new IllegalArgumentException(\"Unsupported kind: \" + kind);\n        }\n      } catch (SQLiteException ex) {\n        Log.e(\"getThumbnail\", ex);\n      } finally {\n        if (c != null)\n          c.close();\n      }\n      return bitmap;\n    }\n  }\n\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/provider/MiniThumbFile.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.provider;\n\nimport android.net.Uri;\nimport android.os.Environment;\n\nimport io.vov.vitamio.provider.MediaStore.Video;\nimport io.vov.vitamio.utils.Log;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.RandomAccessFile;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.FileLock;\nimport java.util.Hashtable;\n\npublic class MiniThumbFile {\n  protected static final int BYTES_PER_MINTHUMB = 10000;\n  private static final int MINI_THUMB_DATA_FILE_VERSION = 7;\n  private static final int HEADER_SIZE = 1 + 8 + 4;\n  private static Hashtable<String, MiniThumbFile> sThumbFiles = new Hashtable<String, MiniThumbFile>();\n  private Uri mUri;\n  private RandomAccessFile mMiniThumbFile;\n  private FileChannel mChannel;\n  private ByteBuffer mBuffer;\n\n  public MiniThumbFile(Uri uri) {\n    mUri = uri;\n    mBuffer = ByteBuffer.allocateDirect(BYTES_PER_MINTHUMB);\n  }\n\n  protected static synchronized void reset() {\n    for (MiniThumbFile file : sThumbFiles.values())\n      file.deactivate();\n    sThumbFiles.clear();\n  }\n\n  protected static synchronized MiniThumbFile instance(Uri uri) {\n    String type = uri.getPathSegments().get(0);\n    MiniThumbFile file = sThumbFiles.get(type);\n    if (file == null) {\n      file = new MiniThumbFile(Uri.parse(MediaStore.CONTENT_AUTHORITY_SLASH + type + \"/media\"));\n      sThumbFiles.put(type, file);\n    }\n\n    return file;\n  }\n\n  private String randomAccessFilePath(int version) {\n    String directoryName = Environment.getExternalStorageDirectory().toString() + \"/\" + Video.Thumbnails.THUMBNAILS_DIRECTORY;\n    return directoryName + \"/.thumbdata\" + version + \"-\" + mUri.hashCode();\n  }\n\n  private void removeOldFile() {\n    String oldPath = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION - 1);\n    File oldFile = new File(oldPath);\n    if (oldFile.exists()) {\n      try {\n        oldFile.delete();\n      } catch (SecurityException ex) {\n      }\n    }\n  }\n\n  private RandomAccessFile miniThumbDataFile() {\n    if (mMiniThumbFile == null) {\n      removeOldFile();\n      String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION);\n      File directory = new File(path).getParentFile();\n      if (!directory.isDirectory()) {\n        if (!directory.mkdirs())\n          Log.e(\"Unable to create .thumbnails directory %s\", directory.toString());\n      }\n      File f = new File(path);\n      try {\n        mMiniThumbFile = new RandomAccessFile(f, \"rw\");\n      } catch (IOException ex) {\n        try {\n          mMiniThumbFile = new RandomAccessFile(f, \"r\");\n        } catch (IOException ex2) {\n        }\n      }\n\n      if (mMiniThumbFile != null)\n        mChannel = mMiniThumbFile.getChannel();\n    }\n    return mMiniThumbFile;\n  }\n\n  protected synchronized void deactivate() {\n    if (mMiniThumbFile != null) {\n      try {\n        mMiniThumbFile.close();\n        mMiniThumbFile = null;\n      } catch (IOException ex) {\n      }\n    }\n  }\n\n  protected synchronized long getMagic(long id) {\n    RandomAccessFile r = miniThumbDataFile();\n    if (r != null) {\n      long pos = id * BYTES_PER_MINTHUMB;\n      FileLock lock = null;\n      try {\n        mBuffer.clear();\n        mBuffer.limit(1 + 8);\n\n        lock = mChannel.lock(pos, 1 + 8, true);\n        if (mChannel.read(mBuffer, pos) == 9) {\n          mBuffer.position(0);\n          if (mBuffer.get() == 1)\n            return mBuffer.getLong();\n        }\n      } catch (IOException ex) {\n        Log.e(\"Got exception checking file magic: \", ex);\n      } catch (RuntimeException ex) {\n        Log.e(\"Got exception when reading magic, id = %d, disk full or mount read-only? %s\", id, ex.getClass().toString());\n      } finally {\n        try {\n          if (lock != null)\n            lock.release();\n        } catch (IOException ex) {\n        }\n      }\n    }\n    return 0;\n  }\n\n  protected synchronized void saveMiniThumbToFile(byte[] data, long id, long magic) throws IOException {\n    RandomAccessFile r = miniThumbDataFile();\n    if (r == null)\n      return;\n\n    long pos = id * BYTES_PER_MINTHUMB;\n    FileLock lock = null;\n    try {\n      if (data != null) {\n        if (data.length > BYTES_PER_MINTHUMB - HEADER_SIZE)\n          return;\n\n        mBuffer.clear();\n        mBuffer.put((byte) 1);\n        mBuffer.putLong(magic);\n        mBuffer.putInt(data.length);\n        mBuffer.put(data);\n        mBuffer.flip();\n\n        lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, false);\n        mChannel.write(mBuffer, pos);\n      }\n    } catch (IOException ex) {\n      Log.e(\"couldn't save mini thumbnail data for %d; %s\", id, ex.getMessage());\n      throw ex;\n    } catch (RuntimeException ex) {\n      Log.e(\"couldn't save mini thumbnail data for %d, disk full or mount read-only? %s\", id, ex.getClass().toString());\n    } finally {\n      try {\n        if (lock != null)\n          lock.release();\n      } catch (IOException ex) {\n      }\n    }\n  }\n\n  protected synchronized byte[] getMiniThumbFromFile(long id, byte[] data) {\n    RandomAccessFile r = miniThumbDataFile();\n    if (r == null)\n      return null;\n\n    long pos = id * BYTES_PER_MINTHUMB;\n    FileLock lock = null;\n    try {\n      mBuffer.clear();\n      lock = mChannel.lock(pos, BYTES_PER_MINTHUMB, true);\n      int size = mChannel.read(mBuffer, pos);\n      if (size > 1 + 8 + 4) {\n        mBuffer.position(9);\n        int length = mBuffer.getInt();\n\n        if (size >= 1 + 8 + 4 + length && data.length >= length) {\n          mBuffer.get(data, 0, length);\n          return data;\n        }\n      }\n    } catch (IOException ex) {\n      Log.e(\"got exception when reading thumbnail id = %d, exception: %s\", id, ex.getMessage());\n    } catch (RuntimeException ex) {\n      Log.e(\"Got exception when reading thumbnail, id = %d, disk full or mount read-only? %s\", id, ex.getClass().toString());\n    } finally {\n      try {\n        if (lock != null)\n          lock.release();\n      } catch (IOException ex) {\n      }\n    }\n    return null;\n  }\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/Base64.java",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\nimport java.io.UnsupportedEncodingException;\n\n/**\n * Utilities for encoding and decoding the Base64 representation of\n * binary data.  See RFCs <a\n * href=\"http://www.ietf.org/rfc/rfc2045.txt\">2045</a> and <a\n * href=\"http://www.ietf.org/rfc/rfc3548.txt\">3548</a>.\n */\npublic class Base64 {\n    /**\n     * Default values for encoder/decoder flags.\n     */\n    public static final int DEFAULT = 0;\n\n    /**\n     * Encoder flag bit to omit the padding '=' characters at the end\n     * of the output (if any).\n     */\n    public static final int NO_PADDING = 1;\n\n    /**\n     * Encoder flag bit to omit all line terminators (i.e., the output\n     * will be on one long line).\n     */\n    public static final int NO_WRAP = 2;\n\n    /**\n     * Encoder flag bit to indicate lines should be terminated with a\n     * CRLF pair instead of just an LF.  Has no effect if {@code\n     * NO_WRAP} is specified as well.\n     */\n    public static final int CRLF = 4;\n\n    /**\n     * Encoder/decoder flag bit to indicate using the \"URL and\n     * filename safe\" variant of Base64 (see RFC 3548 section 4) where\n     * {@code -} and {@code _} are used in place of {@code +} and\n     * {@code /}.\n     */\n    public static final int URL_SAFE = 8;\n\n    /**\n     * Flag to pass to {@link Base64OutputStream} to indicate that it\n     * should not close the output stream it is wrapping when it\n     * itself is closed.\n     */\n    public static final int NO_CLOSE = 16;\n\n    //  --------------------------------------------------------\n    //  shared code\n    //  --------------------------------------------------------\n\n    /* package */ static abstract class Coder {\n        public byte[] output;\n        public int op;\n\n        /**\n         * Encode/decode another block of input data.  this.output is\n         * provided by the caller, and must be big enough to hold all\n         * the coded data.  On exit, this.opwill be set to the length\n         * of the coded data.\n         *\n         * @param finish true if this is the final call to process for\n         *        this object.  Will finalize the coder state and\n         *        include any final bytes in the output.\n         *\n         * @return true if the input so far is good; false if some\n         *         error has been detected in the input stream..\n         */\n        public abstract boolean process(byte[] input, int offset, int len, boolean finish);\n\n        /**\n         * @return the maximum number of bytes a call to process()\n         * could produce for the given number of input bytes.  This may\n         * be an overestimate.\n         */\n        public abstract int maxOutputSize(int len);\n    }\n\n    //  --------------------------------------------------------\n    //  decoding\n    //  --------------------------------------------------------\n\n    /**\n     * Decode the Base64-encoded data in input and return the data in\n     * a new byte array.\n     *\n     * <p>The padding '=' characters at the end are considered optional, but\n     * if any are present, there must be the correct number of them.\n     *\n     * @param str    the input String to decode, which is converted to\n     *               bytes using the default charset\n     * @param flags  controls certain features of the decoded output.\n     *               Pass {@code DEFAULT} to decode standard Base64.\n     *\n     * @throws IllegalArgumentException if the input contains\n     * incorrect padding\n     */\n    public static byte[] decode(String str, int flags) {\n        return decode(str.getBytes(), flags);\n    }\n\n    /**\n     * Decode the Base64-encoded data in input and return the data in\n     * a new byte array.\n     *\n     * <p>The padding '=' characters at the end are considered optional, but\n     * if any are present, there must be the correct number of them.\n     *\n     * @param input the input array to decode\n     * @param flags  controls certain features of the decoded output.\n     *               Pass {@code DEFAULT} to decode standard Base64.\n     *\n     * @throws IllegalArgumentException if the input contains\n     * incorrect padding\n     */\n    public static byte[] decode(byte[] input, int flags) {\n        return decode(input, 0, input.length, flags);\n    }\n\n    /**\n     * Decode the Base64-encoded data in input and return the data in\n     * a new byte array.\n     *\n     * <p>The padding '=' characters at the end are considered optional, but\n     * if any are present, there must be the correct number of them.\n     *\n     * @param input  the data to decode\n     * @param offset the position within the input array at which to start\n     * @param len    the number of bytes of input to decode\n     * @param flags  controls certain features of the decoded output.\n     *               Pass {@code DEFAULT} to decode standard Base64.\n     *\n     * @throws IllegalArgumentException if the input contains\n     * incorrect padding\n     */\n    public static byte[] decode(byte[] input, int offset, int len, int flags) {\n        // Allocate space for the most data the input could represent.\n        // (It could contain less if it contains whitespace, etc.)\n        Decoder decoder = new Decoder(flags, new byte[len*3/4]);\n\n        if (!decoder.process(input, offset, len, true)) {\n            throw new IllegalArgumentException(\"bad base-64\");\n        }\n\n        // Maybe we got lucky and allocated exactly enough output space.\n        if (decoder.op == decoder.output.length) {\n            return decoder.output;\n        }\n\n        // Need to shorten the array, so allocate a new one of the\n        // right size and copy.\n        byte[] temp = new byte[decoder.op];\n        System.arraycopy(decoder.output, 0, temp, 0, decoder.op);\n        return temp;\n    }\n\n    /* package */ static class Decoder extends Coder {\n        /**\n         * Lookup table for turning bytes into their position in the\n         * Base64 alphabet.\n         */\n        private static final int DECODE[] = {\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,\n            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,\n            -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,\n            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,\n            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\n            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        };\n\n        /**\n         * Decode lookup table for the \"web safe\" variant (RFC 3548\n         * sec. 4) where - and _ replace + and /.\n         */\n        private static final int DECODE_WEBSAFE[] = {\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,\n            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,\n            -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,\n            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,\n            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,\n            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n        };\n\n        /** Non-data values in the DECODE arrays. */\n        private static final int SKIP = -1;\n        private static final int EQUALS = -2;\n\n        /**\n         * States 0-3 are reading through the next input tuple.\n         * State 4 is having read one '=' and expecting exactly\n         * one more.\n         * State 5 is expecting no more data or padding characters\n         * in the input.\n         * State 6 is the error state; an error has been detected\n         * in the input and no future input can \"fix\" it.\n         */\n        private int state;   // state number (0 to 6)\n        private int value;\n\n        final private int[] alphabet;\n\n        public Decoder(int flags, byte[] output) {\n            this.output = output;\n\n            alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE;\n            state = 0;\n            value = 0;\n        }\n\n        /**\n         * @return an overestimate for the number of bytes {@code\n         * len} bytes could decode to.\n         */\n        public int maxOutputSize(int len) {\n            return len * 3/4 + 10;\n        }\n\n        /**\n         * Decode another block of input data.\n         *\n         * @return true if the state machine is still healthy.  false if\n         *         bad base-64 data has been detected in the input stream.\n         */\n        public boolean process(byte[] input, int offset, int len, boolean finish) {\n            if (this.state == 6) return false;\n\n            int p = offset;\n            len += offset;\n\n            // Using local variables makes the decoder about 12%\n            // faster than if we manipulate the member variables in\n            // the loop.  (Even alphabet makes a measurable\n            // difference, which is somewhat surprising to me since\n            // the member variable is final.)\n            int state = this.state;\n            int value = this.value;\n            int op = 0;\n            final byte[] output = this.output;\n            final int[] alphabet = this.alphabet;\n\n            while (p < len) {\n                // Try the fast path:  we're starting a new tuple and the\n                // next four bytes of the input stream are all data\n                // bytes.  This corresponds to going through states\n                // 0-1-2-3-0.  We expect to use this method for most of\n                // the data.\n                //\n                // If any of the next four bytes of input are non-data\n                // (whitespace, etc.), value will end up negative.  (All\n                // the non-data values in decode are small negative\n                // numbers, so shifting any of them up and or'ing them\n                // together will result in a value with its top bit set.)\n                //\n                // You can remove this whole block and the output should\n                // be the same, just slower.\n                if (state == 0) {\n                    while (p+4 <= len &&\n                           (value = ((alphabet[input[p] & 0xff] << 18) |\n                                     (alphabet[input[p+1] & 0xff] << 12) |\n                                     (alphabet[input[p+2] & 0xff] << 6) |\n                                     (alphabet[input[p+3] & 0xff]))) >= 0) {\n                        output[op+2] = (byte) value;\n                        output[op+1] = (byte) (value >> 8);\n                        output[op] = (byte) (value >> 16);\n                        op += 3;\n                        p += 4;\n                    }\n                    if (p >= len) break;\n                }\n\n                // The fast path isn't available -- either we've read a\n                // partial tuple, or the next four input bytes aren't all\n                // data, or whatever.  Fall back to the slower state\n                // machine implementation.\n\n                int d = alphabet[input[p++] & 0xff];\n\n                switch (state) {\n                case 0:\n                    if (d >= 0) {\n                        value = d;\n                        ++state;\n                    } else if (d != SKIP) {\n                        this.state = 6;\n                        return false;\n                    }\n                    break;\n\n                case 1:\n                    if (d >= 0) {\n                        value = (value << 6) | d;\n                        ++state;\n                    } else if (d != SKIP) {\n                        this.state = 6;\n                        return false;\n                    }\n                    break;\n\n                case 2:\n                    if (d >= 0) {\n                        value = (value << 6) | d;\n                        ++state;\n                    } else if (d == EQUALS) {\n                        // Emit the last (partial) output tuple;\n                        // expect exactly one more padding character.\n                        output[op++] = (byte) (value >> 4);\n                        state = 4;\n                    } else if (d != SKIP) {\n                        this.state = 6;\n                        return false;\n                    }\n                    break;\n\n                case 3:\n                    if (d >= 0) {\n                        // Emit the output triple and return to state 0.\n                        value = (value << 6) | d;\n                        output[op+2] = (byte) value;\n                        output[op+1] = (byte) (value >> 8);\n                        output[op] = (byte) (value >> 16);\n                        op += 3;\n                        state = 0;\n                    } else if (d == EQUALS) {\n                        // Emit the last (partial) output tuple;\n                        // expect no further data or padding characters.\n                        output[op+1] = (byte) (value >> 2);\n                        output[op] = (byte) (value >> 10);\n                        op += 2;\n                        state = 5;\n                    } else if (d != SKIP) {\n                        this.state = 6;\n                        return false;\n                    }\n                    break;\n\n                case 4:\n                    if (d == EQUALS) {\n                        ++state;\n                    } else if (d != SKIP) {\n                        this.state = 6;\n                        return false;\n                    }\n                    break;\n\n                case 5:\n                    if (d != SKIP) {\n                        this.state = 6;\n                        return false;\n                    }\n                    break;\n                }\n            }\n\n            if (!finish) {\n                // We're out of input, but a future call could provide\n                // more.\n                this.state = state;\n                this.value = value;\n                this.op = op;\n                return true;\n            }\n\n            // Done reading input.  Now figure out where we are left in\n            // the state machine and finish up.\n\n            switch (state) {\n            case 0:\n                // Output length is a multiple of three.  Fine.\n                break;\n            case 1:\n                // Read one extra input byte, which isn't enough to\n                // make another output byte.  Illegal.\n                this.state = 6;\n                return false;\n            case 2:\n                // Read two extra input bytes, enough to emit 1 more\n                // output byte.  Fine.\n                output[op++] = (byte) (value >> 4);\n                break;\n            case 3:\n                // Read three extra input bytes, enough to emit 2 more\n                // output bytes.  Fine.\n                output[op++] = (byte) (value >> 10);\n                output[op++] = (byte) (value >> 2);\n                break;\n            case 4:\n                // Read one padding '=' when we expected 2.  Illegal.\n                this.state = 6;\n                return false;\n            case 5:\n                // Read all the padding '='s we expected and no more.\n                // Fine.\n                break;\n            }\n\n            this.state = state;\n            this.op = op;\n            return true;\n        }\n    }\n\n    //  --------------------------------------------------------\n    //  encoding\n    //  --------------------------------------------------------\n\n    /**\n     * Base64-encode the given data and return a newly allocated\n     * String with the result.\n     *\n     * @param input  the data to encode\n     * @param flags  controls certain features of the encoded output.\n     *               Passing {@code DEFAULT} results in output that\n     *               adheres to RFC 2045.\n     */\n    public static String encodeToString(byte[] input, int flags) {\n        try {\n            return new String(encode(input, flags), \"US-ASCII\");\n        } catch (UnsupportedEncodingException e) {\n            // US-ASCII is guaranteed to be available.\n            throw new AssertionError(e);\n        }\n    }\n\n    /**\n     * Base64-encode the given data and return a newly allocated\n     * String with the result.\n     *\n     * @param input  the data to encode\n     * @param offset the position within the input array at which to\n     *               start\n     * @param len    the number of bytes of input to encode\n     * @param flags  controls certain features of the encoded output.\n     *               Passing {@code DEFAULT} results in output that\n     *               adheres to RFC 2045.\n     */\n    public static String encodeToString(byte[] input, int offset, int len, int flags) {\n        try {\n            return new String(encode(input, offset, len, flags), \"US-ASCII\");\n        } catch (UnsupportedEncodingException e) {\n            // US-ASCII is guaranteed to be available.\n            throw new AssertionError(e);\n        }\n    }\n\n    /**\n     * Base64-encode the given data and return a newly allocated\n     * byte[] with the result.\n     *\n     * @param input  the data to encode\n     * @param flags  controls certain features of the encoded output.\n     *               Passing {@code DEFAULT} results in output that\n     *               adheres to RFC 2045.\n     */\n    public static byte[] encode(byte[] input, int flags) {\n        return encode(input, 0, input.length, flags);\n    }\n\n    /**\n     * Base64-encode the given data and return a newly allocated\n     * byte[] with the result.\n     *\n     * @param input  the data to encode\n     * @param offset the position within the input array at which to\n     *               start\n     * @param len    the number of bytes of input to encode\n     * @param flags  controls certain features of the encoded output.\n     *               Passing {@code DEFAULT} results in output that\n     *               adheres to RFC 2045.\n     */\n    public static byte[] encode(byte[] input, int offset, int len, int flags) {\n        Encoder encoder = new Encoder(flags, null);\n\n        // Compute the exact length of the array we will produce.\n        int output_len = len / 3 * 4;\n\n        // Account for the tail of the data and the padding bytes, if any.\n        if (encoder.do_padding) {\n            if (len % 3 > 0) {\n                output_len += 4;\n            }\n        } else {\n            switch (len % 3) {\n                case 0: break;\n                case 1: output_len += 2; break;\n                case 2: output_len += 3; break;\n            }\n        }\n\n        // Account for the newlines, if any.\n        if (encoder.do_newline && len > 0) {\n            output_len += (((len-1) / (3 * Encoder.LINE_GROUPS)) + 1) *\n                (encoder.do_cr ? 2 : 1);\n        }\n\n        encoder.output = new byte[output_len];\n        encoder.process(input, offset, len, true);\n\n        assert encoder.op == output_len;\n\n        return encoder.output;\n    }\n\n    /* package */ static class Encoder extends Coder {\n        /**\n         * Emit a new line every this many output tuples.  Corresponds to\n         * a 76-character line length (the maximum allowable according to\n         * <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045</a>).\n         */\n        public static final int LINE_GROUPS = 19;\n\n        /**\n         * Lookup table for turning Base64 alphabet positions (6 bits)\n         * into output bytes.\n         */\n        private static final byte ENCODE[] = {\n            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',\n            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',\n            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',\n            'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',\n        };\n\n        /**\n         * Lookup table for turning Base64 alphabet positions (6 bits)\n         * into output bytes.\n         */\n        private static final byte ENCODE_WEBSAFE[] = {\n            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',\n            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',\n            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',\n            'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_',\n        };\n\n        final private byte[] tail;\n        /* package */ int tailLen;\n        private int count;\n\n        final public boolean do_padding;\n        final public boolean do_newline;\n        final public boolean do_cr;\n        final private byte[] alphabet;\n\n        public Encoder(int flags, byte[] output) {\n            this.output = output;\n\n            do_padding = (flags & NO_PADDING) == 0;\n            do_newline = (flags & NO_WRAP) == 0;\n            do_cr = (flags & CRLF) != 0;\n            alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;\n\n            tail = new byte[2];\n            tailLen = 0;\n\n            count = do_newline ? LINE_GROUPS : -1;\n        }\n\n        /**\n         * @return an overestimate for the number of bytes {@code\n         * len} bytes could encode to.\n         */\n        public int maxOutputSize(int len) {\n            return len * 8/5 + 10;\n        }\n\n        public boolean process(byte[] input, int offset, int len, boolean finish) {\n            // Using local variables makes the encoder about 9% faster.\n            final byte[] alphabet = this.alphabet;\n            final byte[] output = this.output;\n            int op = 0;\n            int count = this.count;\n\n            int p = offset;\n            len += offset;\n            int v = -1;\n\n            // First we need to concatenate the tail of the previous call\n            // with any input bytes available now and see if we can empty\n            // the tail.\n\n            switch (tailLen) {\n                case 0:\n                    // There was no tail.\n                    break;\n\n                case 1:\n                    if (p+2 <= len) {\n                        // A 1-byte tail with at least 2 bytes of\n                        // input available now.\n                        v = ((tail[0] & 0xff) << 16) |\n                            ((input[p++] & 0xff) << 8) |\n                            (input[p++] & 0xff);\n                        tailLen = 0;\n                    };\n                    break;\n\n                case 2:\n                    if (p+1 <= len) {\n                        // A 2-byte tail with at least 1 byte of input.\n                        v = ((tail[0] & 0xff) << 16) |\n                            ((tail[1] & 0xff) << 8) |\n                            (input[p++] & 0xff);\n                        tailLen = 0;\n                    }\n                    break;\n            }\n\n            if (v != -1) {\n                output[op++] = alphabet[(v >> 18) & 0x3f];\n                output[op++] = alphabet[(v >> 12) & 0x3f];\n                output[op++] = alphabet[(v >> 6) & 0x3f];\n                output[op++] = alphabet[v & 0x3f];\n                if (--count == 0) {\n                    if (do_cr) output[op++] = '\\r';\n                    output[op++] = '\\n';\n                    count = LINE_GROUPS;\n                }\n            }\n\n            // At this point either there is no tail, or there are fewer\n            // than 3 bytes of input available.\n\n            // The main loop, turning 3 input bytes into 4 output bytes on\n            // each iteration.\n            while (p+3 <= len) {\n                v = ((input[p] & 0xff) << 16) |\n                    ((input[p+1] & 0xff) << 8) |\n                    (input[p+2] & 0xff);\n                output[op] = alphabet[(v >> 18) & 0x3f];\n                output[op+1] = alphabet[(v >> 12) & 0x3f];\n                output[op+2] = alphabet[(v >> 6) & 0x3f];\n                output[op+3] = alphabet[v & 0x3f];\n                p += 3;\n                op += 4;\n                if (--count == 0) {\n                    if (do_cr) output[op++] = '\\r';\n                    output[op++] = '\\n';\n                    count = LINE_GROUPS;\n                }\n            }\n\n            if (finish) {\n                // Finish up the tail of the input.  Note that we need to\n                // consume any bytes in tail before any bytes\n                // remaining in input; there should be at most two bytes\n                // total.\n\n                if (p-tailLen == len-1) {\n                    int t = 0;\n                    v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4;\n                    tailLen -= t;\n                    output[op++] = alphabet[(v >> 6) & 0x3f];\n                    output[op++] = alphabet[v & 0x3f];\n                    if (do_padding) {\n                        output[op++] = '=';\n                        output[op++] = '=';\n                    }\n                    if (do_newline) {\n                        if (do_cr) output[op++] = '\\r';\n                        output[op++] = '\\n';\n                    }\n                } else if (p-tailLen == len-2) {\n                    int t = 0;\n                    v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) |\n                        (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2);\n                    tailLen -= t;\n                    output[op++] = alphabet[(v >> 12) & 0x3f];\n                    output[op++] = alphabet[(v >> 6) & 0x3f];\n                    output[op++] = alphabet[v & 0x3f];\n                    if (do_padding) {\n                        output[op++] = '=';\n                    }\n                    if (do_newline) {\n                        if (do_cr) output[op++] = '\\r';\n                        output[op++] = '\\n';\n                    }\n                } else if (do_newline && op > 0 && count != LINE_GROUPS) {\n                    if (do_cr) output[op++] = '\\r';\n                    output[op++] = '\\n';\n                }\n\n                assert tailLen == 0;\n                assert p == len;\n            } else {\n                // Save the leftovers in tail to be consumed on the next\n                // call to encodeInternal.\n\n                if (p == len-1) {\n                    tail[tailLen++] = input[p];\n                } else if (p == len-2) {\n                    tail[tailLen++] = input[p];\n                    tail[tailLen++] = input[p+1];\n                }\n            }\n\n            this.op = op;\n            this.count = count;\n\n            return true;\n        }\n    }\n\n    private Base64() { }   // don't instantiate\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/CPU.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport android.os.Build;\nimport android.text.TextUtils;\n\npublic class CPU {\n\tprivate static final Map<String, String> cpuinfo = new HashMap<String, String>();\n\tprivate static int cachedFeature = -1;\n\tprivate static String cachedFeatureString = null;\n\tpublic static final int FEATURE_ARM_V5TE =  1 << 0;\n\tpublic static final int FEATURE_ARM_V6   =  1 << 1;\n\tpublic static final int FEATURE_ARM_VFP  =  1 << 2;\n\tpublic static final int FEATURE_ARM_V7A  =  1 << 3;\n\tpublic static final int FEATURE_ARM_VFPV3 = 1 << 4;\n\tpublic static final int FEATURE_ARM_NEON =  1 << 5;\n\tpublic static final int FEATURE_X86      =  1 << 6;\n\tpublic static final int FEATURE_MIPS     =  1 << 7;\n\t\n\tpublic static String getFeatureString() {\n\t\tgetFeature();\n\t\treturn cachedFeatureString;\n\t}\n\n\tpublic static int getFeature() {\n\t\tif (cachedFeature > 0)\n\t\t\treturn getCachedFeature();\n\n\t\tcachedFeature = FEATURE_ARM_V5TE;\n\n\t\tif (cpuinfo.isEmpty()) {\n\t\t\tBufferedReader bis = null;\n\t\t\ttry {\n\t\t\t\tbis = new BufferedReader(new FileReader(new File(\"/proc/cpuinfo\")));\n\t\t\t\tString line;\n\t\t\t\tString[] pairs;\n\t\t\t\twhile ((line = bis.readLine()) != null) {\n\t\t\t\t\tif (!line.trim().equals(\"\")) {\n\t\t\t\t\t\tpairs = line.split(\":\");\n\t\t\t\t\t\tif (pairs.length > 1)\n\t\t\t\t\t\t\tcpuinfo.put(pairs[0].trim(), pairs[1].trim());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.e(\"getCPUFeature\", e);\n\t\t\t} finally {\n\t\t\t\ttry {\n\t\t\t\t\tif (bis != null)\n\t\t\t\t\t\tbis.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tLog.e(\"getCPUFeature\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!cpuinfo.isEmpty()) {\n\t\t\tfor (String key : cpuinfo.keySet())\n\t\t\t\tLog.d(\"%s:%s\", key, cpuinfo.get(key));\n\n\t\t\tboolean hasARMv6 = false;\n\t\t\tboolean hasARMv7 = false;\n\n\t\t\tString val = cpuinfo.get(\"CPU architecture\");\n\t\t\tif (!TextUtils.isEmpty(val)) {\n\t\t\t\ttry {\n\t\t\t\t\tint i = StringUtils.convertToInt(val);\n\t\t\t\t\tLog.d(\"CPU architecture: %s\", i);\n\t\t\t\t\tif (i >= 7) {\n\t\t\t\t\t\thasARMv6 = true;\n\t\t\t\t\t\thasARMv7 = true;\n\t\t\t\t\t} else if (i >= 6) {\n\t\t\t\t\t\thasARMv6 = true;\n\t\t\t\t\t\thasARMv7 = false;\n\t\t\t\t\t}\n\t\t\t\t} catch (NumberFormatException ex) {\n\t\t\t\t\tLog.e(\"getCPUFeature\", ex);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tval = cpuinfo.get(\"Processor\");\n\t\t\t\tif (TextUtils.isEmpty(val)) {\n\t\t\t\t    val = cpuinfo.get(\"model name\");\n\t\t\t\t}\n\t\t\t\tif (val != null && (val.contains(\"(v7l)\") || val.contains(\"ARMv7\"))) {\n\t\t\t\t\thasARMv6 = true;\n\t\t\t\t\thasARMv7 = true;\n\t\t\t\t}\n\t\t\t\tif (val != null && (val.contains(\"(v6l)\") || val.contains(\"ARMv6\"))) {\n\t\t\t\t\thasARMv6 = true;\n\t\t\t\t\thasARMv7 = false;\n\t\t\t\t}\n\n\t\t\t\tif (hasARMv6)\n\t\t\t\t\tcachedFeature |= FEATURE_ARM_V6;\n\t\t\t\tif (hasARMv7)\n\t\t\t\t\tcachedFeature |= FEATURE_ARM_V7A;\n\n\t\t\t\tval = cpuinfo.get(\"Features\");\n\t\t\t\tif (val != null) {\n\t\t\t\t\tif (val.contains(\"neon\"))\n\t\t\t\t\t\tcachedFeature |= FEATURE_ARM_VFP | FEATURE_ARM_VFPV3 | FEATURE_ARM_NEON;\n\t\t\t\t\telse if (val.contains(\"vfpv3\"))\n\t\t\t\t\t\tcachedFeature |= FEATURE_ARM_VFP | FEATURE_ARM_VFPV3;\n\t\t\t\t\telse if (val.contains(\"vfp\"))\n\t\t\t\t\t\tcachedFeature |= FEATURE_ARM_VFP;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t\tString vendor_id = cpuinfo.get(\"vendor_id\");\n\t\t\t\t\tString mips = cpuinfo.get(\"cpu model\");\n\t\t\t\t if (!TextUtils.isEmpty(vendor_id) && vendor_id.contains(\"GenuineIntel\")) {\n\t\t\t\t\t cachedFeature |= FEATURE_X86;\n\t\t\t\t } else if (!TextUtils.isEmpty(mips) && mips.contains(\"MIPS\")) {\n\t\t\t\t\t cachedFeature |= FEATURE_MIPS;\n\t\t\t\t }\n\t\t\t} \n\t\t}\n\n\t\treturn getCachedFeature();\n\t}\n\n\tprivate static int getCachedFeature() {\n\t\tif (cachedFeatureString == null) {\n\t\t\tStringBuffer sb = new StringBuffer();\n\t\t\tif ((cachedFeature & FEATURE_ARM_V5TE) > 0)\n\t\t\t\tsb.append(\"V5TE \");\n\t\t\tif ((cachedFeature & FEATURE_ARM_V6) > 0)\n\t\t\t\tsb.append(\"V6 \");\n\t\t\tif ((cachedFeature & FEATURE_ARM_VFP) > 0)\n\t\t\t\tsb.append(\"VFP \");\n\t\t\tif ((cachedFeature & FEATURE_ARM_V7A) > 0)\n\t\t\t\tsb.append(\"V7A \");\n\t\t\tif ((cachedFeature & FEATURE_ARM_VFPV3) > 0)\n\t\t\t\tsb.append(\"VFPV3 \");\n\t\t\tif ((cachedFeature & FEATURE_ARM_NEON) > 0)\n\t\t\t\tsb.append(\"NEON \");\n\t\t\tif ((cachedFeature & FEATURE_X86) > 0)\n\t\t\t\tsb.append(\"X86 \");\n\t\t\tif ((cachedFeature & FEATURE_MIPS) > 0)\n\t\t\t\tsb.append(\"MIPS \");\n\t\t\tcachedFeatureString = sb.toString();\n\t\t}\n\t\tLog.d(\"GET CPU FATURE: %s\", cachedFeatureString);\n\t\treturn cachedFeature;\n\t}\n\n\tpublic static boolean isDroidXDroid2() {\n\t\treturn (Build.MODEL.trim().equalsIgnoreCase(\"DROIDX\") || Build.MODEL.trim().equalsIgnoreCase(\"DROID2\") || Build.FINGERPRINT.toLowerCase().contains(\"shadow\") || Build.FINGERPRINT.toLowerCase().contains(\"droid2\"));\n\t}\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/ContextUtils.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport android.content.Context;\nimport android.content.pm.ApplicationInfo;\n\npublic class ContextUtils {\n\tpublic static int getVersionCode(Context ctx) {\n\t\tint version = 0;\n\t\ttry {\n\t\t\tversion = ctx.getPackageManager().getPackageInfo(ctx.getApplicationInfo().packageName, 0).versionCode;\n\t\t} catch (Exception e) {\n\t\t\tLog.e(\"getVersionInt\", e);\n\t\t}\n\t\treturn version;\n\t}\n\n\tpublic static String getDataDir(Context ctx) {\n\t\tApplicationInfo ai = ctx.getApplicationInfo();\n\t\tif (ai.dataDir != null)\n\t\t\treturn fixLastSlash(ai.dataDir);\n\t\telse\n\t\t\treturn \"/data/data/\" + ai.packageName + \"/\";\n\t}\n\n\tpublic static String fixLastSlash(String str) {\n\t\tString res = str == null ? \"/\" : str.trim() + \"/\";\n\t\tif (res.length() > 2 && res.charAt(res.length() - 2) == '/')\n\t\t\tres = res.substring(0, res.length() - 1);\n\t\treturn res;\n\t}\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/Crypto.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport java.io.BufferedInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.ObjectInputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.security.PublicKey;\nimport java.security.spec.AlgorithmParameterSpec;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\npublic class Crypto {\n\tprivate Cipher ecipher;\n\n\tpublic Crypto(String key) {\n\t\ttry {\n\t\t\tSecretKeySpec skey = new SecretKeySpec(generateKey(key), \"AES\");\n\t\t\tsetupCrypto(skey);\n\t\t} catch (Exception e) {\n\t\t\tLog.e(\"Crypto\", e);\n\t\t}\n\t}\n\n\tprivate void setupCrypto(SecretKey key) {\n\t\tbyte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };\n\t\tAlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);\n\t\ttry {\n\t\t\tecipher = Cipher.getInstance(\"AES/CBC/PKCS5Padding\");\n\t\t\tecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);\n\t\t} catch (Exception e) {\n\t\t\tecipher = null;\n\t\t\tLog.e(\"setupCrypto\", e);\n\t\t}\n\t}\n\n\tpublic String encrypt(String plaintext) {\n\t\tif (ecipher == null)\n\t\t\treturn \"\";\n\n\t\ttry {\n\t\t\tbyte[] ciphertext = ecipher.doFinal(plaintext.getBytes(\"UTF-8\"));\n\t\t\treturn Base64.encodeToString(ciphertext, Base64.NO_WRAP);\n\t\t} catch (Exception e) {\n\t\t\tLog.e(\"encryp\", e);\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tpublic static String md5(String plain) {\n\t\ttry {\n\t\t\tMessageDigest m = MessageDigest.getInstance(\"MD5\");\n\t\t\tm.update(plain.getBytes());\n\t\t\tbyte[] digest = m.digest();\n\t\t\tBigInteger bigInt = new BigInteger(1, digest);\n\t\t\tString hashtext = bigInt.toString(16);\n\t\t\twhile (hashtext.length() < 32) {\n\t\t\t\thashtext = \"0\" + hashtext;\n\t\t\t}\n\t\t\treturn hashtext;\n\t\t} catch (Exception e) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tprivate static byte[] generateKey(String input) {\n\t\ttry {\n\t\t\tbyte[] bytesOfMessage = input.getBytes(\"UTF-8\");\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"SHA256\");\n\t\t\treturn md.digest(bytesOfMessage);\n\t\t} catch (Exception e) {\n\t\t\tLog.e(\"generateKey\", e);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\tprivate PublicKey readKeyFromStream(InputStream keyStream) throws IOException {\n\t\tObjectInputStream oin = new ObjectInputStream(new BufferedInputStream(keyStream));\n\t\ttry {\n\t\t\tPublicKey pubKey = (PublicKey) oin.readObject();\n\t\t\treturn pubKey;\n\t\t} catch (Exception e) {\n\t\t\tLog.e(\"readKeyFromStream\", e);\n\t\t\treturn null;\n\t\t} finally {\n\t\t\toin.close();\n\t\t}\n\t}\n\n\tpublic String rsaEncrypt(InputStream keyStream, String data) {\n\t\ttry {\n\t\t\treturn rsaEncrypt(keyStream, data.getBytes(\"UTF-8\"));\n\t\t} catch (UnsupportedEncodingException e) {\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n\tpublic String rsaEncrypt(InputStream keyStream, byte[] data) {\n\t\ttry {\n\t\t\tPublicKey pubKey = readKeyFromStream(keyStream);\n\t\t\tCipher cipher = Cipher.getInstance(\"RSA/ECB/NoPadding\");\n\t\t\tcipher.init(Cipher.ENCRYPT_MODE, pubKey);\n\t\t\tbyte[] cipherData = cipher.doFinal(data);\n\t\t\treturn Base64.encodeToString(cipherData, Base64.NO_WRAP);\n\t\t} catch (Exception e) {\n\t\t\tLog.e(\"rsaEncrypt\", e);\n\t\t\treturn \"\";\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/Device.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.net.ConnectivityManager;\nimport android.os.Build;\nimport android.provider.Settings;\nimport android.telephony.TelephonyManager;\nimport android.util.DisplayMetrics;\n\nimport java.util.Locale;\n\npublic class Device {\n  public static String getLocale() {\n    Locale locale = Locale.getDefault();\n    if (locale != null) {\n      String lo = locale.getLanguage();\n      Log.i(\"getLocale \" + lo);\n      if (lo != null) {\n        return lo.toLowerCase();\n      }\n    }\n    return \"en\";\n  }\n\n  public static String getDeviceFeatures(Context ctx) {\n    return getIdentifiers(ctx) + getSystemFeatures() + getScreenFeatures(ctx);\n  }\n  \n  @SuppressLint(\"NewApi\")\n  public static String getIdentifiers(Context ctx) {\n    StringBuilder sb = new StringBuilder();\n    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.FROYO)\n    \tsb.append(getPair(\"serial\", Build.SERIAL));\n    else\n    \tsb.append(getPair(\"serial\", \"No Serial\"));\n    sb.append(getPair(\"android_id\", Settings.Secure.getString(ctx.getContentResolver(), Settings.Secure.ANDROID_ID)));\n    TelephonyManager tel = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);\n    sb.append(getPair(\"sim_country_iso\", tel.getSimCountryIso()));\n    sb.append(getPair(\"network_operator_name\", tel.getNetworkOperatorName()));\n    sb.append(getPair(\"unique_id\", Crypto.md5(sb.toString())));\n    ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);\n    sb.append(getPair(\"network_type\", cm.getActiveNetworkInfo() == null ? \"-1\" : String.valueOf(cm.getActiveNetworkInfo().getType())));\n    return sb.toString();\n  }\n\n  public static String getSystemFeatures() {\n    StringBuilder sb = new StringBuilder();\n    sb.append(getPair(\"android_release\", Build.VERSION.RELEASE));\n    sb.append(getPair(\"android_sdk_int\", \"\" + Build.VERSION.SDK_INT));\n    sb.append(getPair(\"device_cpu_abi\", Build.CPU_ABI));\n    sb.append(getPair(\"device_model\", Build.MODEL));\n    sb.append(getPair(\"device_manufacturer\", Build.MANUFACTURER));\n    sb.append(getPair(\"device_board\", Build.BOARD));\n    sb.append(getPair(\"device_fingerprint\", Build.FINGERPRINT));\n    sb.append(getPair(\"device_cpu_feature\", CPU.getFeatureString()));\n    return sb.toString();\n  }\n\n  public static String getScreenFeatures(Context ctx) {\n    StringBuilder sb = new StringBuilder();\n    DisplayMetrics disp = ctx.getResources().getDisplayMetrics();\n    sb.append(getPair(\"screen_density\", \"\" + disp.density));\n    sb.append(getPair(\"screen_density_dpi\", \"\" + disp.densityDpi));\n    sb.append(getPair(\"screen_height_pixels\", \"\" + disp.heightPixels));\n    sb.append(getPair(\"screen_width_pixels\", \"\" + disp.widthPixels));\n    sb.append(getPair(\"screen_scaled_density\", \"\" + disp.scaledDensity));\n    sb.append(getPair(\"screen_xdpi\", \"\" + disp.xdpi));\n    sb.append(getPair(\"screen_ydpi\", \"\" + disp.ydpi));\n    return sb.toString();\n  }\n\n  private static String getPair(String key, String value) {\n    key = key == null ? \"\" : key.trim();\n    value = value == null ? \"\" : value.trim();\n    return \"&\" + key + \"=\" + value;\n  }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/FileUtils.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport android.net.Uri;\nimport android.text.TextUtils;\n\nimport java.io.File;\nimport java.io.IOException;\n\npublic class FileUtils {\n\tprivate static final String FILE_NAME_RESERVED = \"|\\\\?*<\\\":>+[]/'\";\n\n\tpublic static String getUniqueFileName(String name, String id) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tfor (Character c : name.toCharArray()) {\n\t\t\tif (FILE_NAME_RESERVED.indexOf(c) == -1) {\n\t\t\t\tsb.append(c);\n\t\t\t}\n\t\t}\n\t\tname = sb.toString();\n\t\tif (name.length() > 16) {\n\t\t\tname = name.substring(0, 16);\n\t\t}\n\t\tid = Crypto.md5(id);\n\t\tname += id;\n\t\ttry {\n\t\t\tFile f = File.createTempFile(name, null);\n\t\t\tif (f.exists()) {\n\t\t\t\tf.delete();\n\t\t\t\treturn name;\n\t\t\t}\n\t\t} catch (IOException e) {\n\t\t}\n\t\treturn id;\n\t}\n\n\tpublic static String getCanonical(File f) {\n\t\tif (f == null)\n\t\t\treturn null;\n\n\t\ttry {\n\t\t\treturn f.getCanonicalPath();\n\t\t} catch (IOException e) {\n\t\t\treturn f.getAbsolutePath();\n\t\t}\n\t}\n\n\t/**\n\t * Get the path for the file:/// only\n\t * \n\t * @param uri\n\t * @return\n\t */\n\tpublic static String getPath(String uri) {\n\t\tLog.i(\"FileUtils#getPath(%s)\", uri);\n\t\tif (TextUtils.isEmpty(uri))\n\t\t\treturn null;\n\t\tif (uri.startsWith(\"file://\") && uri.length() > 7)\n\t\t\treturn Uri.decode(uri.substring(7));\n\t\treturn Uri.decode(uri);\n\t}\n\n\tpublic static String getName(String uri) {\n\t\tString path = getPath(uri);\n\t\tif (path != null)\n\t\t\treturn new File(path).getName();\n\t\treturn null;\n\t}\n\n\tpublic static void deleteDir(File f) {\n\t\tif (f.exists() && f.isDirectory()) {\n\t\t\tfor (File file : f.listFiles()) {\n\t\t\t\tif (file.isDirectory())\n\t\t\t\t\tdeleteDir(file);\n\t\t\t\tfile.delete();\n\t\t\t}\n\t\t\tf.delete();\n\t\t}\n\t}\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/IOUtils.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport android.database.Cursor;\nimport android.os.ParcelFileDescriptor;\nimport android.util.Log;\n\nimport java.io.Closeable;\n\npublic class IOUtils {\n\t\n\tprivate static final String TAG = \"IOUtils\";\n\t\n\tpublic static void closeSilently(Closeable c) {\n\t\tif (c == null)\n\t\t\treturn;\n\t\ttry {\n\t\t\tc.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"fail to close\", t);\n\t\t}\n\t}\n\n\tpublic static void closeSilently(ParcelFileDescriptor c) {\n\t\tif (c == null)\n\t\t\treturn;\n\t\ttry {\n\t\t\tc.close();\n\t\t} catch (Throwable t) {\n\t\t\tLog.w(TAG, \"fail to close\", t);\n\t\t}\n\t}\n\t\n\tpublic static void closeSilently(Cursor cursor) {\n    try {\n    \tif (cursor != null) cursor.close();\n     } catch (Throwable t) {\n    \t Log.w(TAG, \"fail to close\", t);\n     }\n\t }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/Log.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport io.vov.vitamio.BuildConfig;\n\nimport java.util.MissingFormatArgumentException;\n\npublic class Log {\n\tpublic static final String TAG = \"Vitamio[Player]\";\n\n\tpublic static void i(String msg, Object... args) {\n\t\ttry {\n\t\t\tif (BuildConfig.DEBUG) \n\t\t\t\tandroid.util.Log.i(TAG, String.format(msg, args));\n\t\t} catch (MissingFormatArgumentException e) {\n\t\t\tandroid.util.Log.e(TAG, \"vitamio.Log\", e);\n\t\t\tandroid.util.Log.i(TAG, msg);\n\t\t}\n\t}\n\n\tpublic static void d(String msg, Object... args) {\n\t\ttry {\n\t\t\tif (BuildConfig.DEBUG) \n\t\t\t\tandroid.util.Log.d(TAG, String.format(msg, args));\n\t\t} catch (MissingFormatArgumentException e) {\n\t\t\tandroid.util.Log.e(TAG, \"vitamio.Log\", e);\n\t\t\tandroid.util.Log.d(TAG, msg);\n\t\t}\n\t}\n\n\tpublic static void e(String msg, Object... args) {\n\t\ttry {\n\t\t\t\tandroid.util.Log.e(TAG, String.format(msg, args));\n\t\t} catch (MissingFormatArgumentException e) {\n\t\t\tandroid.util.Log.e(TAG, \"vitamio.Log\", e);\n\t\t\tandroid.util.Log.e(TAG, msg);\n\t\t}\n\t}\n\n\tpublic static void e(String msg, Throwable t) {\n\t\tandroid.util.Log.e(TAG, msg, t);\n\t}\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/ScreenResolution.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.DisplayMetrics;\nimport android.util.Pair;\nimport android.view.Display;\nimport android.view.WindowManager;\n\nimport java.lang.reflect.Method;\n\n/**\n * Class to get the real screen resolution includes the system status bar.\n * We can get the value by calling the getRealMetrics method if API >= 17\n * Reflection needed on old devices..\n * */\npublic class ScreenResolution {\n  /**\n   * Gets the resolution,\n   * @return a pair to return the width and height\n   * */\n  public static Pair<Integer,Integer> getResolution(Context ctx){\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n      return getRealResolution(ctx);\n    }\n    else {\n      return getRealResolutionOnOldDevice(ctx);\n    }\n  }\n\n  /**\n   * Gets resolution on old devices.\n   * Tries the reflection to get the real resolution first.\n   * Fall back to getDisplayMetrics if the above method failed.\n   * */\n  private static Pair<Integer, Integer> getRealResolutionOnOldDevice(Context ctx) {\n    try{\n      WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);\n      Display display = wm.getDefaultDisplay();\n      Method mGetRawWidth = Display.class.getMethod(\"getRawWidth\");\n      Method mGetRawHeight = Display.class.getMethod(\"getRawHeight\");\n      Integer realWidth = (Integer) mGetRawWidth.invoke(display);\n      Integer realHeight = (Integer) mGetRawHeight.invoke(display);\n      return new Pair<Integer, Integer>(realWidth, realHeight);\n    }\n    catch (Exception e) {\n      DisplayMetrics disp = ctx.getResources().getDisplayMetrics();\n      return new Pair<Integer, Integer>(disp.widthPixels, disp.heightPixels);\n    }\n  }\n\n  /**\n   * Gets real resolution via the new getRealMetrics API.\n   * */\n  @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n  private static Pair<Integer,Integer> getRealResolution(Context ctx) {\n    WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);\n    Display display = wm.getDefaultDisplay();\n    DisplayMetrics metrics = new DisplayMetrics();\n    display.getRealMetrics(metrics);\n    return new Pair<Integer, Integer>(metrics.widthPixels, metrics.heightPixels);\n  }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/utils/StringUtils.java",
    "content": "/*\n * Copyright (C) 2012 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.utils;\n\nimport java.util.Arrays;\nimport java.util.Iterator;\n\npublic class StringUtils {\n\tpublic static String join(Object[] elements, CharSequence separator) {\n\t\treturn join(Arrays.asList(elements), separator);\n\t}\n\n\tpublic static String join(Iterable<? extends Object> elements, CharSequence separator) {\n\t\tStringBuilder builder = new StringBuilder();\n\n\t\tif (elements != null) {\n\t\t\tIterator<? extends Object> iter = elements.iterator();\n\t\t\tif (iter.hasNext()) {\n\t\t\t\tbuilder.append(String.valueOf(iter.next()));\n\t\t\t\twhile (iter.hasNext()) {\n\t\t\t\t\tbuilder.append(separator).append(String.valueOf(iter.next()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn builder.toString();\n\t}\n\n\tpublic static String fixLastSlash(String str) {\n\t\tString res = str == null ? \"/\" : str.trim() + \"/\";\n\t\tif (res.length() > 2 && res.charAt(res.length() - 2) == '/')\n\t\t\tres = res.substring(0, res.length() - 1);\n\t\treturn res;\n\t}\n\n\tpublic static int convertToInt(String str) throws NumberFormatException {\n\t\tint s, e;\n\t\tfor (s = 0; s < str.length(); s++)\n\t\t\tif (Character.isDigit(str.charAt(s)))\n\t\t\t\tbreak;\n\t\tfor (e = str.length(); e > 0; e--)\n\t\t\tif (Character.isDigit(str.charAt(e - 1)))\n\t\t\t\tbreak;\n\t\tif (e > s) {\n\t\t\ttry {\n\t\t\t\treturn Integer.parseInt(str.substring(s, e));\n\t\t\t} catch (NumberFormatException ex) {\n\t\t\t\tLog.e(\"convertToInt\", ex);\n\t\t\t\tthrow new NumberFormatException();\n\t\t\t}\n\t\t} else {\n\t\t\tthrow new NumberFormatException();\n\t\t}\n\t}\n\n\tpublic static String generateTime(long time) {\n\t\tint totalSeconds = (int) (time / 1000);\n\t\tint seconds = totalSeconds % 60;\n\t\tint minutes = (totalSeconds / 60) % 60;\n\t\tint hours = totalSeconds / 3600;\n\n\t\treturn hours > 0 ? String.format(\"%02d:%02d:%02d\", hours, minutes, seconds) : String.format(\"%02d:%02d\", minutes, seconds);\n\t}\n\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/widget/CenterLayout.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.RemoteViews.RemoteView;\n\n@RemoteView\npublic class CenterLayout extends ViewGroup {\n  private int mPaddingLeft = 0;\n  private int mPaddingRight = 0;\n  private int mPaddingTop = 0;\n  private int mPaddingBottom = 0;\n  private int mWidth, mHeight;\n\n  public CenterLayout(Context context) {\n    super(context);\n  }\n\n  public CenterLayout(Context context, AttributeSet attrs) {\n    super(context, attrs);\n  }\n\n  public CenterLayout(Context context, AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n  }\n\n  @Override\n  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n    int count = getChildCount();\n\n    int maxHeight = 0;\n    int maxWidth = 0;\n\n    measureChildren(widthMeasureSpec, heightMeasureSpec);\n\n    for (int i = 0; i < count; i++) {\n      View child = getChildAt(i);\n      if (child.getVisibility() != GONE) {\n        int childRight;\n        int childBottom;\n\n        CenterLayout.LayoutParams lp = (CenterLayout.LayoutParams) child.getLayoutParams();\n\n        childRight = lp.x + child.getMeasuredWidth();\n        childBottom = lp.y + child.getMeasuredHeight();\n\n        maxWidth = Math.max(maxWidth, childRight);\n        maxHeight = Math.max(maxHeight, childBottom);\n      }\n    }\n\n    maxWidth += mPaddingLeft + mPaddingRight;\n    maxHeight += mPaddingTop + mPaddingBottom;\n\n    maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());\n    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());\n\n    setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), resolveSize(maxHeight, heightMeasureSpec));\n  }\n\n  @Override\n  protected void onLayout(boolean changed, int l, int t, int r, int b) {\n    int count = getChildCount();\n    mWidth = getMeasuredWidth();\n    mHeight = getMeasuredHeight();\n    for (int i = 0; i < count; i++) {\n      View child = getChildAt(i);\n      if (child.getVisibility() != GONE) {\n        CenterLayout.LayoutParams lp = (CenterLayout.LayoutParams) child.getLayoutParams();\n        int childLeft = mPaddingLeft + lp.x;\n        if (lp.width > 0)\n          childLeft += (int) ((mWidth - lp.width) / 2.0);\n        else\n          childLeft += (int) ((mWidth - child.getMeasuredWidth()) / 2.0);\n        int childTop = mPaddingTop + lp.y;\n        if (lp.height > 0)\n          childTop += (int) ((mHeight - lp.height) / 2.0);\n        else\n          childTop += (int) ((mHeight - child.getMeasuredHeight()) / 2.0);\n        child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight());\n      }\n    }\n  }\n\n  @Override\n  protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\n    return p instanceof CenterLayout.LayoutParams;\n  }\n\n  @Override\n  protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {\n    return new LayoutParams(p);\n  }\n\n  public static class LayoutParams extends ViewGroup.LayoutParams {\n    public int x;\n    public int y;\n\n    public LayoutParams(int width, int height, int x, int y) {\n      super(width, height);\n      this.x = x;\n      this.y = y;\n    }\n\n    public LayoutParams(ViewGroup.LayoutParams source) {\n      super(source);\n    }\n  }\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/widget/MediaController.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.widget;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.media.AudioManager;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.widget.FrameLayout;\nimport android.widget.ImageButton;\nimport android.widget.PopupWindow;\nimport android.widget.SeekBar;\nimport android.widget.SeekBar.OnSeekBarChangeListener;\nimport android.widget.TextView;\n\nimport java.lang.reflect.Method;\n\nimport io.vov.vitamio.utils.Log;\nimport io.vov.vitamio.utils.StringUtils;\n\n/**\n * A view containing controls for a MediaPlayer. Typically contains the buttons\n * like \"Play/Pause\" and a progress slider. It takes care of synchronizing the\n * controls with the state of the MediaPlayer.\n * <p/>\n * The way to use this class is to a) instantiate it programatically or b)\n * create it in your xml layout.\n * <p/>\n * a) The MediaController will create a default set of controls and put them in\n * a window floating above your application. Specifically, the controls will\n * float above the view specified with setAnchorView(). By default, the window\n * will disappear if left idle for three seconds and reappear when the user\n * touches the anchor view. To customize the MediaController's style, layout and\n * controls you should extend MediaController and override the {#link\n * {@link #makeControllerView()} method.\n * <p/>\n * b) The MediaController is a FrameLayout, you can put it in your layout xml\n * and get it through {@link #findViewById(int)}.\n * <p/>\n * NOTES: In each way, if you want customize the MediaController, the SeekBar's\n * id must be mediacontroller_progress, the Play/Pause's must be\n * mediacontroller_pause, current time's must be mediacontroller_time_current,\n * total time's must be mediacontroller_time_total, file name's must be\n * mediacontroller_file_name. And your resources must have a pause_button\n * drawable and a play_button drawable.\n * <p/>\n * Functions like show() and hide() have no effect when MediaController is\n * created in an xml layout.\n */\npublic class MediaController extends FrameLayout {\n  private static final int sDefaultTimeout = 3000;\n  private static final int FADE_OUT = 1;\n  private static final int SHOW_PROGRESS = 2;\n  private MediaPlayerControl mPlayer;\n  private Context mContext;\n  private PopupWindow mWindow;\n  private int mAnimStyle;\n  private View mAnchor;\n  private View mRoot;\n  private SeekBar mProgress;\n  private TextView mEndTime, mCurrentTime;\n  private TextView mFileName;\n  private OutlineTextView mInfoView;\n  private String mTitle;\n  private long mDuration;\n  private boolean mShowing;\n  private boolean mDragging;\n  private boolean mInstantSeeking = false;\n  private boolean mFromXml = false;\n  private ImageButton mPauseButton;\n  private AudioManager mAM;\n  private OnShownListener mShownListener;\n  private OnHiddenListener mHiddenListener;\n  @SuppressLint(\"HandlerLeak\")\n  private Handler mHandler = new Handler() {\n    @Override\n    public void handleMessage(Message msg) {\n      long pos;\n      switch (msg.what) {\n        case FADE_OUT:\n          hide();\n          break;\n        case SHOW_PROGRESS:\n          pos = setProgress();\n          if (!mDragging && mShowing) {\n            msg = obtainMessage(SHOW_PROGRESS);\n            sendMessageDelayed(msg, 1000 - (pos % 1000));\n            updatePausePlay();\n          }\n          break;\n      }\n    }\n  };\n  private View.OnClickListener mPauseListener = new View.OnClickListener() {\n    public void onClick(View v) {\n      doPauseResume();\n      show(sDefaultTimeout);\n    }\n  };\n  private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {\n    public void onStartTrackingTouch(SeekBar bar) {\n      mDragging = true;\n      show(3600000);\n      mHandler.removeMessages(SHOW_PROGRESS);\n      if (mInstantSeeking)\n        mAM.setStreamMute(AudioManager.STREAM_MUSIC, true);\n      if (mInfoView != null) {\n        mInfoView.setText(\"\");\n        mInfoView.setVisibility(View.VISIBLE);\n      }\n    }\n\n    public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {\n      if (!fromuser)\n        return;\n\n      long newposition = (mDuration * progress) / 1000;\n      String time = StringUtils.generateTime(newposition);\n      if (mInstantSeeking)\n        mPlayer.seekTo(newposition);\n      if (mInfoView != null)\n        mInfoView.setText(time);\n      if (mCurrentTime != null)\n        mCurrentTime.setText(time);\n    }\n\n    public void onStopTrackingTouch(SeekBar bar) {\n      if (!mInstantSeeking)\n        mPlayer.seekTo((mDuration * bar.getProgress()) / 1000);\n      if (mInfoView != null) {\n        mInfoView.setText(\"\");\n        mInfoView.setVisibility(View.GONE);\n      }\n      show(sDefaultTimeout);\n      mHandler.removeMessages(SHOW_PROGRESS);\n      mAM.setStreamMute(AudioManager.STREAM_MUSIC, false);\n      mDragging = false;\n      mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000);\n    }\n  };\n\n  public MediaController(Context context, AttributeSet attrs) {\n    super(context, attrs);\n    mRoot = this;\n    mFromXml = true;\n    initController(context);\n  }\n\n  public MediaController(Context context) {\n    super(context);\n    if (!mFromXml && initController(context))\n      initFloatingWindow();\n  }\n\n  private boolean initController(Context context) {\n    mContext = context;\n    mAM = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);\n    return true;\n  }\n\n  @Override\n  public void onFinishInflate() {\n    if (mRoot != null)\n      initControllerView(mRoot);\n  }\n\n  private void initFloatingWindow() {\n    mWindow = new PopupWindow(mContext);\n    mWindow.setFocusable(false);\n    mWindow.setBackgroundDrawable(null);\n    mWindow.setOutsideTouchable(true);\n    mAnimStyle = android.R.style.Animation;\n  }\n  \n  @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n\tpublic void setWindowLayoutType() {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n\t\t\ttry {\n\t\t\t\tmAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);\n\t\t\t\tMethod setWindowLayoutType = PopupWindow.class.getMethod(\"setWindowLayoutType\", new Class[] { int.class });\n\t\t\t\tsetWindowLayoutType.invoke(mWindow, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG);\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.e(\"setWindowLayoutType\", e);\n\t\t\t}\n\t\t}\n\t}\n\n  /**\n   * Set the view that acts as the anchor for the control view. This can for\n   * example be a VideoView, or your Activity's main view.\n   *\n   * @param view The view to which to anchor the controller when it is visible.\n   */\n  public void setAnchorView(View view) {\n    mAnchor = view;\n    if (!mFromXml) {\n      removeAllViews();\n      mRoot = makeControllerView();\n      mWindow.setContentView(mRoot);\n      mWindow.setWidth(LayoutParams.MATCH_PARENT);\n      mWindow.setHeight(LayoutParams.WRAP_CONTENT);\n    }\n    initControllerView(mRoot);\n  }\n\n  /**\n   * Create the view that holds the widgets that control playback. Derived\n   * classes can override this to create their own.\n   *\n   * @return The controller view.\n   */\n  protected View makeControllerView() {\n    return ((LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(getResources().getIdentifier(\"mediacontroller\", \"layout\", mContext.getPackageName()), this);\n  }\n\n  private void initControllerView(View v) {\n    mPauseButton = (ImageButton) v.findViewById(getResources().getIdentifier(\"mediacontroller_play_pause\", \"id\", mContext.getPackageName()));\n    if (mPauseButton != null) {\n      mPauseButton.requestFocus();\n      mPauseButton.setOnClickListener(mPauseListener);\n    }\n\n    mProgress = (SeekBar) v.findViewById(getResources().getIdentifier(\"mediacontroller_seekbar\", \"id\", mContext.getPackageName()));\n    if (mProgress != null) {\n      if (mProgress instanceof SeekBar) {\n        SeekBar seeker = (SeekBar) mProgress;\n        seeker.setOnSeekBarChangeListener(mSeekListener);\n      }\n      mProgress.setMax(1000);\n    }\n\n    mEndTime = (TextView) v.findViewById(getResources().getIdentifier(\"mediacontroller_time_total\", \"id\", mContext.getPackageName()));\n    mCurrentTime = (TextView) v.findViewById(getResources().getIdentifier(\"mediacontroller_time_current\", \"id\", mContext.getPackageName()));\n    mFileName = (TextView) v.findViewById(getResources().getIdentifier(\"mediacontroller_file_name\", \"id\", mContext.getPackageName()));\n    if (mFileName != null)\n      mFileName.setText(mTitle);\n  }\n\n  public void setMediaPlayer(MediaPlayerControl player) {\n    mPlayer = player;\n    updatePausePlay();\n  }\n\n  /**\n   * Control the action when the seekbar dragged by user\n   *\n   * @param seekWhenDragging True the media will seek periodically\n   */\n  public void setInstantSeeking(boolean seekWhenDragging) {\n    mInstantSeeking = seekWhenDragging;\n  }\n\n  public void show() {\n    show(sDefaultTimeout);\n  }\n\n  /**\n   * Set the content of the file_name TextView\n   *\n   * @param name\n   */\n  public void setFileName(String name) {\n    mTitle = name;\n    if (mFileName != null)\n      mFileName.setText(mTitle);\n  }\n\n  /**\n   * Set the View to hold some information when interact with the\n   * MediaController\n   *\n   * @param v\n   */\n  public void setInfoView(OutlineTextView v) {\n    mInfoView = v;\n  }\n\n  /**\n   * <p>\n   * Change the animation style resource for this controller.\n   * </p>\n   * <p/>\n   * <p>\n   * If the controller is showing, calling this method will take effect only the\n   * next time the controller is shown.\n   * </p>\n   *\n   * @param animationStyle animation style to use when the controller appears\n   *                       and disappears. Set to -1 for the default animation, 0 for no animation, or\n   *                       a resource identifier for an explicit animation.\n   */\n  public void setAnimationStyle(int animationStyle) {\n    mAnimStyle = animationStyle;\n  }\n\n  /**\n   * Show the controller on screen. It will go away automatically after\n   * 'timeout' milliseconds of inactivity.\n   *\n   * @param timeout The timeout in milliseconds. Use 0 to show the controller\n   *                until hide() is called.\n   */\n  public void show(int timeout) {\n    if (!mShowing && mAnchor != null && mAnchor.getWindowToken() != null) {\n      if (mPauseButton != null)\n        mPauseButton.requestFocus();\n\n      if (mFromXml) {\n        setVisibility(View.VISIBLE);\n      } else {\n        int[] location = new int[2];\n\n        mAnchor.getLocationOnScreen(location);\n        Rect anchorRect = new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] + mAnchor.getHeight());\n\n        mWindow.setAnimationStyle(mAnimStyle);\n        setWindowLayoutType();\n        mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, anchorRect.left, anchorRect.bottom);\n      }\n      mShowing = true;\n      if (mShownListener != null)\n        mShownListener.onShown();\n    }\n    updatePausePlay();\n    mHandler.sendEmptyMessage(SHOW_PROGRESS);\n\n    if (timeout != 0) {\n      mHandler.removeMessages(FADE_OUT);\n      mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT), timeout);\n    }\n  }\n\n  public boolean isShowing() {\n    return mShowing;\n  }\n\n  public void hide() {\n    if (mAnchor == null)\n      return;\n\n    if (mShowing) {\n      try {\n        mHandler.removeMessages(SHOW_PROGRESS);\n        if (mFromXml)\n          setVisibility(View.GONE);\n        else\n          mWindow.dismiss();\n      } catch (IllegalArgumentException ex) {\n        Log.d(\"MediaController already removed\");\n      }\n      mShowing = false;\n      if (mHiddenListener != null)\n        mHiddenListener.onHidden();\n    }\n  }\n\n  public void setOnShownListener(OnShownListener l) {\n    mShownListener = l;\n  }\n\n  public void setOnHiddenListener(OnHiddenListener l) {\n    mHiddenListener = l;\n  }\n\n  private long setProgress() {\n    if (mPlayer == null || mDragging)\n      return 0;\n\n    long position = mPlayer.getCurrentPosition();\n    long duration = mPlayer.getDuration();\n    if (mProgress != null) {\n      if (duration > 0) {\n        long pos = 1000L * position / duration;\n        mProgress.setProgress((int) pos);\n      }\n      int percent = mPlayer.getBufferPercentage();\n      mProgress.setSecondaryProgress(percent * 10);\n    }\n\n    mDuration = duration;\n\n    if (mEndTime != null)\n      mEndTime.setText(StringUtils.generateTime(mDuration));\n    if (mCurrentTime != null)\n      mCurrentTime.setText(StringUtils.generateTime(position));\n\n    return position;\n  }\n\n  @Override\n  public boolean onTouchEvent(MotionEvent event) {\n    show(sDefaultTimeout);\n    return true;\n  }\n\n  @Override\n  public boolean onTrackballEvent(MotionEvent ev) {\n    show(sDefaultTimeout);\n    return false;\n  }\n\n  @Override\n  public boolean dispatchKeyEvent(KeyEvent event) {\n    int keyCode = event.getKeyCode();\n    if (event.getRepeatCount() == 0 && (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE)) {\n      doPauseResume();\n      show(sDefaultTimeout);\n      if (mPauseButton != null)\n        mPauseButton.requestFocus();\n      return true;\n    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) {\n      if (mPlayer.isPlaying()) {\n        mPlayer.pause();\n        updatePausePlay();\n      }\n      return true;\n    } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {\n      hide();\n      return true;\n    } else {\n      show(sDefaultTimeout);\n    }\n    return super.dispatchKeyEvent(event);\n  }\n\n  private void updatePausePlay() {\n    if (mRoot == null || mPauseButton == null)\n      return;\n\n    if (mPlayer.isPlaying())\n      mPauseButton.setImageResource(getResources().getIdentifier(\"mediacontroller_pause\", \"drawable\", mContext.getPackageName()));\n    else\n      mPauseButton.setImageResource(getResources().getIdentifier(\"mediacontroller_play\", \"drawable\", mContext.getPackageName()));\n  }\n\n  private void doPauseResume() {\n    if (mPlayer.isPlaying())\n      mPlayer.pause();\n    else\n      mPlayer.start();\n    updatePausePlay();\n  }\n\n  @Override\n  public void setEnabled(boolean enabled) {\n    if (mPauseButton != null)\n      mPauseButton.setEnabled(enabled);\n    if (mProgress != null)\n      mProgress.setEnabled(enabled);\n    super.setEnabled(enabled);\n  }\n\n  public interface OnShownListener {\n    public void onShown();\n  }\n\n  public interface OnHiddenListener {\n    public void onHidden();\n  }\n\n  public interface MediaPlayerControl {\n    void start();\n\n    void pause();\n\n    long getDuration();\n\n    long getCurrentPosition();\n\n    void seekTo(long pos);\n\n    boolean isPlaying();\n\n    int getBufferPercentage();\n  }\n\n}\n"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/widget/OutlineTextView.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.widget;\n\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\nimport android.graphics.Typeface;\nimport android.text.Layout;\nimport android.text.StaticLayout;\nimport android.text.TextPaint;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\n\n/**\n * Display text with border, use the same XML attrs as\n * {@link android.widget.TextView}, except that {@link OutlineTextView} will\n * transform the shadow to border\n */\npublic class OutlineTextView extends TextView {\n  private TextPaint mTextPaint;\n  private TextPaint mTextPaintOutline;\n  private String mText = \"\";\n  private int mAscent = 0;\n  private float mBorderSize;\n  private int mBorderColor;\n  private int mColor;\n  private float mSpacingMult = 1.0f;\n  private float mSpacingAdd = 0;\n  private boolean mIncludePad = true;\n\n  public OutlineTextView(Context context) {\n    super(context);\n    initPaint();\n  }\n\n  public OutlineTextView(Context context, AttributeSet attrs) {\n    super(context, attrs);\n    initPaint();\n  }\n\n  public OutlineTextView(Context context, AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n    initPaint();\n  }\n\n  private void initPaint() {\n    mTextPaint = new TextPaint();\n    mTextPaint.setAntiAlias(true);\n    mTextPaint.setTextSize(getTextSize());\n    mTextPaint.setColor(mColor);\n    mTextPaint.setStyle(Paint.Style.FILL);\n    mTextPaint.setTypeface(getTypeface());\n\n    mTextPaintOutline = new TextPaint();\n    mTextPaintOutline.setAntiAlias(true);\n    mTextPaintOutline.setTextSize(getTextSize());\n    mTextPaintOutline.setColor(mBorderColor);\n    mTextPaintOutline.setStyle(Paint.Style.STROKE);\n    mTextPaintOutline.setTypeface(getTypeface());\n    mTextPaintOutline.setStrokeWidth(mBorderSize);\n  }\n\n  public void setText(String text) {\n    super.setText(text);\n    mText = text.toString();\n    requestLayout();\n    invalidate();\n  }\n\n  public void setTextSize(float size) {\n    super.setTextSize(size);\n    requestLayout();\n    invalidate();\n    initPaint();\n  }\n\n  public void setTextColor(int color) {\n    super.setTextColor(color);\n    mColor = color;\n    invalidate();\n    initPaint();\n  }\n\n  public void setShadowLayer(float radius, float dx, float dy, int color) {\n    super.setShadowLayer(radius, dx, dy, color);\n    mBorderSize = radius;\n    mBorderColor = color;\n    requestLayout();\n    invalidate();\n    initPaint();\n  }\n\n  public void setTypeface(Typeface tf, int style) {\n    super.setTypeface(tf, style);\n    requestLayout();\n    invalidate();\n    initPaint();\n  }\n\n  public void setTypeface(Typeface tf) {\n    super.setTypeface(tf);\n    requestLayout();\n    invalidate();\n    initPaint();\n  }\n\n  @Override\n  protected void onDraw(Canvas canvas) {\n    Layout layout = new StaticLayout(getText(), mTextPaintOutline, getWidth(), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad);\n    layout.draw(canvas);\n    layout = new StaticLayout(getText(), mTextPaint, getWidth(), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad);\n    layout.draw(canvas);\n  }\n\n  @Override\n  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n    Layout layout = new StaticLayout(getText(), mTextPaintOutline, measureWidth(widthMeasureSpec), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad);\n    int ex = (int) (mBorderSize * 2 + 1);\n    setMeasuredDimension(measureWidth(widthMeasureSpec) + ex, measureHeight(heightMeasureSpec) * layout.getLineCount() + ex);\n  }\n\n  private int measureWidth(int measureSpec) {\n    int result = 0;\n    int specMode = MeasureSpec.getMode(measureSpec);\n    int specSize = MeasureSpec.getSize(measureSpec);\n\n    if (specMode == MeasureSpec.EXACTLY) {\n      result = specSize;\n    } else {\n      result = (int) mTextPaintOutline.measureText(mText) + getPaddingLeft() + getPaddingRight();\n      if (specMode == MeasureSpec.AT_MOST) {\n        result = Math.min(result, specSize);\n      }\n    }\n\n    return result;\n  }\n\n  private int measureHeight(int measureSpec) {\n    int result = 0;\n    int specMode = MeasureSpec.getMode(measureSpec);\n    int specSize = MeasureSpec.getSize(measureSpec);\n\n    mAscent = (int) mTextPaintOutline.ascent();\n    if (specMode == MeasureSpec.EXACTLY) {\n      result = specSize;\n    } else {\n      result = (int) (-mAscent + mTextPaintOutline.descent()) + getPaddingTop() + getPaddingBottom();\n      if (specMode == MeasureSpec.AT_MOST) {\n        result = Math.min(result, specSize);\n      }\n    }\n    return result;\n  }\n}"
  },
  {
    "path": "vitamio/src/io/vov/vitamio/widget/VideoView.java",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.widget;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.graphics.PixelFormat;\nimport android.media.AudioManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.util.Pair;\nimport android.util.SparseArray;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceView;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewGroup.LayoutParams;\n\nimport io.vov.vitamio.MediaFormat;\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;\nimport io.vov.vitamio.MediaPlayer.OnCompletionListener;\nimport io.vov.vitamio.MediaPlayer.OnErrorListener;\nimport io.vov.vitamio.MediaPlayer.OnInfoListener;\nimport io.vov.vitamio.MediaPlayer.OnPreparedListener;\nimport io.vov.vitamio.MediaPlayer.OnSeekCompleteListener;\nimport io.vov.vitamio.MediaPlayer.OnTimedTextListener;\nimport io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener;\nimport io.vov.vitamio.MediaPlayer.TrackInfo;\nimport io.vov.vitamio.Vitamio;\nimport io.vov.vitamio.utils.Log;\nimport io.vov.vitamio.utils.ScreenResolution;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Displays a video file. The VideoView class can load images from various\n * sources (such as resources or content providers), takes care of computing its\n * measurement from the video so that it can be used in any layout manager, and\n * provides various display options such as scaling and tinting.\n * <p/>\n * VideoView also provide many wrapper methods for\n * {@link io.vov.vitamio.MediaPlayer}, such as {@link #getVideoWidth()},\n * {@link #setTimedTextShown(boolean)}\n */\npublic class VideoView extends SurfaceView implements MediaController.MediaPlayerControl {\n\tpublic static final int VIDEO_LAYOUT_ORIGIN = 0;\n\tpublic static final int VIDEO_LAYOUT_SCALE = 1;\n\tpublic static final int VIDEO_LAYOUT_STRETCH = 2;\n\tpublic static final int VIDEO_LAYOUT_ZOOM = 3;\n\tpublic static final int VIDEO_LAYOUT_FIT_PARENT = 4;\n\tprivate static final int STATE_ERROR = -1;\n\tprivate static final int STATE_IDLE = 0;\n\tprivate static final int STATE_PREPARING = 1;\n\tprivate static final int STATE_PREPARED = 2;\n\tprivate static final int STATE_PLAYING = 3;\n\tprivate static final int STATE_PAUSED = 4;\n\tprivate static final int STATE_PLAYBACK_COMPLETED = 5;\n\tprivate static final int STATE_SUSPEND = 6;\n\tprivate static final int STATE_RESUME = 7;\n\tprivate static final int STATE_SUSPEND_UNSUPPORTED = 8;\n\tOnVideoSizeChangedListener mSizeChangedListener = new OnVideoSizeChangedListener() {\n\t\tpublic void onVideoSizeChanged(MediaPlayer mp, int width, int height) {\n      Log.d(\"onVideoSizeChanged: (%dx%d)\", width, height);\n      mVideoWidth = mp.getVideoWidth();\n      mVideoHeight = mp.getVideoHeight();\n      mVideoAspectRatio = mp.getVideoAspectRatio();\n      if (mVideoWidth != 0 && mVideoHeight != 0)\n        setVideoLayout(mVideoLayout, mAspectRatio);\n    }\n  };\n  OnPreparedListener mPreparedListener = new OnPreparedListener() {\n    public void onPrepared(MediaPlayer mp) {\n      Log.d(\"onPrepared\");\n\t\t\tif (false) {\n\t\t\t\tstopPlayback();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmCurrentState = STATE_PREPARED;\n\t\t\t// mTargetState = STATE_PLAYING;\n\n\t\t\t// Get the capabilities of the player for this stream\n\t\t\t//TODO mCanPause\n\n      if (mOnPreparedListener != null)\n        mOnPreparedListener.onPrepared(mMediaPlayer);\n      if (mMediaController != null)\n        mMediaController.setEnabled(true);\n      mVideoWidth = mp.getVideoWidth();\n      mVideoHeight = mp.getVideoHeight();\n      mVideoAspectRatio = mp.getVideoAspectRatio();\n\n      long seekToPosition = mSeekWhenPrepared;\n      if (seekToPosition != 0)\n        seekTo(seekToPosition);\n      \n      if (mVideoWidth != 0 && mVideoHeight != 0) {\n        setVideoLayout(mVideoLayout, mAspectRatio);\n        if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {\n          if (mTargetState == STATE_PLAYING) {\n            start();\n            if (mMediaController != null)\n              mMediaController.show();\n          } else if (!isPlaying() && (seekToPosition != 0 || getCurrentPosition() > 0)) {\n            if (mMediaController != null)\n              mMediaController.show(0);\n          }\n        }\n      } else if (mTargetState == STATE_PLAYING) {\n        start();\n      }\n    }\n  };\n  SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() {\n    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {\n      mSurfaceWidth = w;\n      mSurfaceHeight = h;\n      boolean isValidState = (mTargetState == STATE_PLAYING);\n      boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);\n      if (mMediaPlayer != null && isValidState && hasValidSize) {\n        if (mSeekWhenPrepared != 0)\n          seekTo(mSeekWhenPrepared);\n        start();\n        if (mMediaController != null) {\n          if (mMediaController.isShowing())\n            mMediaController.hide();\n          mMediaController.show();\n        }\n      }\n    }\n\n    public void surfaceCreated(SurfaceHolder holder) {\n      mSurfaceHolder = holder;\n      if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND && mTargetState == STATE_RESUME) {\n        mMediaPlayer.setDisplay(mSurfaceHolder);\n        resume();\n      } else {\n        openVideo();\n      }\n    }\n\n\t\tpublic void surfaceDestroyed(SurfaceHolder holder) {\n\t\t\tmSurfaceHolder = null;\n\t\t\tif (mMediaController != null)\n\t\t\t\tmMediaController.hide();\n\t\t\trelease(true);\n\t\t}\n\t};\n\tprivate Uri mUri;\n\tprivate long mDuration;\n\tprivate int mCurrentState = STATE_IDLE;\n\tprivate int mTargetState = STATE_IDLE;\n\tprivate float mAspectRatio = 0;\n\tprivate int mVideoLayout = VIDEO_LAYOUT_SCALE;\n\tprivate SurfaceHolder mSurfaceHolder = null;\n\tprivate MediaPlayer mMediaPlayer = null;\n\tprivate int mVideoWidth;\n\tprivate int mVideoHeight;\n\tprivate float mVideoAspectRatio;\n\tprivate int mVideoChroma = MediaPlayer.VIDEOCHROMA_RGBA;\n\tprivate boolean mHardwareDecoder = false;\n\tprivate int mSurfaceWidth;\n\tprivate int mSurfaceHeight;\n\tprivate MediaController mMediaController;\n\tprivate View mMediaBufferingIndicator;\n\tprivate OnCompletionListener mOnCompletionListener;\n\tprivate OnPreparedListener mOnPreparedListener;\n\tprivate OnErrorListener mOnErrorListener;\n\tprivate OnSeekCompleteListener mOnSeekCompleteListener;\n\tprivate OnTimedTextListener mOnTimedTextListener;\n\tprivate OnInfoListener mOnInfoListener;\n\tprivate OnBufferingUpdateListener mOnBufferingUpdateListener;\n\tprivate int mCurrentBufferPercentage;\n\tprivate long mSeekWhenPrepared; // recording the seek position while preparing\n\tprivate Context mContext;\n\tprivate Map<String, String> mHeaders;\n\tprivate int mBufSize;\n\tprivate OnCompletionListener mCompletionListener = new OnCompletionListener() {\n\t\tpublic void onCompletion(MediaPlayer mp) {\n      Log.d(\"onCompletion\");\n      mCurrentState = STATE_PLAYBACK_COMPLETED;\n      mTargetState = STATE_PLAYBACK_COMPLETED;\n      if (mMediaController != null)\n        mMediaController.hide();\n      if (mOnCompletionListener != null)\n        mOnCompletionListener.onCompletion(mMediaPlayer);\n    }\n  };\n  private OnErrorListener mErrorListener = new OnErrorListener() {\n    public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {\n      Log.d(\"Error: %d, %d\", framework_err, impl_err);\n      mCurrentState = STATE_ERROR;\n      mTargetState = STATE_ERROR;\n      if (mMediaController != null)\n        mMediaController.hide();\n\n      if (mOnErrorListener != null) {\n        if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err))\n          return true;\n      }\n\n      if (getWindowToken() != null) {\n        int message = framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK ? getResources().getIdentifier(\"VideoView_error_text_invalid_progressive_playback\", \"string\", mContext.getPackageName()): getResources().getIdentifier(\"VideoView_error_text_unknown\", \"string\", mContext.getPackageName());\n\n        new AlertDialog.Builder(mContext).setTitle(getResources().getIdentifier(\"VideoView_error_title\", \"string\", mContext.getPackageName())).setMessage(message).setPositiveButton(getResources().getIdentifier(\"VideoView_error_button\", \"string\", mContext.getPackageName()), new DialogInterface.OnClickListener() {\n          public void onClick(DialogInterface dialog, int whichButton) {\n            if (mOnCompletionListener != null)\n              mOnCompletionListener.onCompletion(mMediaPlayer);\n          }\n        }).setCancelable(false).show();\n      }\n      return true;\n    }\n  };\n  private OnBufferingUpdateListener mBufferingUpdateListener = new OnBufferingUpdateListener() {\n    public void onBufferingUpdate(MediaPlayer mp, int percent) {\n      mCurrentBufferPercentage = percent;\n      if (mOnBufferingUpdateListener != null)\n        mOnBufferingUpdateListener.onBufferingUpdate(mp, percent);\n    }\n  };\n  private OnInfoListener mInfoListener = new OnInfoListener() {\n    @Override\n    public boolean onInfo(MediaPlayer mp, int what, int extra) {\n      Log.d(\"onInfo: (%d, %d)\", what, extra);\n    \t\n      \tif(MediaPlayer.MEDIA_INFO_UNKNOW_TYPE == what){\n    \t\tLog.e(\" VITAMIO--TYPE_CHECK  stype  not include  onInfo mediaplayer unknow type \");\n    \t} \n    \t\n    \tif(MediaPlayer.MEDIA_INFO_FILE_OPEN_OK == what){\n    \t\tlong buffersize=mMediaPlayer.audioTrackInit(); \n    \t\tmMediaPlayer.audioInitedOk(buffersize);\n    \t}\n\n      Log.d(\"onInfo: (%d, %d)\", what, extra);\n\n      if (mOnInfoListener != null) {\n        mOnInfoListener.onInfo(mp, what, extra);\n      } else if (mMediaPlayer != null) {\n        if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) {\n          mMediaPlayer.pause();\n          if (mMediaBufferingIndicator != null)\n            mMediaBufferingIndicator.setVisibility(View.VISIBLE);\n        } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) {\n          mMediaPlayer.start();\n          if (mMediaBufferingIndicator != null)\n            mMediaBufferingIndicator.setVisibility(View.GONE);\n        }\n      }\n      return true;\n    }\n  };\n  private OnSeekCompleteListener mSeekCompleteListener = new OnSeekCompleteListener() {\n    @Override\n    public void onSeekComplete(MediaPlayer mp) {\n      Log.d(\"onSeekComplete\");\n      if (mOnSeekCompleteListener != null)\n        mOnSeekCompleteListener.onSeekComplete(mp);\n    }\n  };\n  private OnTimedTextListener mTimedTextListener = new OnTimedTextListener() {\n    @Override\n    public void onTimedTextUpdate(byte[] pixels, int width, int height) {\n      Log.i(\"onSubtitleUpdate: bitmap subtitle, %dx%d\", width, height);\n      if (mOnTimedTextListener != null)\n        mOnTimedTextListener.onTimedTextUpdate(pixels, width, height);\n    }\n\n    @Override\n    public void onTimedText(String text) {\n      Log.i(\"onSubtitleUpdate: %s\", text);\n      if (mOnTimedTextListener != null)\n        mOnTimedTextListener.onTimedText(text);\n    }\n  };\n\n  public VideoView(Context context) {\n    super(context);\n    initVideoView(context);\n  }\n\n  public VideoView(Context context, AttributeSet attrs) {\n    this(context, attrs, 0);\n    initVideoView(context);\n  }\n\n  public VideoView(Context context, AttributeSet attrs, int defStyle) {\n    super(context, attrs, defStyle);\n    initVideoView(context);\n  }\n\n  @Override\n  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n    int width = getDefaultSize(mVideoWidth, widthMeasureSpec);\n    int height = getDefaultSize(mVideoHeight, heightMeasureSpec);\n    setMeasuredDimension(width, height);\n  }\n\n\t/**\n\t * Set the display options\n\t *\n\t * @param layout      <ul>\n\t *                    <li>{@link #VIDEO_LAYOUT_ORIGIN}\n\t *                    <li>{@link #VIDEO_LAYOUT_SCALE}\n\t *                    <li>{@link #VIDEO_LAYOUT_STRETCH}\n\t *                    <li>{@link #VIDEO_LAYOUT_FIT_PARENT}\n\t *                    <li>{@link #VIDEO_LAYOUT_ZOOM}\n\t *                    </ul>\n\t * @param aspectRatio video aspect ratio, will audo detect if 0.\n\t */\n\tpublic void setVideoLayout(int layout, float aspectRatio) {\n\t\tLayoutParams lp = getLayoutParams();\n\t\tPair<Integer, Integer> res = ScreenResolution.getResolution(mContext);\n\t\tint windowWidth = res.first.intValue(), windowHeight = res.second.intValue();\n\t\tfloat windowRatio = windowWidth / (float) windowHeight;\n\t\tfloat videoRatio = aspectRatio <= 0.01f ? mVideoAspectRatio : aspectRatio;\n\t\tmSurfaceHeight = mVideoHeight;\n\t\tmSurfaceWidth = mVideoWidth;\n\t\tif (VIDEO_LAYOUT_ORIGIN == layout && mSurfaceWidth < windowWidth && mSurfaceHeight < windowHeight) {\n\t\t\tlp.width = (int) (mSurfaceHeight * videoRatio);\n\t\t\tlp.height = mSurfaceHeight;\n\t\t} else if (layout == VIDEO_LAYOUT_ZOOM) {\n\t\t\tlp.width = windowRatio > videoRatio ? windowWidth : (int) (videoRatio * windowHeight);\n\t\t\tlp.height = windowRatio < videoRatio ? windowHeight : (int) (windowWidth / videoRatio);\n\t\t} else if (layout == VIDEO_LAYOUT_FIT_PARENT) {\n\t\t\tViewGroup parent = (ViewGroup) getParent();\n\t\t\tfloat parentRatio = ((float) parent.getWidth()) / ((float) parent.getHeight());\n\t\t\tlp.width = (parentRatio < videoRatio) ? parent.getWidth() : Math.round(((float) parent.getHeight()) * videoRatio);\n\t\t\tlp.height = (parentRatio > videoRatio) ? parent.getHeight() : Math.round(((float) parent.getWidth()) / videoRatio);\n\t\t} else {\n\t\t\tboolean full = layout == VIDEO_LAYOUT_STRETCH;\n\t\t\tlp.width = (full || windowRatio < videoRatio) ? windowWidth : (int) (videoRatio * windowHeight);\n\t\t\tlp.height = (full || windowRatio > videoRatio) ? windowHeight : (int) (windowWidth / videoRatio);\n\t\t}\n\t\tsetLayoutParams(lp);\n\t\tgetHolder().setFixedSize(mSurfaceWidth, mSurfaceHeight);\n    Log.d(\"VIDEO: %dx%dx%f, Surface: %dx%d, LP: %dx%d, Window: %dx%dx%f\", mVideoWidth, mVideoHeight, mVideoAspectRatio, mSurfaceWidth, mSurfaceHeight, lp.width, lp.height, windowWidth, windowHeight, windowRatio);\n    mVideoLayout = layout;\n    mAspectRatio = aspectRatio;\n  }\n\n  @SuppressWarnings(\"deprecation\")\n  private void initVideoView(Context ctx) {\n    mContext = ctx;\n    mVideoWidth = 0;\n    mVideoHeight = 0;\n    getHolder().setFormat(PixelFormat.RGBA_8888); // PixelFormat.RGB_565\n    getHolder().addCallback(mSHCallback);\n    // this value only use Hardware decoder before Android 2.3\n    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && mHardwareDecoder) {\n      getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);\n    }\n    setFocusable(true);\n    setFocusableInTouchMode(true);\n    requestFocus();\n    mCurrentState = STATE_IDLE;\n    mTargetState = STATE_IDLE;\n    if (ctx instanceof Activity)\n      ((Activity) ctx).setVolumeControlStream(AudioManager.STREAM_MUSIC);\n  }\n\n  public boolean isValid() {\n    return (mSurfaceHolder != null && mSurfaceHolder.getSurface().isValid());\n  }\n \n  public void setVideoPath(String path) {\n    setVideoURI(Uri.parse(path));\n  }\n\n  public void setVideoURI(Uri uri) {\n    setVideoURI(uri, null);\n  }\n  \n  public void setVideoURI(Uri uri, Map<String, String> headers) {\n    mUri = uri;\n    mHeaders = headers;\n    mSeekWhenPrepared = 0;\n    openVideo();\n    requestLayout();\n    invalidate();\n  }\n\n  public void stopPlayback() {\n    if (mMediaPlayer != null) {\n      mMediaPlayer.stop();\n      mMediaPlayer.release();\n      mMediaPlayer = null;\n      mCurrentState = STATE_IDLE;\n      mTargetState = STATE_IDLE;\n    }\n  }\n  \n  private void openVideo() {\n//    if (mUri == null || mSurfaceHolder == null || !Vitamio.isInitialized(mContext))\n//      return;\n    if (mUri == null || mSurfaceHolder == null )\n    return;\n    Intent i = new Intent(\"com.android.music.musicservicecommand\");\n    i.putExtra(\"command\", \"pause\");\n    mContext.sendBroadcast(i);\n\n\t\trelease(false);\n\t\ttry {\n\t\t\tmDuration = -1;\n\t\t\tmCurrentBufferPercentage = 0;\n\t\t\tmMediaPlayer = new MediaPlayer(mContext, mHardwareDecoder);\n\t\t\tmMediaPlayer.setOnPreparedListener(mPreparedListener);\n\t\t\tmMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);\n\t\t\tmMediaPlayer.setOnCompletionListener(mCompletionListener);\n\t\t\tmMediaPlayer.setOnErrorListener(mErrorListener);\n\t\t\tmMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);\n\t\t\tmMediaPlayer.setOnInfoListener(mInfoListener);\n\t\t\tmMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener);\n\t\t\tmMediaPlayer.setOnTimedTextListener(mTimedTextListener);\n\t\t\t\n\t\t\tLog.d(\" set user optional --------  \");\n\t\t\tHashMap<String, String> options = new HashMap<String, String>();\n\t\t\toptions.put(\"rtsp_transport\", \"tcp\"); // udp\n\t\t//\toptions.put(\"user-agent\", \"userAgent\");\n\t\t//\toptions.put(\"cookies\", \"cookies\");\n\t\t\toptions.put(\"analyzeduration\", \"1000000\");\n\t\t\tmMediaPlayer.setDataSource(mContext, mUri, options);\n\n\t\t\tmMediaPlayer.setDisplay(mSurfaceHolder);\n\t\t\tmMediaPlayer.setBufferSize(mBufSize);\n\t\t\tmMediaPlayer.setVideoChroma(mVideoChroma == MediaPlayer.VIDEOCHROMA_RGB565 ? MediaPlayer.VIDEOCHROMA_RGB565 : MediaPlayer.VIDEOCHROMA_RGBA);\n\t\t\tmMediaPlayer.setScreenOnWhilePlaying(true);\n\t\t\tmMediaPlayer.prepareAsync();\n\t\t\tmCurrentState = STATE_PREPARING;\n\t\t\tattachMediaController();\n\t\t} catch (IOException ex) {\n\t\t\tLog.e(\"Unable to open content: \" + mUri, ex);\n\t\t\tmCurrentState = STATE_ERROR;\n\t\t\tmTargetState = STATE_ERROR;\n\t\t\tmErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);\n\t\t\treturn;\n\t\t} catch (IllegalArgumentException ex) {\n\t\t\tLog.e(\"Unable to open content: \" + mUri, ex);\n\t\t\tmCurrentState = STATE_ERROR;\n\t\t\tmTargetState = STATE_ERROR;\n\t\t\tmErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);\n\t\t\treturn;\n\t\t}\n\t}\n\n  public void setMediaController(MediaController controller) {\n    if (mMediaController != null)\n      mMediaController.hide();\n    mMediaController = controller;\n    attachMediaController();\n  }\n  \n  public void setMediaBufferingIndicator(View mediaBufferingIndicator) {\n    if (mMediaBufferingIndicator != null)\n      mMediaBufferingIndicator.setVisibility(View.GONE);\n    mMediaBufferingIndicator = mediaBufferingIndicator;\n  }\n\n  private void attachMediaController() {\n    if (mMediaPlayer != null && mMediaController != null) {\n      mMediaController.setMediaPlayer(this);\n      View anchorView = this.getParent() instanceof View ? (View) this.getParent() : this;\n      mMediaController.setAnchorView(anchorView);\n      mMediaController.setEnabled(isInPlaybackState());\n\n      if (mUri != null) {\n        List<String> paths = mUri.getPathSegments();\n        String name = paths == null || paths.isEmpty() ? \"null\" : paths.get(paths.size() - 1);\n        mMediaController.setFileName(name);\n      }\n    }\n  }\n\n  public void setOnPreparedListener(OnPreparedListener l) {\n    mOnPreparedListener = l;\n  }\n\n  public void setOnCompletionListener(OnCompletionListener l) {\n    mOnCompletionListener = l;\n  }\n\n  public void setOnErrorListener(OnErrorListener l) {\n    mOnErrorListener = l;\n  }\n\n  public void setOnBufferingUpdateListener(OnBufferingUpdateListener l) {\n    mOnBufferingUpdateListener = l;\n  }\n\n  public void setOnSeekCompleteListener(OnSeekCompleteListener l) {\n    mOnSeekCompleteListener = l;\n  }\n\n  public void setOnTimedTextListener(OnTimedTextListener l) {\n    mOnTimedTextListener = l;\n  }\n\n  public void setOnInfoListener(OnInfoListener l) {\n    mOnInfoListener = l;\n  }\n\n  private void release(boolean cleartargetstate) {\n    if (mMediaPlayer != null) {\n      mMediaPlayer.reset();\n      mMediaPlayer.release();\n      mMediaPlayer = null;\n      mCurrentState = STATE_IDLE;\n      if (cleartargetstate)\n        mTargetState = STATE_IDLE;\n    }\n  }\n\n  @Override\n  public boolean onTouchEvent(MotionEvent ev) {\n    if (isInPlaybackState() && mMediaController != null)\n      toggleMediaControlsVisiblity();\n    return false;\n  }\n\n  @Override\n  public boolean onTrackballEvent(MotionEvent ev) {\n    if (isInPlaybackState() && mMediaController != null)\n      toggleMediaControlsVisiblity();\n    return false;\n  }\n\n  @Override\n  public boolean onKeyDown(int keyCode, KeyEvent event) {\n    boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_MENU && keyCode != KeyEvent.KEYCODE_CALL && keyCode != KeyEvent.KEYCODE_ENDCALL;\n    if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {\n      if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE) {\n        if (mMediaPlayer.isPlaying()) {\n          pause();\n          mMediaController.show();\n        } else {\n          start();\n          mMediaController.hide();\n        }\n        return true;\n      } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {\n        if (!mMediaPlayer.isPlaying()) {\n            start();\n            mMediaController.hide();\n        }\n        return true;\n      } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {\n        if (mMediaPlayer.isPlaying()) {\n          pause();\n          mMediaController.show();\n        }\n        return true;\n      } else {\n        toggleMediaControlsVisiblity();\n      }\n    }\n\n    return super.onKeyDown(keyCode, event);\n  }\n\n  private void toggleMediaControlsVisiblity() {\n    if (mMediaController.isShowing()) {\n      mMediaController.hide();\n    } else {\n      mMediaController.show();\n    }\n  }\n\n  public void start() {\n    if (isInPlaybackState()) {\n      mMediaPlayer.start();\n      mCurrentState = STATE_PLAYING;\n    }\n    mTargetState = STATE_PLAYING;\n  }\n\n  public void pause() {\n    if (isInPlaybackState()) {\n      if (mMediaPlayer.isPlaying()) {\n        mMediaPlayer.pause();\n        mCurrentState = STATE_PAUSED;\n      }\n    }\n    mTargetState = STATE_PAUSED;\n  }\n\n  public void suspend() {\n    if (isInPlaybackState()) {\n      release(false);\n      mCurrentState = STATE_SUSPEND_UNSUPPORTED;\n      Log.d(\"Unable to suspend video. Release MediaPlayer.\");\n    }\n  }\n\n  public void resume() {\n    if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND) {\n      mTargetState = STATE_RESUME;\n    } else if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) {\n      openVideo();\n    }\n  }\n\n  public long getDuration() {\n    if (isInPlaybackState()) {\n      if (mDuration > 0)\n        return mDuration;\n      mDuration = mMediaPlayer.getDuration();\n      return mDuration;\n    }\n    mDuration = -1;\n    return mDuration;\n  }\n\n  public long getCurrentPosition() {\n    if (isInPlaybackState())\n      return mMediaPlayer.getCurrentPosition();\n    return 0;\n  }\n\n  public void seekTo(long msec) {\n    if (isInPlaybackState()) {\n      mMediaPlayer.seekTo(msec);\n      mSeekWhenPrepared = 0;\n    } else {\n      mSeekWhenPrepared = msec;\n    }\n  }\n\n  public boolean isPlaying() {\n    return isInPlaybackState() && mMediaPlayer.isPlaying();\n  }\n\n  public int getBufferPercentage() {\n    if (mMediaPlayer != null)\n      return mCurrentBufferPercentage;\n    return 0;\n  }\n\n  public void setVolume(float leftVolume, float rightVolume) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.setVolume(leftVolume, rightVolume);\n  }\n\n  public int getVideoWidth() {\n    return mVideoWidth;\n  }\n\n  public int getVideoHeight() {\n    return mVideoHeight;\n  }\n\n  public float getVideoAspectRatio() {\n    return mVideoAspectRatio;\n  }\n  \n  /**\n   * Must set before {@link #setVideoURI}\n   * @param chroma\n   */\n  public void setVideoChroma(int chroma) {\n    getHolder().setFormat(chroma == MediaPlayer.VIDEOCHROMA_RGB565 ? PixelFormat.RGB_565 : PixelFormat.RGBA_8888); // PixelFormat.RGB_565\n    mVideoChroma = chroma;\n  }\n  \n  public void setHardwareDecoder(boolean hardware) {\n    mHardwareDecoder= hardware;\n  }\n  \n  public void setVideoQuality(int quality) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.setVideoQuality(quality);\n  }\n  \n  public void setBufferSize(int bufSize) {\n    mBufSize = bufSize;\n  }\n\n  public boolean isBuffering() {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.isBuffering();\n    return false;\n  }\n\n  public String getMetaEncoding() {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.getMetaEncoding();\n    return null;\n  }\n\n  public void setMetaEncoding(String encoding) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.setMetaEncoding(encoding);\n  }\n\n  public SparseArray<MediaFormat> getAudioTrackMap(String encoding) {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.findTrackFromTrackInfo(TrackInfo.MEDIA_TRACK_TYPE_AUDIO, mMediaPlayer.getTrackInfo(encoding));\n    return null;\n  }\n\n  public int getAudioTrack() {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.getAudioTrack();\n    return -1;\n  }\n\n  public void setAudioTrack(int audioIndex) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.selectTrack(audioIndex);\n  }\n\n  public void setTimedTextShown(boolean shown) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.setTimedTextShown(shown);\n  }\n\n  public void setTimedTextEncoding(String encoding) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.setTimedTextEncoding(encoding);\n  }\n\n  public int getTimedTextLocation() {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.getTimedTextLocation();\n    return -1;\n  }\n\n  public void addTimedTextSource(String subPath) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.addTimedTextSource(subPath);\n  }\n\n  public String getTimedTextPath() {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.getTimedTextPath();\n    return null;\n  }\n\n  public void setSubTrack(int trackId) {\n    if (mMediaPlayer != null)\n      mMediaPlayer.selectTrack(trackId);\n  }\n\n  public int getTimedTextTrack() {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.getTimedTextTrack();\n    return -1;\n  }\n\n  public SparseArray<MediaFormat> getSubTrackMap(String encoding) {\n    if (mMediaPlayer != null)\n      return mMediaPlayer.findTrackFromTrackInfo(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT, mMediaPlayer.getTrackInfo(encoding));\n    return null;\n  }\n\n  protected boolean isInPlaybackState() {\n    return (mMediaPlayer != null && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING);\n  }\n}"
  },
  {
    "path": "vitamio-sample/.classpath",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<classpath>\n\t<classpathentry kind=\"con\" path=\"com.android.ide.eclipse.adt.ANDROID_FRAMEWORK\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.LIBRARIES\"/>\n\t<classpathentry exported=\"true\" kind=\"con\" path=\"com.android.ide.eclipse.adt.DEPENDENCIES\"/>\n\t<classpathentry kind=\"src\" path=\"src\"/>\n\t<classpathentry kind=\"src\" path=\"gen\"/>\n\t<classpathentry kind=\"output\" path=\"bin/classes\"/>\n</classpath>\n"
  },
  {
    "path": "vitamio-sample/.gitignore",
    "content": "/build\n/bin\n"
  },
  {
    "path": "vitamio-sample/.project",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<projectDescription>\n\t<name>VitamioListActivity</name>\n\t<comment></comment>\n\t<projects>\n\t</projects>\n\t<buildSpec>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>org.eclipse.jdt.core.javabuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t\t<buildCommand>\n\t\t\t<name>com.android.ide.eclipse.adt.ApkBuilder</name>\n\t\t\t<arguments>\n\t\t\t</arguments>\n\t\t</buildCommand>\n\t</buildSpec>\n\t<natures>\n\t\t<nature>com.android.ide.eclipse.adt.AndroidNature</nature>\n\t\t<nature>org.eclipse.jdt.core.javanature</nature>\n\t</natures>\n</projectDescription>\n"
  },
  {
    "path": "vitamio-sample/.settings/org.eclipse.jdt.core.prefs",
    "content": "eclipse.preferences.version=1\norg.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6\norg.eclipse.jdt.core.compiler.compliance=1.6\norg.eclipse.jdt.core.compiler.source=1.6\n"
  },
  {
    "path": "vitamio-sample/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"io.vov.vitamio.demo\"\n    android:versionCode=\"002\"\n    android:versionName=\"0.0.2\" >\n\n    <uses-sdk\n        android:minSdkVersion=\"7\"\n        android:targetSdkVersion=\"19\" />\n\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n\n    <application\n        android:allowBackup=\"false\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:label=\"@string/vitamio_demo_name\" >\n\n        <!-- Don't forgot InitActivity -->\n        <activity\n            android:name=\"io.vov.vitamio.activity.InitActivity\"\n            android:configChanges=\"orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@android:style/Theme.NoTitleBar\"\n            android:windowSoftInputMode=\"stateAlwaysHidden\" />\n        <activity android:name=\".VitamioListActivity\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".MediaPlayerDemo\"\n            android:label=\"Media/MediaPlayer\" >\n        </activity>\n        <activity\n            android:name=\".VideoViewDemo\"\n            android:configChanges=\"orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation\"\n            android:label=\"Media/VideoView\" >\n        </activity>\n        <activity\n            android:name=\".MediaPlayerDemo_Video\"\n            android:label=\"Media/MediaPlayer\" >\n        </activity>\n        <activity\n            android:name=\".MediaPlayerDemo_setSurface\"\n            android:label=\"Media/MediaPlayer\" >\n        </activity>\n        <activity\n            android:name=\".MediaPlayerDemo_Audio\"\n            android:label=\"Media/MediaPlayer\" >\n        </activity>\n        <activity\n            android:name=\".MediaMetadataRetrieverDemo\"\n            android:label=\"Media/MediaMetadata\" >\n        </activity>\n        <activity\n            android:name=\".MediaPlayerSubtitle\"\n            android:label=\"@string/title_activity_media_player_subtitle\" >\n        </activity>\n        <activity\n            android:name=\".VideoViewSubtitle\"\n            android:label=\"@string/title_activity_video_view_subtitle\" >\n        </activity>\n        <activity\n            android:name=\".VideoSubtitleList\"\n            android:label=\"@string/title_activity_video_subtitle_list\" >\n        </activity>\n        <activity\n            android:name=\".VideoViewBuffer\"\n            android:label=\"@string/title_activity_video_buffer\" >\n        </activity>\n         <meta-data\n            android:name=\"UMENG_APPKEY\"\n            android:value=\"563c244467e58e53e60005cc\" >\n        </meta-data>\n        <meta-data\n            android:name=\"UMENG_CHANNEL\"\n            android:value=\"demo\" >\n        </meta-data>\n    </application>\n\n</manifest>"
  },
  {
    "path": "vitamio-sample/README.md",
    "content": "VitamioDemo\n===========\n\nThe demo sample is a showcase of the functionality of Vitamio.\n\n"
  },
  {
    "path": "vitamio-sample/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\ndependencies {\n    compile project(':vitamio')\n    compile files('libs/umeng-analytics-v5.6.1.jar')\n}\n\nandroid {\n    compileSdkVersion 24\n    buildToolsVersion \"24.0.3\"\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 24\n    }\n\n    sourceSets {\n        main {\n            manifest.srcFile 'AndroidManifest.xml'\n            java.srcDirs = ['src']\n            res.srcDirs = ['res']\n        }\n    }\n}\n\n"
  },
  {
    "path": "vitamio-sample/gen/io/vov/vitamio/R.java",
    "content": "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n *\n * This class was automatically generated by the\n * aapt tool from the resource data it found.  It\n * should not be modified by hand.\n */\npackage io.vov.vitamio;\n\npublic final class R {\n\tpublic static final class color {\n\t\tpublic static final int mediacontroller_bg = 0x7f050002;\n\t\tpublic static final int mediacontroller_bg_pressed = 0x7f050001;\n\t\tpublic static final int transparent = 0x7f050000;\n\t}\n\tpublic static final class drawable {\n\t\tpublic static final int ic_launcher = 0x7f020000;\n\t\tpublic static final int mediacontroller_button = 0x7f020001;\n\t\tpublic static final int mediacontroller_pause = 0x7f020002;\n\t\tpublic static final int mediacontroller_play = 0x7f020003;\n\t\tpublic static final int scrubber_control_disabled_holo = 0x7f020008;\n\t\tpublic static final int scrubber_control_focused_holo = 0x7f020009;\n\t\tpublic static final int scrubber_control_normal_holo = 0x7f02000a;\n\t\tpublic static final int scrubber_control_pressed_holo = 0x7f02000b;\n\t\tpublic static final int scrubber_control_selector_holo = 0x7f02000c;\n\t\tpublic static final int scrubber_primary_holo = 0x7f02000d;\n\t\tpublic static final int scrubber_progress_horizontal_holo_dark = 0x7f02000e;\n\t\tpublic static final int scrubber_secondary_holo = 0x7f02000f;\n\t\tpublic static final int scrubber_track_holo_dark = 0x7f020010;\n\t}\n\tpublic static final class id {\n\t\tpublic static final int mediacontroller_file_name = 0x7f080005;\n\t\tpublic static final int mediacontroller_play_pause = 0x7f080001;\n\t\tpublic static final int mediacontroller_seekbar = 0x7f080004;\n\t\tpublic static final int mediacontroller_time_current = 0x7f080002;\n\t\tpublic static final int mediacontroller_time_total = 0x7f080003;\n\t}\n\tpublic static final class layout {\n\t\tpublic static final int mediacontroller = 0x7f030001;\n\t}\n\tpublic static final class string {\n\t\tpublic static final int VideoView_error_button = 0x7f06000b;\n\t\tpublic static final int VideoView_error_text_invalid_progressive_playback = 0x7f060009;\n\t\tpublic static final int VideoView_error_text_unknown = 0x7f06000a;\n\t\tpublic static final int VideoView_error_title = 0x7f060008;\n\t\tpublic static final int mediacontroller_play_pause = 0x7f06000c;\n\t\tpublic static final int permission_group_tools_description = 0x7f060003;\n\t\tpublic static final int permission_group_tools_label = 0x7f060002;\n\t\tpublic static final int permission_receive_messages_description = 0x7f060005;\n\t\tpublic static final int permission_receive_messages_label = 0x7f060004;\n\t\tpublic static final int permission_write_providers_description = 0x7f060007;\n\t\tpublic static final int permission_write_providers_label = 0x7f060006;\n\t\tpublic static final int vitamio_init_decoders = 0x7f060001;\n\t\tpublic static final int vitamio_library_app_name = 0x7f060000;\n\t}\n\tpublic static final class style {\n\t\tpublic static final int MediaController_SeekBar = 0x7f070000;\n\t\tpublic static final int MediaController_Text = 0x7f070001;\n\t}\n}\n"
  },
  {
    "path": "vitamio-sample/gen/io/vov/vitamio/demo/BuildConfig.java",
    "content": "/** Automatically generated file. DO NOT MODIFY */\npackage io.vov.vitamio.demo;\n\npublic final class BuildConfig {\n    public final static boolean DEBUG = true;\n}"
  },
  {
    "path": "vitamio-sample/gen/io/vov/vitamio/demo/R.java",
    "content": "/* AUTO-GENERATED FILE.  DO NOT MODIFY.\n *\n * This class was automatically generated by the\n * aapt tool from the resource data it found.  It\n * should not be modified by hand.\n */\n\npackage io.vov.vitamio.demo;\n\npublic final class R {\n    public static final class attr {\n    }\n    public static final class color {\n        public static final int mediacontroller_bg=0x7f050002;\n        public static final int mediacontroller_bg_pressed=0x7f050001;\n        public static final int transparent=0x7f050000;\n    }\n    public static final class drawable {\n        public static final int ic_launcher=0x7f020000;\n        public static final int mediacontroller_button=0x7f020001;\n        public static final int mediacontroller_pause=0x7f020002;\n        public static final int mediacontroller_play=0x7f020003;\n        public static final int mediacontroller_screen_fit=0x7f020004;\n        public static final int mediacontroller_screen_size=0x7f020005;\n        public static final int mediacontroller_sreen_size_100=0x7f020006;\n        public static final int mediacontroller_sreen_size_crop=0x7f020007;\n        public static final int scrubber_control_disabled_holo=0x7f020008;\n        public static final int scrubber_control_focused_holo=0x7f020009;\n        public static final int scrubber_control_normal_holo=0x7f02000a;\n        public static final int scrubber_control_pressed_holo=0x7f02000b;\n        public static final int scrubber_control_selector_holo=0x7f02000c;\n        public static final int scrubber_primary_holo=0x7f02000d;\n        public static final int scrubber_progress_horizontal_holo_dark=0x7f02000e;\n        public static final int scrubber_secondary_holo=0x7f02000f;\n        public static final int scrubber_track_holo_dark=0x7f020010;\n    }\n    public static final class id {\n        public static final int buffer=0x7f08000f;\n        public static final int button1=0x7f080015;\n        public static final int dd=0x7f08000c;\n        public static final int download_rate=0x7f080011;\n        public static final int load_rate=0x7f080012;\n        public static final int localaudio=0x7f080009;\n        public static final int localvideo=0x7f080006;\n        public static final int localvideo_setsurface=0x7f080007;\n        public static final int mediacontroller_file_name=0x7f080005;\n        public static final int mediacontroller_play_pause=0x7f080001;\n        public static final int mediacontroller_seekbar=0x7f080004;\n        public static final int mediacontroller_time_current=0x7f080002;\n        public static final int mediacontroller_time_total=0x7f080003;\n        public static final int probar=0x7f080010;\n        public static final int start=0x7f080014;\n        public static final int streamvideo=0x7f080008;\n        public static final int sub1=0x7f08000b;\n        public static final int subtitle_view=0x7f08000e;\n        public static final int surface=0x7f08000a;\n        public static final int surface_view=0x7f08000d;\n        public static final int textView=0x7f080000;\n        public static final int url=0x7f080013;\n    }\n    public static final class layout {\n        public static final int media_metadata=0x7f030000;\n        public static final int mediacontroller=0x7f030001;\n        public static final int mediaplayer_1=0x7f030002;\n        public static final int mediaplayer_2=0x7f030003;\n        public static final int mediaplayer_3=0x7f030004;\n        public static final int subtitle1=0x7f030005;\n        public static final int subtitle2=0x7f030006;\n        public static final int videobuffer=0x7f030007;\n        public static final int videoview=0x7f030008;\n    }\n    public static final class raw {\n        public static final int test_cbr=0x7f040000;\n    }\n    public static final class string {\n        public static final int VideoView_error_button=0x7f06000b;\n        public static final int VideoView_error_text_invalid_progressive_playback=0x7f060009;\n        public static final int VideoView_error_text_unknown=0x7f06000a;\n        public static final int VideoView_error_title=0x7f060008;\n        public static final int action_settings=0x7f060014;\n        public static final int local_audio=0x7f060011;\n        public static final int local_video=0x7f06000e;\n        public static final int local_video_setsurface=0x7f06000f;\n        public static final int mediacontroller_play_pause=0x7f06000c;\n        public static final int openvideo1=0x7f06001b;\n        public static final int openvideo2=0x7f06001c;\n        public static final int permission_group_tools_description=0x7f060003;\n        public static final int permission_group_tools_label=0x7f060002;\n        public static final int permission_receive_messages_description=0x7f060005;\n        public static final int permission_receive_messages_label=0x7f060004;\n        public static final int permission_write_providers_description=0x7f060007;\n        public static final int permission_write_providers_label=0x7f060006;\n        public static final int res_audio=0x7f060012;\n        public static final int stream_video=0x7f060010;\n        public static final int subtitle1=0x7f060015;\n        public static final int subtitle12=0x7f060016;\n        public static final int title_activity_media_player_subtitle=0x7f060017;\n        public static final int title_activity_video_buffer=0x7f06001a;\n        public static final int title_activity_video_subtitle_demo=0x7f060013;\n        public static final int title_activity_video_subtitle_list=0x7f060019;\n        public static final int title_activity_video_view_subtitle=0x7f060018;\n        public static final int vitamio_demo_name=0x7f06000d;\n        public static final int vitamio_init_decoders=0x7f060001;\n        public static final int vitamio_library_app_name=0x7f060000;\n    }\n    public static final class style {\n        public static final int MediaController_SeekBar=0x7f070000;\n        public static final int MediaController_Text=0x7f070001;\n        public static final int NoActionBar=0x7f070002;\n    }\n}\n"
  },
  {
    "path": "vitamio-sample/lint.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<lint>\n</lint>"
  },
  {
    "path": "vitamio-sample/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "vitamio-sample/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-19\nandroid.library.reference.1=../vitamio\n"
  },
  {
    "path": "vitamio-sample/res/layout/media_metadata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"horizontal\"\n    android:padding=\"10dp\" >\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Metadata: \" />\n\n    <TextView\n        android:id=\"@+id/textView\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n</LinearLayout>"
  },
  {
    "path": "vitamio-sample/res/layout/mediaplayer_1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <Button\n        android:id=\"@+id/localvideo\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/local_video\" />\n    \n    <Button\n        android:id=\"@+id/localvideo_setsurface\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/local_video_setsurface\" />\n\n    <Button\n        android:id=\"@+id/streamvideo\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/stream_video\" />\n\n    <Button\n        android:id=\"@+id/localaudio\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/local_audio\" />\n<!--\n\t\t<Button\n        android:id=\"@+id/resourcesaudio\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/res_audio\" />\n-->\n\n</LinearLayout>\n"
  },
  {
    "path": "vitamio-sample/res/layout/mediaplayer_2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <io.vov.vitamio.widget.CenterLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\" >\n\n        <SurfaceView\n            android:id=\"@+id/surface\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\" >\n        </SurfaceView>\n    </io.vov.vitamio.widget.CenterLayout>\n\n</LinearLayout>"
  },
  {
    "path": "vitamio-sample/res/layout/mediaplayer_3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <TextureView\n        android:id=\"@+id/surface\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\" >\n    </TextureView>\n\n</LinearLayout>"
  },
  {
    "path": "vitamio-sample/res/layout/subtitle1.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n \n    <SurfaceView android:id=\"@+id/surface\"\n\t\tandroid:layout_width=\"match_parent\" \n\t\tandroid:layout_height=\"match_parent\"\n\t\tandroid:layout_centerHorizontal=\"true\"\n\t\tandroid:layout_centerVertical=\"true\"\n\t\t  />\n    \n \t\t<TextView  android:id=\"@+id/sub1\"\n         android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/res_audio\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_centerInParent=\"true\"\n        />\n</RelativeLayout>"
  },
  {
    "path": "vitamio-sample/res/layout/subtitle2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <io.vov.vitamio.widget.CenterLayout\n        android:id=\"@+id/dd\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\" >\n\n        <io.vov.vitamio.widget.VideoView\n            android:id=\"@+id/surface_view\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_centerVertical=\"true\" />\n    </io.vov.vitamio.widget.CenterLayout>\n\n    <TextView\n        android:id=\"@+id/subtitle_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerInParent=\"true\"\n        android:text=\"@string/res_audio\" /> \n\n    <Button\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:layout_toRightOf=\"@id/sub1\"\n        android:background=\"@drawable/mediacontroller_sreen_size_100\"\n        android:onClick=\"changeLayout\" />\n\n</RelativeLayout>"
  },
  {
    "path": "vitamio-sample/res/layout/videobuffer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <io.vov.vitamio.widget.CenterLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\" >\n\n        <io.vov.vitamio.widget.VideoView\n            android:id=\"@+id/buffer\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:layout_centerHorizontal=\"true\"\n            android:layout_centerVertical=\"true\" />\n    </io.vov.vitamio.widget.CenterLayout>\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:orientation=\"horizontal\" >\n\n        <ProgressBar\n            android:id=\"@+id/probar\"\n            style=\"?android:attr/progressBarStyleLarge\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\" />\n\n        <TextView\n            android:id=\"@+id/download_rate\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:text=\"\" />\n\n        <TextView\n            android:id=\"@+id/load_rate\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:text=\"\" />\n    </LinearLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "vitamio-sample/res/layout/videoview.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\" >\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\" >\n\n        <EditText\n            android:id=\"@+id/url\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ems=\"12\"\n            android:hint=\"Please input url\" >\n        </EditText>\n\n        <Button\n            android:id=\"@+id/start\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:onClick=\"startPlay\"\n            android:text=\"Play\" />\n    </LinearLayout>\n\n    <io.vov.vitamio.widget.VideoView\n        android:id=\"@+id/surface_view\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\" />\n\n    <Button\n        android:id=\"@+id/button1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"openVideo\"\n        android:text=\"@string/openvideo1\" />\n\n\n</LinearLayout>"
  },
  {
    "path": "vitamio-sample/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"vitamio_demo_name\">VitamioDemo</string>\n\t<string name=\"local_video\">Play Video from Local File</string>\n\t<string name=\"local_video_setsurface\">Play Video use setSurface</string>\n    <string name=\"stream_video\">Play Streaming Video</string>\n    <string name=\"local_audio\">Play Audio from Local File</string>\n    <string name=\"res_audio\">Play Audio from Resources</string>\n    <string name=\"title_activity_video_subtitle_demo\">VideoSubtitleDemo</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"subtitle1\">MediaPlayer  Subtitles</string>\n    <string name=\"subtitle12\">VideoView Subtitles</string>\n    <string name=\"title_activity_media_player_subtitle\">MediaPlayerSubtitle</string>\n    <string name=\"title_activity_video_view_subtitle\">VideoViewSubtitle</string>\n    <string name=\"title_activity_video_subtitle_list\">VideoSubtitleList</string>\n    <string name=\"title_activity_video_buffer\">VideoViewBuffer</string>\n    <string name=\"openvideo1\">Open Video1</string>\n    <string name=\"openvideo2\">Open Video2</string>\n</resources>"
  },
  {
    "path": "vitamio-sample/res/values/styles.xml",
    "content": "<resources>\n\n    <style name=\"NoActionBar\" parent=\"android:Theme.Holo\">\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/MediaMetadataRetrieverDemo.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.demo;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport java.io.IOException;\n\nimport io.vov.vitamio.MediaMetadataRetriever;\n\npublic class MediaMetadataRetrieverDemo extends Activity {\n\n\tprivate String path = \"\";\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tio.vov.vitamio.MediaMetadataRetriever retriever = new io.vov.vitamio.MediaMetadataRetriever(this);\n\t\ttry {\n\t\t\tpath = \"\";\n\t\t\tif (path == \"\") {\n\t\t\t\t// Tell the user to provide an audio file URL.\n\t\t\t\tToast.makeText(MediaMetadataRetrieverDemo.this, \"Please edit MediaMetadataRetrieverDemo Activity, \" + \"and set the path variable to your audio file path.\" + \" Your audio file must be stored on sdcard.\", Toast.LENGTH_LONG).show();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tretriever.setDataSource(path);\n\t\t} catch (IllegalArgumentException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalStateException e) {\n\t\t\te.printStackTrace();\n\t\t} catch (IOException e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\n\t\tString duration=retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);\t\n\t\tlong durationMs = Long.parseLong(duration);\t\n\t\tString artist = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);\n\t\tString title = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);\t\n\t\tsetContentView(R.layout.media_metadata);\n\t\tTextView textView = (TextView)findViewById(R.id.textView);\n\t\ttextView.setText(durationMs + \"\" + artist + title);\t\n\t\tretriever.release();\n\t}\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/MediaPlayerDemo.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\n\npublic class MediaPlayerDemo extends Activity {\n\tprivate Button mlocalvideo;\n\tprivate Button mlocalvideoSurface;\n\tprivate Button mstreamvideo;\n\tprivate Button mlocalaudio;\n\tprivate Button mresourcesaudio;\n\tprivate static final String MEDIA = \"media\";\n\tprivate static final int LOCAL_AUDIO = 1;\n\tprivate static final int STREAM_AUDIO = 2;\n\tprivate static final int RESOURCES_AUDIO = 3;\n\tprivate static final int LOCAL_VIDEO = 4;\n\tprivate static final int STREAM_VIDEO = 5;\n\tprivate static final int RESOURCES_VIDEO = 6;\n\tprivate static final int LOCAL_VIDEO_SURFACE = 7;\n\n\t@Override\n\tprotected void onCreate(Bundle icicle) {\n\t\tsuper.onCreate(icicle);\n\t\tsetContentView(R.layout.mediaplayer_1);\n\t\tmlocalaudio = (Button) findViewById(R.id.localaudio);\n\t\tmlocalaudio.setOnClickListener(mLocalAudioListener);\n\t\t//mresourcesaudio = (Button) findViewById(R.id.resourcesaudio);\n\t\t//mresourcesaudio.setOnClickListener(mResourcesAudioListener);\n\n\t\tmlocalvideo = (Button) findViewById(R.id.localvideo);\n\t\tmlocalvideo.setOnClickListener(mLocalVideoListener);\n\t\tmlocalvideoSurface = (Button) findViewById(R.id.localvideo_setsurface);\n\t\tmlocalvideoSurface.setOnClickListener(mSetSurfaceVideoListener);\n\t\tmstreamvideo = (Button) findViewById(R.id.streamvideo);\n\t\tmstreamvideo.setOnClickListener(mStreamVideoListener);\n\t}\n\n\tprivate OnClickListener mLocalAudioListener = new OnClickListener() {\n\t\tpublic void onClick(View v) {\n\t\t\tIntent intent = new Intent(MediaPlayerDemo.this.getApplication(), MediaPlayerDemo_Audio.class);\n\t\t\tintent.putExtra(MEDIA, LOCAL_AUDIO);\n\t\t\tstartActivity(intent);\n\n\t\t}\n\t};\n\tprivate OnClickListener mResourcesAudioListener = new OnClickListener() {\n\t\tpublic void onClick(View v) {\n\t\t\tIntent intent = new Intent(MediaPlayerDemo.this.getApplication(), MediaPlayerDemo_Audio.class);\n\t\t\tintent.putExtra(MEDIA, RESOURCES_AUDIO);\n\t\t\tstartActivity(intent);\n\n\t\t}\n\t};\n\n\tprivate OnClickListener mLocalVideoListener = new OnClickListener() {\n\t\tpublic void onClick(View v) {\n\t\t\tIntent intent = new Intent(MediaPlayerDemo.this, MediaPlayerDemo_Video.class);\n\t\t\tintent.putExtra(MEDIA, LOCAL_VIDEO);\n\t\t\tstartActivity(intent);\n\n\t\t}\n\t};\n\tprivate OnClickListener mStreamVideoListener = new OnClickListener() {\n\t\tpublic void onClick(View v) {\n\t\t\tIntent intent = new Intent(MediaPlayerDemo.this, MediaPlayerDemo_Video.class);\n\t\t\tintent.putExtra(MEDIA, STREAM_VIDEO);\n\t\t\tstartActivity(intent);\n\n\t\t}\n\t};\n\t\n\tprivate OnClickListener mSetSurfaceVideoListener = new OnClickListener() {\n\t\tpublic void onClick(View v) {\n\t\t\tIntent intent = new Intent(MediaPlayerDemo.this, MediaPlayerDemo_setSurface.class);\n\t\t\tintent.putExtra(MEDIA, LOCAL_VIDEO_SURFACE);\n\t\t\tstartActivity(intent);\n\n\t\t}\n\t};\n\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/MediaPlayerDemo_Audio.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.res.AssetFileDescriptor;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport java.io.IOException;\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.Vitamio;\n\npublic class MediaPlayerDemo_Audio extends Activity {\n\n\tprivate static final String TAG = \"MediaPlayerDemo\";\n\tprivate MediaPlayer mMediaPlayer;\n\tprivate static final String MEDIA = \"media\";\n\tprivate static final int LOCAL_AUDIO = 1;\n\tprivate static final int STREAM_AUDIO = 2;\n\tprivate static final int RESOURCES_AUDIO = 3;\n\tprivate static final int LOCAL_VIDEO = 4;\n\tprivate static final int STREAM_VIDEO = 5;\n\tprivate String path;\n\n\tprivate TextView tx;\n\n\t@Override\n\tpublic void onCreate(Bundle icicle) {\n\t\tsuper.onCreate(icicle);\n\t\tVitamio.isInitialized(getApplicationContext());\n\t\ttx = new TextView(this);\n\t\tsetContentView(tx);\n\t\tBundle extras = getIntent().getExtras();\n\t\tplayAudio(extras.getInt(MEDIA));\n\t}\n\n\tprivate void playAudio(Integer media) {\n\t\ttry {\n\t\t\tswitch (media) {\n\t\t\tcase LOCAL_AUDIO:\n\t\t\t\t/**\n\t\t\t\t * TODO: Set the path variable to a local audio file path.\n\t\t\t\t */\n\t\t\t\tpath = \"\";\n\t\t\t\tif (path == \"\") {\n\t\t\t\t\t// Tell the user to provide an audio file URL.\n\t\t\t\t\tToast.makeText(MediaPlayerDemo_Audio.this, \"Please edit MediaPlayer_Audio Activity, \" + \"and set the path variable to your audio file path.\" + \" Your audio file must be stored on sdcard.\", Toast.LENGTH_LONG).show();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tmMediaPlayer = new MediaPlayer(this);\n\t\t\t\tmMediaPlayer.setDataSource(path);\n\t\t\t\tmMediaPlayer.prepare();\n\t\t\t\tmMediaPlayer.start();\n\t\t\t\tbreak;\n\t\t\tcase RESOURCES_AUDIO:\n\t\t\t\t/**\n\t\t\t\t * TODO: Upload a audio file to res/raw folder and provide its resid in\n\t\t\t\t * MediaPlayer.create() method.\n\t\t\t\t */\n\t\t\t\t//Bug need fixed\n\t\t\t\tmMediaPlayer = createMediaPlayer(this, R.raw.test_cbr);\n\t\t\t\tmMediaPlayer.start();\n\n\t\t\t}\n\t\t\ttx.setText(\"Playing audio...\");\n\n\t\t} catch (Exception e) {\n\t\t\tLog.e(TAG, \"error: \" + e.getMessage(), e);\n\t\t}\n\n\t}\n\n\tpublic MediaPlayer createMediaPlayer(Context context, int resid) {\n\t\ttry {\n\t\t\tAssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);\n\t\t\tMediaPlayer mp = new MediaPlayer(context);\n\t\t\tmp.setDataSource(afd.getFileDescriptor());\n\t\t\tafd.close();\n\t\t\tmp.prepare();\n\t\t\treturn mp;\n\t\t} catch (IOException ex) {\n\t\t\tLog.d(TAG, \"create failed:\", ex);\n\t\t\t// fall through\n\t\t} catch (IllegalArgumentException ex) {\n\t\t\tLog.d(TAG, \"create failed:\", ex);\n\t\t\t// fall through\n\t\t} catch (SecurityException ex) {\n\t\t\tLog.d(TAG, \"create failed:\", ex);\n\t\t\t// fall through\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\tif (mMediaPlayer != null) {\n\t\t\tmMediaPlayer.release();\n\t\t\tmMediaPlayer = null;\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/MediaPlayerDemo_Video.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\nimport android.app.Activity;\nimport android.graphics.PixelFormat;\nimport android.media.AudioManager;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceView;\nimport android.widget.Toast;\n\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;\nimport io.vov.vitamio.MediaPlayer.OnCompletionListener;\nimport io.vov.vitamio.MediaPlayer.OnPreparedListener;\nimport io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener;\nimport io.vov.vitamio.Vitamio;\n\npublic class MediaPlayerDemo_Video extends Activity implements OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback {\n\n\tprivate static final String TAG = \"MediaPlayerDemo\";\n\tprivate int mVideoWidth;\n\tprivate int mVideoHeight;\n\tprivate MediaPlayer mMediaPlayer;\n\tprivate SurfaceView mPreview;\n\tprivate SurfaceHolder holder;\n\tprivate String path;\n\tprivate Bundle extras;\n\tprivate static final String MEDIA = \"media\";\n\tprivate static final int LOCAL_AUDIO = 1;\n\tprivate static final int STREAM_AUDIO = 2;\n\tprivate static final int RESOURCES_AUDIO = 3;\n\tprivate static final int LOCAL_VIDEO = 4;\n\tprivate static final int STREAM_VIDEO = 5;\n\tprivate boolean mIsVideoSizeKnown = false;\n\tprivate boolean mIsVideoReadyToBePlayed = false;\n\n\t/**\n\t * \n\t * Called when the activity is first created.\n\t */\n\t@Override\n\tpublic void onCreate(Bundle icicle) {\n\t\tsuper.onCreate(icicle);\n\t\tVitamio.isInitialized(getApplicationContext());\n\t\tsetContentView(R.layout.mediaplayer_2);\n\t\tmPreview = (SurfaceView) findViewById(R.id.surface);\n\t\tholder = mPreview.getHolder();\n\t\tholder.addCallback(this);\n\t\tholder.setFormat(PixelFormat.RGBA_8888); \n\t\textras = getIntent().getExtras();\n\n\t}\n\n\tprivate void playVideo(Integer Media) {\n\t\tdoCleanUp();\n\t\ttry {\n\n\t\t\tswitch (Media) {\n\t\t\tcase LOCAL_VIDEO:\n\t\t\t\t/*\n\t\t\t\t * TODO: Set the path variable to a local media file path.\n\t\t\t\t */\n\t\t\t\tpath = \"\";\n\t\t\t\tif (path == \"\") {\n\t\t\t\t\t// Tell the user to provide a media file URL.\n\t\t\t\t\tToast.makeText(MediaPlayerDemo_Video.this, \"Please edit MediaPlayerDemo_Video Activity, \" + \"and set the path variable to your media file path.\" + \" Your media file must be stored on sdcard.\", Toast.LENGTH_LONG).show();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase STREAM_VIDEO:\n\t\t\t\t/*\n\t\t\t\t * TODO: Set path variable to progressive streamable mp4 or\n\t\t\t\t * 3gpp format URL. Http protocol should be used.\n\t\t\t\t * Mediaplayer can only play \"progressive streamable\n\t\t\t\t * contents\" which basically means: 1. the movie atom has to\n\t\t\t\t * precede all the media data atoms. 2. The clip has to be\n\t\t\t\t * reasonably interleaved.\n\t\t\t\t * \n\t\t\t\t */\n\t\t\t\tpath = \"\";\n\t\t\t\tif (path == \"\") {\n\t\t\t\t\t// Tell the user to provide a media file URL.\n\t\t\t\t\tToast.makeText(MediaPlayerDemo_Video.this, \"Please edit MediaPlayerDemo_Video Activity,\" + \" and set the path variable to your media file URL.\", Toast.LENGTH_LONG).show();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\n\t\t\t}\n\n\t\t\t// Create a new media player and set the listeners\n\t\t\tmMediaPlayer = new MediaPlayer(this);\n\t\t\tmMediaPlayer.setDataSource(path);\n\t\t\tmMediaPlayer.setDisplay(holder);\n\t\t\tmMediaPlayer.prepareAsync();\n\t\t\tmMediaPlayer.setOnBufferingUpdateListener(this);\n\t\t\tmMediaPlayer.setOnCompletionListener(this);\n\t\t\tmMediaPlayer.setOnPreparedListener(this);\n\t\t\tmMediaPlayer.setOnVideoSizeChangedListener(this);\n\t\t\tsetVolumeControlStream(AudioManager.STREAM_MUSIC);\n\n\t\t} catch (Exception e) {\n\t\t\tLog.e(TAG, \"error: \" + e.getMessage(), e);\n\t\t}\n\t}\n\n\tpublic void onBufferingUpdate(MediaPlayer arg0, int percent) {\n\t\t// Log.d(TAG, \"onBufferingUpdate percent:\" + percent);\n\n\t}\n\n\tpublic void onCompletion(MediaPlayer arg0) {\n\t\tLog.d(TAG, \"onCompletion called\");\n\t}\n\n\tpublic void onVideoSizeChanged(MediaPlayer mp, int width, int height) {\n\t\tLog.v(TAG, \"onVideoSizeChanged called\");\n\t\tif (width == 0 || height == 0) {\n\t\t\tLog.e(TAG, \"invalid video width(\" + width + \") or height(\" + height + \")\");\n\t\t\treturn;\n\t\t}\n\t\tmIsVideoSizeKnown = true;\n\t\tmVideoWidth = width;\n\t\tmVideoHeight = height;\n\t\tif (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {\n\t\t\tstartVideoPlayback();\n\t\t}\n\t}\n\n\tpublic void onPrepared(MediaPlayer mediaplayer) {\n\t\tLog.d(TAG, \"onPrepared called\");\n\t\tmIsVideoReadyToBePlayed = true;\n\t\tif (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {\n\t\t\tstartVideoPlayback();\n\t\t}\n\t}\n\n\tpublic void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) {\n\t\tLog.d(TAG, \"surfaceChanged called\");\n\n\t}\n\n\tpublic void surfaceDestroyed(SurfaceHolder surfaceholder) {\n\t\tLog.d(TAG, \"surfaceDestroyed called\");\n\t}\n\n\tpublic void surfaceCreated(SurfaceHolder holder) {\n\t\tLog.d(TAG, \"surfaceCreated called\");\n\t\tplayVideo(extras.getInt(MEDIA));\n\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\tsuper.onPause();\n\t\treleaseMediaPlayer();\n\t\tdoCleanUp();\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\treleaseMediaPlayer();\n\t\tdoCleanUp();\n\t}\n\n\tprivate void releaseMediaPlayer() {\n\t\tif (mMediaPlayer != null) {\n\t\t\tmMediaPlayer.release();\n\t\t\tmMediaPlayer = null;\n\t\t}\n\t}\n\n\tprivate void doCleanUp() {\n\t\tmVideoWidth = 0;\n\t\tmVideoHeight = 0;\n\t\tmIsVideoReadyToBePlayed = false;\n\t\tmIsVideoSizeKnown = false;\n\t}\n\n\tprivate void startVideoPlayback() {\n\t\tLog.v(TAG, \"startVideoPlayback\");\n\t\tholder.setFixedSize(mVideoWidth, mVideoHeight);\n\t\tmMediaPlayer.start();\n\t}\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/MediaPlayerDemo_setSurface.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.graphics.Matrix;\nimport android.graphics.SurfaceTexture;\nimport android.media.AudioManager;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.Surface;\nimport android.view.TextureView;\nimport android.widget.Toast;\n\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;\nimport io.vov.vitamio.MediaPlayer.OnCompletionListener;\nimport io.vov.vitamio.MediaPlayer.OnPreparedListener;\nimport io.vov.vitamio.Vitamio;\n\n@SuppressLint(\"NewApi\")\npublic class MediaPlayerDemo_setSurface extends Activity implements OnBufferingUpdateListener,\n    OnCompletionListener, OnPreparedListener, TextureView.SurfaceTextureListener {\n\n  private static final String TAG = \"MediaPlayerDemo\";\n  private int mVideoWidth;\n  private int mVideoHeight;\n  private MediaPlayer mMediaPlayer;\n  private TextureView mTextureView;\n  private String path;\n  private Surface surf;\n  \n  private boolean mIsVideoSizeKnown = false;\n  private boolean mIsVideoReadyToBePlayed = false;\n\n  /**\n   * \n   * Called when the activity is first created.\n   */\n  @Override\n  public void onCreate(Bundle icicle) {\n    super.onCreate(icicle);\n\tVitamio.isInitialized(getApplicationContext());\n    setContentView(R.layout.mediaplayer_3);\n    mTextureView = (TextureView) findViewById(R.id.surface);\n    mTextureView.setSurfaceTextureListener(this);\n\n  }\n\n  @SuppressLint(\"NewApi\")\n  private void playVideo(SurfaceTexture surfaceTexture) {\n    doCleanUp();\n    try {\n\n      path = \"\";\n      if (path == \"\") {\n        // Tell the user to provide a media file URL.\n        Toast.makeText(\n            MediaPlayerDemo_setSurface.this,\n            \"Please edit MediaPlayerDemo_setSurface Activity, \"\n                + \"and set the path variable to your media file path.\"\n                + \" Your media file must be stored on sdcard.\", Toast.LENGTH_LONG).show();\n        return;\n      }\n      // Create a new media player and set the listeners\n      mMediaPlayer = new MediaPlayer(this, true);\n      mMediaPlayer.setDataSource(path);\n      if (surf == null) {\n          surf = new Surface (surfaceTexture);\n      }\n      mMediaPlayer.setSurface(surf);\n      mMediaPlayer.prepareAsync();\n      mMediaPlayer.setOnBufferingUpdateListener(this);\n      mMediaPlayer.setOnCompletionListener(this);\n      mMediaPlayer.setOnPreparedListener(this);\n      setVolumeControlStream(AudioManager.STREAM_MUSIC);\n\n    } catch (Exception e) {\n      Log.e(TAG, \"error: \" + e.getMessage(), e);\n    }\n  }\n\n  public void onBufferingUpdate(MediaPlayer arg0, int percent) {\n    // Log.d(TAG, \"onBufferingUpdate percent:\" + percent);\n\n  }\n\n  public void onCompletion(MediaPlayer arg0) {\n    Log.d(TAG, \"onCompletion called\");\n  }\n\n  public void onPrepared(MediaPlayer mediaplayer) {\n    Log.d(TAG, \"onPrepared called\");\n    mIsVideoReadyToBePlayed = true;\n    if (mIsVideoReadyToBePlayed) {\n      startVideoPlayback();\n    }\n  }\n\n  @Override\n  protected void onPause() {\n    super.onPause();\n    releaseMediaPlayer();\n    doCleanUp();\n  }\n\n  @Override\n  protected void onDestroy() {\n    super.onDestroy();\n    releaseMediaPlayer();\n    doCleanUp();\n  }\n\n  private void releaseMediaPlayer() {\n    if (mMediaPlayer != null) {\n      mMediaPlayer.release();\n      mMediaPlayer = null;\n    }\n  }\n\n  private void doCleanUp() {\n    mVideoWidth = 0;\n    mVideoHeight = 0;\n    mIsVideoReadyToBePlayed = false;\n    mIsVideoSizeKnown = false;\n  }\n\n  private void startVideoPlayback() {\n    Log.v(TAG, \"startVideoPlayback\");\n    adjustAspectRatio(mMediaPlayer.getVideoWidth(), mMediaPlayer.getVideoHeight());\n    mMediaPlayer.start();\n  }\n\n  /**\n   * Sets the TextureView transform to preserve the aspect ratio of the video.\n   */\n  private void adjustAspectRatio(int videoWidth, int videoHeight) {\n    int viewWidth = mTextureView.getWidth();\n    int viewHeight = mTextureView.getHeight();\n    double aspectRatio = (double) videoHeight / videoWidth;\n\n    int newWidth, newHeight;\n    if (viewHeight > (int) (viewWidth * aspectRatio)) {\n      // limited by narrow width; restrict height\n      newWidth = viewWidth;\n      newHeight = (int) (viewWidth * aspectRatio);\n    } else {\n      // limited by short height; restrict width\n      newWidth = (int) (viewHeight / aspectRatio);\n      newHeight = viewHeight;\n    }\n    int xoff = (viewWidth - newWidth) / 2;\n    int yoff = (viewHeight - newHeight) / 2;\n    Log.v(TAG, \"video=\" + videoWidth + \"x\" + videoHeight + \" view=\" + viewWidth + \"x\" + viewHeight\n        + \" newView=\" + newWidth + \"x\" + newHeight + \" off=\" + xoff + \",\" + yoff);\n\n    Matrix txform = new Matrix();\n    mTextureView.getTransform(txform);\n    txform.setScale((float) newWidth / viewWidth, (float) newHeight / viewHeight);\n    //txform.postRotate(10);          // just for fun\n    txform.postTranslate(xoff, yoff);\n    mTextureView.setTransform(txform);\n  }\n\n  @Override\n  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {\n    playVideo(surface);\n  }\n\n  @Override\n  public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {\n\n  }\n\n  @Override\n  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {\n    return false;\n  }\n\n  @Override\n  public void onSurfaceTextureUpdated(SurfaceTexture surface) {\n\n  }\n\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/MediaPlayerSubtitle.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\nimport java.io.IOException;\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.MediaPlayer.OnPreparedListener;\nimport io.vov.vitamio.MediaPlayer.OnTimedTextListener;\nimport io.vov.vitamio.Vitamio;\n\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.app.Activity;\nimport android.graphics.PixelFormat;\nimport android.view.SurfaceHolder;\nimport android.view.SurfaceHolder.Callback;\nimport android.view.SurfaceView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\npublic class MediaPlayerSubtitle extends Activity implements Callback, OnPreparedListener, OnTimedTextListener {\n\n\tSurfaceView splayer;\n\tSurfaceHolder sholder;\n\tTextView tv;\n\tprivate MediaPlayer mediaPlayer;\n\tprivate String path = \"\";\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tVitamio.isInitialized(getApplicationContext());\n\t\tsetContentView(R.layout.subtitle1);\n\t\ttv = (TextView) findViewById(R.id.sub1);\n\t\tsplayer = (SurfaceView) findViewById(R.id.surface);\n\t\tsholder = splayer.getHolder();\n\t\tsholder.setFormat(PixelFormat.RGBA_8888);\n\t\tsholder.addCallback(this);\n\t}\n\n\tprivate void playVideo() {\n\t\ttry {\n\t\t\tif (path == \"\") {\n\t\t\t\t// Tell the user to provide an audio file URL.\n\t\t\t\tToast.makeText(MediaPlayerSubtitle.this, \"Please edit MediaPlayer Activity, \" + \"and set the path variable to your media file path.\" + \" Your media file must be stored on sdcard.\", Toast.LENGTH_LONG).show();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tmediaPlayer = new MediaPlayer(this);\n\t\t\tmediaPlayer.setDataSource(path);\n\t\t\tmediaPlayer.setDisplay(sholder);\n\t\t\tmediaPlayer.prepareAsync();\n\t\t\tmediaPlayer.setOnPreparedListener(this);\n\n\t\t\tmediaPlayer.setOnTimedTextListener(this);\n\n\t\t\t// TODO Auto-generated catch block\n\t\t} catch (IllegalArgumentException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (IllegalStateException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (SecurityException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t} catch (IOException e) {\n\t\t\t// TODO Auto-generated catch block\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\t@Override\n\tpublic void surfaceCreated(SurfaceHolder holder) {\n\t\t// TODO Auto-generated method stub\n\t\tplayVideo();\n\t}\n\n\t@Override\n\tpublic void surfaceDestroyed(SurfaceHolder holder) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n\tprivate void startVPback() {\n\n\t\tmediaPlayer.start();\n\t}\n\n\t@Override\n\tpublic void onPrepared(MediaPlayer arg0) {\n\n\t\t// TODO Auto-generated method stub\n\t\tstartVPback();\n\t\tmediaPlayer.addTimedTextSource(Environment.getExternalStorageDirectory() + \"/12.srt\");\n\t\tmediaPlayer.setTimedTextShown(true);\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\t// TODO Auto-generated method stub\n\t\tsuper.onPause();\n\t\trelaMediaPlay();\n\t}\n\n\tprivate void relaMediaPlay() {\n\t\t// TODO Auto-generated method stub\n\t\tif (mediaPlayer != null) {\n\t\t\tmediaPlayer.release();\n\t\t\tmediaPlayer = null;\n\t\t}\n\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\t// TODO Auto-generated method stub\n\t\tsuper.onDestroy();\n\t\trelaMediaPlay();\n\n\t}\n\n\t@Override\n\tpublic void onTimedText(String text) {\n\t\t// TODO Auto-generated method stub\n\t\ttv.setText(text);\n\t}\n\n\t@Override\n\tpublic void onTimedTextUpdate(byte[] pixels, int width, int height) {\n\t\t// TODO Auto-generated method stub\n\n\t}\n\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/VideoSubtitleList.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.demo;\n\nimport android.app.ListActivity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.ListView;\nimport android.widget.SimpleAdapter;\n\n\nimport io.vov.vitamio.Vitamio;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class VideoSubtitleList extends ListActivity {\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tVitamio.isInitialized(getApplicationContext());\n\t\tsetListAdapter(new SimpleAdapter(this, getData(), android.R.layout.simple_list_item_1, new String[] { \"title\" }, new int[] { android.R.id.text1 }));\n\t}\n\n\tprotected List<Map<String, Object>> getData() {\n\t\tList<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();\n\t\taddItem(myData, \"MediaPlayerSubtitle\", new Intent(this, MediaPlayerSubtitle.class));\n\t\taddItem(myData, \"VideoViewSubtitle\", new Intent(this, VideoViewSubtitle.class));\n\t\treturn myData;\n\t}\n\n\tprotected void addItem(List<Map<String, Object>> data, String name, Intent intent) {\n\t\tMap<String, Object> temp = new HashMap<String, Object>();\n\t\ttemp.put(\"title\", name);\n\t\ttemp.put(\"intent\", intent);\n\t\tdata.add(temp);\n\t}\n\n\t@Override\n\tprotected void onListItemClick(ListView l, View v, int position, long id) {\n\t\t@SuppressWarnings(\"unchecked\")\n    Map<String, Object> map = (Map<String, Object>) l.getItemAtPosition(position);\n\t\tIntent intent = (Intent) map.get(\"intent\");\n\t\tstartActivity(intent);\n\t}\n\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/VideoViewBuffer.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\nimport android.app.Activity;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;\nimport io.vov.vitamio.MediaPlayer.OnInfoListener;\nimport io.vov.vitamio.Vitamio;\nimport io.vov.vitamio.widget.MediaController;\nimport io.vov.vitamio.widget.VideoView;\n\npublic class VideoViewBuffer extends Activity implements OnInfoListener, OnBufferingUpdateListener {\n\n  /**\n   * TODO: Set the path variable to a streaming video URL or a local media file\n   * path.\n   */\n  private String path = \"http://vfx.mtime.cn/Video/2016/12/29/mp4/161229134943070513_480.mp4\";\n  private Uri uri;\n  private VideoView mVideoView;\n  private ProgressBar pb;\n  private TextView downloadRateView, loadRateView;\n\n  @Override\n  public void onCreate(Bundle icicle) {\n    super.onCreate(icicle);\n\t\tVitamio.isInitialized(getApplicationContext());\n\n    setContentView(R.layout.videobuffer);\n    mVideoView = (VideoView) findViewById(R.id.buffer);\n    pb = (ProgressBar) findViewById(R.id.probar);\n\n    downloadRateView = (TextView) findViewById(R.id.download_rate);\n    loadRateView = (TextView) findViewById(R.id.load_rate);\n    if (path == \"\") {\n      // Tell the user to provide a media file URL/path.\n      Toast.makeText(\n          VideoViewBuffer.this,\n          \"Please edit VideoBuffer Activity, and set path\"\n              + \" variable to your media file URL/path\", Toast.LENGTH_LONG).show();\n      return;\n    } else {\n      /*\n       * Alternatively,for streaming media you can use\n       * mVideoView.setVideoURI(Uri.parse(URLstring));\n       */\n      uri = Uri.parse(path);\n      mVideoView.setVideoURI(uri);\n      mVideoView.setMediaController(new MediaController(this));\n      mVideoView.requestFocus();\n      mVideoView.setOnInfoListener(this);\n      mVideoView.setOnBufferingUpdateListener(this);\n      mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {\n        @Override\n        public void onPrepared(MediaPlayer mediaPlayer) {\n          // optional need Vitamio 4.0\n          mediaPlayer.setPlaybackSpeed(2.0f);\n        }\n      });\n    }\n\n  }\n\n  @Override\n  public boolean onInfo(MediaPlayer mp, int what, int extra) {\n    switch (what) {\n    case MediaPlayer.MEDIA_INFO_BUFFERING_START:\n      if (mVideoView.isPlaying()) {\n        mVideoView.pause();\n        pb.setVisibility(View.VISIBLE);\n        downloadRateView.setText(\"\");\n        loadRateView.setText(\"\");\n        downloadRateView.setVisibility(View.VISIBLE);\n        loadRateView.setVisibility(View.VISIBLE);\n\n      }\n      break;\n    case MediaPlayer.MEDIA_INFO_BUFFERING_END:\n      mVideoView.start();\n      pb.setVisibility(View.GONE);\n      downloadRateView.setVisibility(View.GONE);\n      loadRateView.setVisibility(View.GONE);\n      break;\n    case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:\n      downloadRateView.setText(\"\" + extra + \"kb/s\" + \"  \");\n      break;\n    }\n    return true;\n  }\n\n  @Override\n  public void onBufferingUpdate(MediaPlayer mp, int percent) {\n    loadRateView.setText(percent + \"%\");\n  }\n\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/VideoViewDemo.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.Vitamio;\nimport io.vov.vitamio.widget.MediaController;\nimport io.vov.vitamio.widget.VideoView;\n\npublic class VideoViewDemo extends Activity {\n\n\t/**\n\t * TODO: Set the path variable to a streaming video URL or a local media file\n\t * path.\n\t */\n\n\tboolean ifUpdate;;\n\t\n\n\t@Override\n\tpublic void onCreate(Bundle icicle) {\n\t\tsuper.onCreate(icicle);\n\n\t\tVitamio.isInitialized(this);\n\t\t\n\t\tsetContentView(R.layout.videoview);\n\n\t\tplayfunction();\t\n\n\t}\n\n\t\n\tvoid playfunction(){\n\t\t String path = \"\";\n\t\t VideoView mVideoView;\n\t\t EditText mEditText;\n\t\tmEditText = (EditText) findViewById(R.id.url);\n\t\tmVideoView = (VideoView) findViewById(R.id.surface_view);\n\n\t\tpath=\"http://vfx.mtime.cn/Video/2016/12/29/mp4/161229134943070513_480.mp4\";\n      if (path == \"\") {\n\t\t\t// Tell the user to provide a media file URL/path.\n\t\t\tToast.makeText(VideoViewDemo.this, \"Please edit VideoViewDemo Activity, and set path\" + \" variable to your media file URL/path\", Toast.LENGTH_LONG).show();\n\t\t\treturn;\n\t\t} else {\n\t\t\t/*\n\t\t\t * Alternatively,for streaming media you can use\n\t\t\t * mVideoView.setVideoURI(Uri.parse(URLstring));\n\t\t\t */\n\t\t\tmVideoView.setVideoPath(path);\n\t\t\tmVideoView.setMediaController(new MediaController(this));\n\t\t\tmVideoView.requestFocus();\n\n\t\t\tmVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void onPrepared(MediaPlayer mediaPlayer) {\n\t\t\t\t\t// optional need Vitamio 4.0\n\t\t\t\t\tmediaPlayer.setPlaybackSpeed(1.0f);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\t\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/VideoViewSubtitle.java",
    "content": "/*\n * Copyright (C) 2013 yixia.com\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.vov.vitamio.demo;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport io.vov.vitamio.MediaPlayer;\nimport io.vov.vitamio.MediaPlayer.OnTimedTextListener;\nimport io.vov.vitamio.Vitamio;\nimport io.vov.vitamio.widget.VideoView;\n\npublic class VideoViewSubtitle extends Activity {\n\n\tprivate String path = \"http://vfx.mtime.cn/Video/2016/12/29/mp4/161229134943070513_480.mp4\";\n\tprivate String subtitle_path = \"\";\n\tprivate VideoView mVideoView;\n\tprivate TextView mSubtitleView;\n\tprivate long mPosition = 0;\n\tprivate int mVideoLayout = 0;\n\n\t@Override\n\tpublic void onCreate(Bundle icicle) {\n\t\tsuper.onCreate(icicle);\n\t\tVitamio.isInitialized(getApplicationContext());\n\t\tsetContentView(R.layout.subtitle2);\n\t\tmVideoView = (VideoView) findViewById(R.id.surface_view);\n\t\tmSubtitleView = (TextView) findViewById(R.id.subtitle_view);\n\n\t\tif (path == \"\") {\n\t\t\t// Tell the user to provide a media file URL/path.\n\t\t\tToast.makeText(VideoViewSubtitle.this, \"Please edit VideoViewSubtitle Activity, and set path\" + \" variable to your media file URL/path\", Toast.LENGTH_LONG).show();\n\t\t\treturn;\n\t\t} else {\n\t\t\t/*\n\t\t\t * Alternatively,for streaming media you can use\n\t\t\t * mVideoView.setVideoURI(Uri.parse(URLstring));\n\t\t\t */\n\t\t\tmVideoView.setVideoPath(path);\n\n\t\t\t// mVideoView.setMediaController(new MediaController(this));\n\t\t\tmVideoView.requestFocus();\n\n\t\t\tmVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void onPrepared(MediaPlayer mediaPlayer) {\n\t\t\t\t\t// optional need Vitamio 4.0\n\t\t\t\t\tmediaPlayer.setPlaybackSpeed(1.0f);\n\t\t\t\t\tmVideoView.addTimedTextSource(subtitle_path);\n\t\t\t\t\tmVideoView.setTimedTextShown(true);\n\n\t\t\t\t}\n\t\t\t});\n\t\t\tmVideoView.setOnTimedTextListener(new OnTimedTextListener() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onTimedText(String text) {\n\t\t\t\t\tmSubtitleView.setText(text);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onTimedTextUpdate(byte[] pixels, int width, int height) {\n\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\tmPosition = mVideoView.getCurrentPosition();\n\t\tmVideoView.stopPlayback();\n\t\tsuper.onPause();\n\t}\n\n\t@Override\n\tprotected void onResume() {\n\t\tif (mPosition > 0) {\n\t\t\tmVideoView.seekTo(mPosition);\n\t\t\tmPosition = 0;\n\t\t}\n\t\tsuper.onResume();\n\t\tmVideoView.start();\n\t}\n\n\tpublic void changeLayout(View view) {\n\t\tmVideoLayout++;\n\t\tif (mVideoLayout == 4) {\n\t\t\tmVideoLayout = 0;\n\t\t}\n\t\tswitch (mVideoLayout) {\n\t\tcase 0:\n\t\t\tmVideoLayout = VideoView.VIDEO_LAYOUT_ORIGIN;\n\t\t\tview.setBackgroundResource(R.drawable.mediacontroller_sreen_size_100);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tmVideoLayout = VideoView.VIDEO_LAYOUT_SCALE;\n\t\t\tview.setBackgroundResource(R.drawable.mediacontroller_screen_fit);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tmVideoLayout = VideoView.VIDEO_LAYOUT_STRETCH;\n\t\t\tview.setBackgroundResource(R.drawable.mediacontroller_screen_size);\n\t\t\tbreak;\n\t\tcase 3:\n\t\t\tmVideoLayout = VideoView.VIDEO_LAYOUT_ZOOM;\n\t\t\tview.setBackgroundResource(R.drawable.mediacontroller_sreen_size_crop);\n\n\t\t\tbreak;\n\t\t}\n\t\tmVideoView.setVideoLayout(mVideoLayout, 0);\n\t}\n\n}\n"
  },
  {
    "path": "vitamio-sample/src/io/vov/vitamio/demo/VitamioListActivity.java",
    "content": "/*\n * Copyright (C) 2013 YIXIA.COM\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.vov.vitamio.demo;\n\nimport android.app.ListActivity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.ListView;\nimport android.widget.SimpleAdapter;\n\n\nimport io.vov.vitamio.Vitamio;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.umeng.analytics.MobclickAgent;\n\n/**\n * List\n */\npublic class VitamioListActivity extends ListActivity {\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tVitamio.isInitialized(getApplicationContext());\n\n\t\tsetListAdapter(new SimpleAdapter(this, getData(), android.R.layout.simple_list_item_1, new String[] { \"title\" }, new int[] { android.R.id.text1 }));\n\t}\n\n\tprotected List<Map<String, Object>> getData() {\n\t\tList<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();\n\t\taddItem(myData, \"MediaPlayer\", new Intent(this, MediaPlayerDemo.class));\n\t\taddItem(myData, \"VideoView\", new Intent(this, VideoViewDemo.class));\n\t\taddItem(myData, \"MediaMetadata\", new Intent(this, MediaMetadataRetrieverDemo.class));\n\t\taddItem(myData, \"VideoSubtitle\", new Intent(this, VideoSubtitleList.class));\n\t\taddItem(myData, \"VideoViewBuffer\", new Intent(this, VideoViewBuffer.class));\n\t\treturn myData;\n\t}\n\n\tprotected void addItem(List<Map<String, Object>> data, String name, Intent intent) {\n\t\tMap<String, Object> temp = new HashMap<String, Object>();\n\t\ttemp.put(\"title\", name);\n\t\ttemp.put(\"intent\", intent);\n\t\tdata.add(temp);\n\t}\n\n\t@SuppressWarnings(\"unchecked\")\n\t@Override\n\tprotected void onListItemClick(ListView l, View v, int position, long id) {\n\t\tMap<String, Object> map = (Map<String, Object>) l.getItemAtPosition(position);\n\t\tIntent intent = (Intent) map.get(\"intent\");\n\t\tstartActivity(intent);\n\t}\n\t\n\t@Override\n\tprotected void onResume() {\n\t MobclickAgent.onResume(this);\n\t  super.onResume();\n\t}\n\t\n\t@Override\n\tprotected void onPause() {\n\t\tMobclickAgent.onPause(this);\n\t  super.onPause();\n\t}\n\n}\n"
  },
  {
    "path": "voicedialog/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "voicedialog/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 22\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"com.atguigu.voicedialog\"\n        minSdkVersion 14\n        targetSdkVersion 22\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    testCompile 'junit:junit:4.12'\n    compile 'com.android.support:appcompat-v7:22.2.1'\n    compile files('libs/Msc.jar')\n    compile 'com.google.code.gson:gson:2.2.4'\n    compile files('libs/Sunflower.jar')\n}\n"
  },
  {
    "path": "voicedialog/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in C:\\Android_studio_sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "voicedialog/src/androidTest/java/com/atguigu/voicedialog/ApplicationTest.java",
    "content": "package com.atguigu.voicedialog;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "voicedialog/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.atguigu.voicedialog\">\n\n    <uses-permission android:name=\"android.permission.CALL_PHONE\"/>\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.READ_CONTACTS\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n\n    <application\n        android:name=\".MyApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "voicedialog/src/main/java/com/atguigu/voicedialog/ConversationInfo.java",
    "content": "package com.atguigu.voicedialog;\n\n/**\n * 谈话信息\n *\n */\npublic class ConversationInfo {\n    private String askerText; // 提问者数据\n    private String answerText; // 回答者数据\n    private int imageID; // 回答者的数据, 可选\n    private boolean isAsker; // 当前是否是提问者\n\n\n    public String getAskerText() {\n        return askerText;\n    }\n\n    public void setAskerText(String askerText) {\n        this.askerText = askerText;\n    }\n\n    public String getAnswerText() {\n        return answerText;\n    }\n\n    public void setAnswerText(String answerText) {\n        this.answerText = answerText;\n    }\n\n    public int getImageID() {\n        return imageID;\n    }\n\n    public void setImageID(int imageID) {\n        this.imageID = imageID;\n    }\n\n    public boolean isAsker() {\n        return isAsker;\n    }\n\n    public void setAsker(boolean isAsker) {\n        this.isAsker = isAsker;\n    }\n\n    public ConversationInfo() {\n        super();\n    }\n\n    public ConversationInfo(String askerText, String answerText, int imageID,\n                            boolean isAsker) {\n        super();\n        this.askerText = askerText;\n        this.answerText = answerText;\n        this.imageID = imageID;\n        this.isAsker = isAsker;\n    }\n\n    @Override\n    public String toString() {\n        return \"ConversationInfo [askerText=\" + askerText + \", answerText=\"\n                + answerText + \", imageID=\" + imageID + \", isAsker=\" + isAsker\n                + \"]\";\n    }\n}\n"
  },
  {
    "path": "voicedialog/src/main/java/com/atguigu/voicedialog/MainActivity.java",
    "content": "package com.atguigu.voicedialog;\n\nimport android.app.Activity;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.provider.MediaStore;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.google.gson.Gson;\nimport com.iflytek.cloud.RecognizerResult;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.ui.RecognizerDialogListener;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\n\n\n\npublic class MainActivity extends Activity implements View.OnClickListener {\n\n    private SpeechUtils mSpeechUtils;\n    private List<ConversationInfo> conversationList;\n    private MyAdapter mAdapter;\n    private ListView mListView;\n    private Button btnStartListen;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        initView();\n        mSpeechUtils = SpeechUtils.getInstance(this);\n    }\n\n    private void initView() {\n        setContentView(R.layout.activity_main);\n        mListView = (ListView) findViewById(R.id.lv_conversation);\n        btnStartListen = (Button) findViewById(R.id.btn_start_listen);\n        btnStartListen.setOnClickListener(this);\n        conversationList = new ArrayList<>();\n        mAdapter = new MyAdapter();\n        mListView.setAdapter(mAdapter);\n    }\n\n    @Override\n    public void onClick(View v) {\n\n        mSpeechUtils.showListenVoiceDialog(this, new MyRecognizerDialogListener());\n    }\n\n    class MyRecognizerDialogListener implements RecognizerDialogListener{\n\n\n        StringBuffer sb = new StringBuffer();\n\n        @Override\n        public void onResult(RecognizerResult recognizerResult, boolean isLast) {\n\n            //语音识别过来的内容\n            String resultString = recognizerResult.getResultString();\n            sb.append(getVoice(resultString));//添加成一整句\n\n            System.out.println(\"正在识别中: \" + sb.toString());\n            String askerText = \"你说什么，我没听见...\";\n            String answer;\n            int imageID = -1;\n            if (isLast) { // true, 所有的数据都解析完闭. 赋值给askerText\n                askerText = sb.toString();\n                answer = sb.toString();\n                if (askerText.contains(\"美女\")) {\n                    Random random = new Random();\n                    int index = random.nextInt(3);\n                    answer = ResouesUtils.mmTextArray[index];\n                    imageID = ResouesUtils.mmImageArray[index];\n                } else if (askerText.contains(\"精忠报国\")) {\n                    answer = ResouesUtils.markTextArray[0];\n                    imageID = ResouesUtils.markImageArray[0];\n                }else if (askerText.contains(\"奶茶妹\")) {\n                    answer = ResouesUtils.markTextArray[2];\n                    imageID = ResouesUtils.mmImageArray[4];\n                }else if (askerText.contains(\"开会\")) {\n                    answer = ResouesUtils.markTextArray[1];\n                    imageID = ResouesUtils.markImageArray[1];\n                } else if (askerText.contains(\"报警\")) {\n                    String phoneNumber = \"110\";\n                    Intent intentPhone = new Intent(Intent.ACTION_CALL, Uri.parse(\"tel:\" + phoneNumber));\n                    startActivity(intentPhone);\n                    return;\n                }else if(askerText.contains(\"120\")) {\n                    String phoneNumber = \"120\";\n                    Intent intentPhone = new Intent(Intent.ACTION_CALL, Uri.parse(\"tel:\" + phoneNumber));\n                    startActivity(intentPhone);\n                    return;\n                } else if (askerText.contains(\"打开微信\")) {\n                    Intent intent = new Intent();\n                    ComponentName cmp = new ComponentName(\"com.tencent.mm\", \"com.tencent.mm.ui.LauncherUI\");\n                    intent.setAction(Intent.ACTION_MAIN);\n                    intent.addCategory(Intent.CATEGORY_LAUNCHER);\n                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                    intent.setComponent(cmp);\n                    startActivity(intent);\n                    return;\n                } else if (askerText.contains(\"拍照\")) {\n                    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//调用android自带的照相机\n//                   photoUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n                    startActivity(intent);\n                    return;\n                }else if(askerText.contains(\"名字\")) {\n                    answer = ResouesUtils.mmTextArray[3];\n                }else if(askerText.contains(\"我在哪儿\")) {\n                    answer = ResouesUtils.mmTextArray[4];\n                }else if(askerText.contains(\"班长是谁\")) {\n                    answer = ResouesUtils.mmTextArray[5];\n                }else if(askerText.contains(\"老师是谁\")) {\n                    answer = ResouesUtils.mmTextArray[6];\n                    imageID = ResouesUtils.mmImageArray[3];\n                }else if(askerText.contains(\"你会做什么\")) {\n                    answer = ResouesUtils.mmTextArray[7];\n                }else if(askerText.contains(\"爱你\")) {\n                    answer = ResouesUtils.mmTextArray[8];\n                }\n                sb = new StringBuffer();\n            } else { // 还没有把数据解析完毕, return回去, 不去执行后面的代码, 继续下一次拼接.\n                return;\n            }\n\n            ConversationInfo info = new ConversationInfo(askerText, null, -1, true);\n            conversationList.add(info);\n            mAdapter.notifyDataSetChanged();\n\n            // 准备回答的数据.\n            info = new ConversationInfo(null, answer, imageID, false);\n            conversationList.add(info);\n\n            mAdapter.notifyDataSetChanged();\n            mListView.setSelection(conversationList.size());\n            // 把answer说出来\n            mSpeechUtils.speakText(MainActivity.this, answer);\n        }\n\n        @Override\n        public void onError(SpeechError speechError) {\n            Toast.makeText(MainActivity.this, \"识别出错了\", Toast.LENGTH_SHORT).show();\n        }\n    }\n\n    /**\n     * 解析json\n     * @param resultString\n     * @return\n     */\n    private String getVoice(String resultString) {\n        Gson gson = new Gson();\n        SpeechBean voice = gson.fromJson(resultString, SpeechBean.class);\n        List<SpeechBean.WS> ws = voice.ws;\n        StringBuffer sb = new StringBuffer();\n        for (SpeechBean.WS wsBean : ws) {\n            String str = wsBean.cw.get(0).w;\n            sb.append(str);\n        }\n        return sb.toString();\n    }\n\n    class MyAdapter extends BaseAdapter {\n\n        @Override\n        public int getCount() {\n            return conversationList.size();\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            View view = null;\n            if(convertView == null) {\n                view = View.inflate(MainActivity.this, R.layout.listview_item, null);\n            } else {\n                view = convertView;\n            }\n\n            View answerView = view.findViewById(R.id.ll_answer);\n            TextView answerText = (TextView) view.findViewById(R.id.tv_answer_text);\n            ImageView answerImage = (ImageView) view.findViewById(R.id.iv_answer_image);\n            TextView askerText = (TextView) view.findViewById(R.id.tv_asker_text);\n\n            ConversationInfo info = conversationList.get(position);\n            if(info.isAsker()) {\n                // 当前是提问者, 隐藏回答者布局\n                answerView.setVisibility(View.GONE);\n                askerText.setVisibility(View.VISIBLE);\n                askerText.setText(info.getAskerText());\n            } else {\n                // 当前是回答者, 隐藏提问者布局\n                answerView.setVisibility(View.VISIBLE);\n                askerText.setVisibility(View.GONE);\n\n                answerText.setText(info.getAnswerText());\n                if(info.getImageID() == -1) {\n                    answerImage.setVisibility(View.GONE);\n                } else {\n                    answerImage.setVisibility(View.VISIBLE);\n                    answerImage.setImageResource(info.getImageID());\n                }\n            }\n            return view;\n        }\n\n        @Override\n        public Object getItem(int position) {\n            return null;\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return 0;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "voicedialog/src/main/java/com/atguigu/voicedialog/MyApplication.java",
    "content": "package com.atguigu.voicedialog;\n\nimport android.app.Application;\n\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechUtility;\n\n/**\n * 作者：杨光福 on 2016/3/24 23:23\n * 微信：yangguangfu520\n * QQ号：541433511\n * 作用：代表整个应用\n */\npublic class MyApplication extends Application {\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        //将“12345678”替换成您申请的APPID，申请地址：http://open.voicecloud.cn\n        SpeechUtility.createUtility(this, SpeechConstant.APPID + \"=56f4c1dd\");\n    }\n}\n"
  },
  {
    "path": "voicedialog/src/main/java/com/atguigu/voicedialog/ResouesUtils.java",
    "content": "package com.atguigu.voicedialog;\n\npublic class ResouesUtils {\n    public static String[] mmTextArray = {\n            \"这个怎么样?\",\n            \"色狼, 给你看.\",\n            \"我的最爱\",\n            \"我是客服妹妹\",\n            \"你正在尚硅谷安卓1020班\",\n            \"班长李岩\",\n            \"阿福老师\",\n            \"我能陪主人聊天\",\n            \"谢谢，我也爱你哦\"\n    };\n\n    public static String[] markTextArray = {\n            \"精终报国，还是精忠报国？\",\n            \"你们这是开什么会呀？这是\",\n            \"是这个吗？\"\n    };\n\n    public static int[] mmImageArray = {\n            R.drawable.p3,\n            R.drawable.p4,\n            R.drawable.p2,\n            R.drawable.p5,\n            R.drawable.p7\n    };\n\n    public static int[] markImageArray = {\n            R.drawable.p1,\n            R.drawable.p6,\n    };\n}\n"
  },
  {
    "path": "voicedialog/src/main/java/com/atguigu/voicedialog/SpeechBean.java",
    "content": "package com.atguigu.voicedialog;\n\nimport java.util.List;\n\npublic class SpeechBean {\n\n    public String bg;\n    public String ed;\n    public String ls;\n    public String sn;\n    public List<WS> ws;\n\n    public class WS {\n\n        public String bg;\n        public List<CW> cw;\n\n    }\n\n    public class CW {\n\n        public String sc;\n        public String w;\n    }\n}\n"
  },
  {
    "path": "voicedialog/src/main/java/com/atguigu/voicedialog/SpeechUtils.java",
    "content": "package com.atguigu.voicedialog;\n\nimport android.content.Context;\nimport android.os.Bundle;\n\nimport com.iflytek.cloud.InitListener;\nimport com.iflytek.cloud.LexiconListener;\nimport com.iflytek.cloud.SpeechConstant;\nimport com.iflytek.cloud.SpeechError;\nimport com.iflytek.cloud.SpeechRecognizer;\nimport com.iflytek.cloud.SpeechSynthesizer;\nimport com.iflytek.cloud.SynthesizerListener;\nimport com.iflytek.cloud.ui.RecognizerDialog;\nimport com.iflytek.cloud.ui.RecognizerDialogListener;\n\npublic final class SpeechUtils {\n\n    private static SpeechUtils instance;\n\n    private SpeechRecognizer mIat;\n\n    private LexiconListener lexiconListener;\n\n    private SpeechUtils(Context context) {\n        //1.创建SpeechRecognizer对象，第二个参数： 本地听写时传InitListener\n        mIat = SpeechRecognizer.createRecognizer(context, null);\n        //2.设置听写参数，详见《科大讯飞MSC API手册(Android)》 SpeechConstant类\n        mIat.setParameter(SpeechConstant.DOMAIN, \"iat\");\n        mIat.setParameter(SpeechConstant.LANGUAGE, \"zh_cn\");\n        mIat.setParameter(SpeechConstant.ACCENT, \"mandarin \");\n\n    }\n\n\n    public SpeechRecognizer getmIat() {\n        return mIat;\n    }\n\n    public static SpeechUtils getInstance(Context context) {\n\n        if (instance == null) {\n            synchronized (SpeechUtils.class) {\n                if (instance == null) {\n                    instance = new SpeechUtils(context);\n                }\n            }\n        }\n        return instance;\n    }\n\n\n    /**\n     * 把给定的文本内容读出来\n     *\n     * @param context\n     * @param text\n     */\n    public void speakText(Context context, String text) {\n        //1.创建SpeechSynthesizer对象, 第二个参数：本地合成时传InitListener\n        SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer(context, null);\n        //2.合成参数设置，详见《科大讯飞MSC API手册(Android)》SpeechSynthesizer 类\n        mTts.setParameter(SpeechConstant.VOICE_NAME, \"vixl\");//设置发音人\n        mTts.setParameter(SpeechConstant.SPEED, \"70\");//设置语速\n        mTts.setParameter(SpeechConstant.VOLUME, \"80\"); //设置音量，范围0~100\n        //设置合成音频保存位置（可自定义保存位置），保存在“./sdcard/iflytek.pcm”\n        //保存在SD卡需要在AndroidManifest.xml添加写SD卡权限\n        //如果不需要保存合成音频，注释该行代码\n        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, \"./sdcard/iflytek.pcm\");\n\n        //3.开始合成\n        mTts.startSpeaking(text, new MySynthesizerListener());\n    }\n\n    class MySynthesizerListener implements SynthesizerListener {\n\n        @Override\n        public void onBufferProgress(int arg0, int arg1, int arg2, String arg3) {\n\n        }\n\n        @Override\n        public void onCompleted(SpeechError arg0) {\n\n        }\n\n        @Override\n        public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {\n\n        }\n\n        @Override\n        public void onSpeakBegin() {\n\n        }\n\n        @Override\n        public void onSpeakPaused() {\n\n        }\n\n        @Override\n        public void onSpeakProgress(int arg0, int arg1, int arg2) {\n\n        }\n\n        @Override\n        public void onSpeakResumed() {\n\n        }\n\n    }\n\n    /**\n     * 弹出识别的对话框, 并开始语音识别\n     */\n    public void showListenVoiceDialog(Context context, RecognizerDialogListener recognizerDialogListener) {\n        //1.创建SpeechRecognizer对象，第二个参数：本地听写时传InitListener\n        RecognizerDialog iatDialog = new RecognizerDialog(context, new MyInitListener());\n        //2.设置听写参数\n        iatDialog.setParameter(SpeechConstant.DOMAIN, \"iat\");//听写匹配引擎,iat为默认值(日常用语) video(视频) poi(地图) music(音乐)\n        iatDialog.setParameter(SpeechConstant.LANGUAGE, \"zh_cn\");//zh_cn(简体中文) en_us(美式英文)\n        iatDialog.setParameter(SpeechConstant.ACCENT, \"mandarin \");//方言参数 mandarin为默认值(普通话) cantonese(粤语) lmz(四川话) henanese(河南话)\n        //3.设置回调接口\n        iatDialog.setListener(recognizerDialogListener);\n        //4.开始听写\n        iatDialog.show();\n    }\n\n    class MyInitListener implements InitListener {\n\n        @Override\n        public void onInit(int arg0) {\n\n        }\n    }\n\n}\n"
  },
  {
    "path": "voicedialog/src/main/res/layout/activity_main.xml",
    "content": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <ListView\n        android:id=\"@+id/lv_conversation\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"0dip\"\n        android:layout_weight=\"1\"\n        android:cacheColorHint=\"@android:color/transparent\"\n        android:divider=\"@null\"\n        android:dividerHeight=\"0dip\"\n        android:listSelector=\"@android:color/transparent\"></ListView>\n\n    <RelativeLayout\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/bottom_bar\">\n\n        <Button\n            android:id=\"@+id/btn_start_listen\"\n            android:layout_width=\"fill_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerInParent=\"true\"\n            android:layout_marginLeft=\"20dip\"\n            android:layout_marginRight=\"20dip\"\n            android:text=\"开始与机器人对应\" />\n    </RelativeLayout>\n\n</LinearLayout>"
  },
  {
    "path": "voicedialog/src/main/res/layout/listview_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"5dip\" >\n\n    <LinearLayout\n        android:id=\"@+id/ll_answer\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/answer_bubble\"\n        android:orientation=\"vertical\"\n        android:paddingBottom=\"10dip\"\n        android:paddingLeft=\"20dip\"\n        android:paddingRight=\"10dip\"\n        android:paddingTop=\"10dip\"\n        android:visibility=\"gone\" >\n\n        <TextView\n            android:id=\"@+id/tv_answer_text\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textColor=\"#000000\"\n            android:textSize=\"18sp\" />\n\n        <ImageView\n            android:id=\"@+id/iv_answer_image\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"10dip\"\n            android:maxHeight=\"200dip\"\n            android:scaleType=\"fitXY\" />\n    </LinearLayout>\n\n    <TextView\n        android:id=\"@+id/tv_asker_text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:background=\"@drawable/asker_bubble\"\n        android:paddingBottom=\"10dip\"\n        android:paddingLeft=\"10dip\"\n        android:paddingRight=\"20dip\"\n        android:paddingTop=\"10dip\"\n        android:textColor=\"#000000\"\n        android:textSize=\"18sp\"\n        android:visibility=\"gone\" />\n\n</RelativeLayout>"
  },
  {
    "path": "voicedialog/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "voicedialog/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "voicedialog/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">VoiceDialog</string>\n</resources>\n"
  },
  {
    "path": "voicedialog/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "voicedialog/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "voicedialog/src/test/java/com/atguigu/voicedialog/ExampleUnitTest.java",
    "content": "package com.atguigu.voicedialog;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "xUtils3-master/.gitignore",
    "content": "# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Eclipse project files\n.classpath\n.project\nproject.properties\n\n# IDEA project files\n*.iml\n.idea/\n\n# OS generated files\n.DS_Store\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\n\n# others\nant.properties\nbuild.xml\nmap.txt"
  },
  {
    "path": "xUtils3-master/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.3\"\n\n    defaultConfig {\n        applicationId \"org.xutils.sample\"\n        minSdkVersion 14\n        targetSdkVersion 22 // 不使用api23的动态权限策略\n        versionCode 1\n        versionName \"1.0\"\n    }\n\n    signingConfigs {\n        debugConfig {\n            storeFile file(\"debug.keystore\")\n            storePassword \"android\"\n            keyAlias \"androiddebugkey\"\n            keyPassword \"android\"\n        }\n    }\n    buildTypes {\n        release {\n            debuggable false\n            minifyEnabled true\n            signingConfig signingConfigs.debugConfig\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    compile 'com.android.support:appcompat-v7:23.3.0'\n    compile 'com.android.support:support-v4:23.3.0'\n    compile 'com.android.support:recyclerview-v7:23.3.0'\n    compile 'com.android.support:design:23.3.0'\n    compile project(':xutils')\n}\n"
  },
  {
    "path": "xUtils3-master/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/wyouflf/develop/android-sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n################### region for xUtils\n-keepattributes Signature,*Annotation*\n-keep public class org.xutils.** {\n    public protected *;\n}\n-keep public interface org.xutils.** {\n    public protected *;\n}\n-keepclassmembers class * extends org.xutils.** {\n    public protected *;\n}\n-keepclassmembers @org.xutils.db.annotation.* class * {*;}\n-keepclassmembers @org.xutils.http.annotation.* class * {*;}\n-keepclassmembers class * {\n    @org.xutils.view.annotation.Event <methods>;\n}\n#################### end region"
  },
  {
    "path": "xUtils3-master/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.xutils.sample\">\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n\n    <application\n        android:name=\".MyApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/app_name\"\n            android:theme=\"@style/AppTheme.NoActionBar\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity android:name=\".BigImageActivity\" />\n        <activity android:name=\".DownloadActivity\" />\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/BaseActivity.java",
    "content": "package org.xutils.sample;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\n\nimport org.xutils.x;\n\n/**\n * Created by wyouflf on 15/11/4.\n */\npublic class BaseActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        x.view().inject(this);\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/BaseFragment.java",
    "content": "package org.xutils.sample;\n\nimport android.os.Bundle;\nimport android.support.v4.app.Fragment;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport org.xutils.x;\n\n/**\n * Created by wyouflf on 15/11/4.\n */\npublic class BaseFragment extends Fragment {\n\n    private boolean injected = false;\n\n    @Override\n    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n        injected = true;\n        return x.view().inject(this, inflater, container);\n    }\n\n    @Override\n    public void onViewCreated(View view, Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        if (!injected) {\n            x.view().inject(this, this.getView());\n        }\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/BigImageActivity.java",
    "content": "package org.xutils.sample;\n\nimport android.os.Bundle;\nimport android.widget.ImageView;\n\nimport org.xutils.image.ImageOptions;\nimport org.xutils.view.annotation.ContentView;\nimport org.xutils.view.annotation.ViewInject;\nimport org.xutils.x;\n\n@ContentView(R.layout.activity_big_image)\npublic class BigImageActivity extends BaseActivity {\n\n    @ViewInject(R.id.iv_big_img)\n    private ImageView iv_big_img;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        ImageOptions imageOptions = new ImageOptions.Builder()\n                // 加载中或错误图片的ScaleType\n                //.setPlaceholderScaleType(ImageView.ScaleType.MATRIX)\n                // 默认自动适应大小\n                // .setSize(...)\n                .setIgnoreGif(false)\n                        // 如果使用本地文件url, 添加这个设置可以在本地文件更新后刷新立即生效.\n                        //.setUseMemCache(false)\n                .setImageScaleType(ImageView.ScaleType.CENTER).build();\n\n        x.image().bind(iv_big_img, getIntent().getStringExtra(\"url\"), imageOptions);\n\n        // assets file\n        //x.image().bind(iv_big_img, \"assets://test.gif\", imageOptions);\n\n        // local file\n        //x.image().bind(iv_big_img, new File(\"/sdcard/test.gif\").toURI().toString(), imageOptions);\n        //x.image().bind(iv_big_img, \"/sdcard/test.jpg\", imageOptions);\n        //x.image().bind(iv_big_img, \"file:///sdcard/test.gif\", imageOptions);\n        //x.image().bind(iv_big_img, \"file:/sdcard/test.gif\", imageOptions);\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/DbFragment.java",
    "content": "package org.xutils.sample;\n\nimport android.view.View;\nimport android.widget.TextView;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.db.sqlite.WhereBuilder;\nimport org.xutils.db.table.DbModel;\nimport org.xutils.ex.DbException;\nimport org.xutils.sample.db.Child;\nimport org.xutils.sample.db.Parent;\nimport org.xutils.view.annotation.ContentView;\nimport org.xutils.view.annotation.Event;\nimport org.xutils.view.annotation.ViewInject;\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by wyouflf on 15/11/4.\n */\n@ContentView(R.layout.fragment_db)\npublic class DbFragment extends BaseFragment {\n\n    DbManager.DaoConfig daoConfig = new DbManager.DaoConfig()\n            .setDbName(\"test.db\")\n            // 不设置dbDir时, 默认存储在app的私有目录.\n            .setDbDir(new File(\"/sdcard\")) // \"sdcard\"的写法并非最佳实践, 这里为了简单, 先这样写了.\n            .setDbVersion(2)\n            .setDbOpenListener(new DbManager.DbOpenListener() {\n                @Override\n                public void onDbOpened(DbManager db) {\n                    // 开启WAL, 对写入加速提升巨大\n                    db.getDatabase().enableWriteAheadLogging();\n                }\n            })\n            .setDbUpgradeListener(new DbManager.DbUpgradeListener() {\n                @Override\n                public void onUpgrade(DbManager db, int oldVersion, int newVersion) {\n                    // TODO: ...\n                    // db.addColumn(...);\n                    // db.dropTable(...);\n                    // ...\n                    // or\n                    // db.dropDb();\n                }\n            });\n\n    @ViewInject(R.id.tv_db_result)\n    private TextView tv_db_result;\n\n    @Event(R.id.btn_test_db)\n    private void onTestDbClick(View view) {\n\n        // 一对多: (本示例的代码)\n        // 自己在多的一方(child)保存另一方的(parentId), 查找的时候用parentId查parent或child.\n        // 一对一:\n        // 在任何一边保存另一边的Id并加上唯一属性: @Column(name = \"parentId\", property = \"UNIQUE\")\n        // 多对多:\n        // 再建一个关联表, 保存两边的id. 查询分两步: 先查关联表得到id, 再查对应表的属性.\n\n        String temp = \"\";\n\n        try {\n\n            DbManager db = x.getDb(daoConfig);\n\n            Child child = new Child();\n            child.setName(\"child's name\");\n\n            Parent test = db.selector(Parent.class).where(\"id\", \"in\", new int[]{1, 3, 6}).findFirst();\n            // long count = db.selector(Parent.class).where(\"id\", \"in\", new int[]{1, 3, 6}).count();\n            // Parent test = db.selector(Parent.class).where(\"id\", \"between\", new String[]{\"1\", \"5\"}).findFirst();\n            if (test != null) {\n                child.setParentId(test.getId());\n                temp += \"first parent:\" + test + \"\\n\";\n                tv_db_result.setText(temp);\n            }\n\n            Parent parent = new Parent();\n            parent.name = \"测试\" + System.currentTimeMillis();\n            parent.setAdmin(true);\n            parent.setEmail(\"wyouflf@qq.com\");\n            parent.setTime(new Date());\n            parent.setDate(new java.sql.Date(new Date().getTime()));\n            //db.save(parent);\n            db.saveBindingId(parent);\n\n            db.saveBindingId(child);//保存对象关联数据库生成的id\n\n            List<Child> children = db.selector(Child.class).findAll();\n            temp += \"children size:\" + children.size() + \"\\n\";\n            tv_db_result.setText(temp);\n            if (children.size() > 0) {\n                temp += \"last children:\" + children.get(children.size() - 1) + \"\\n\";\n                tv_db_result.setText(temp);\n            }\n\n            Calendar calendar = Calendar.getInstance();\n            calendar.add(Calendar.DATE, -1);\n            calendar.add(Calendar.HOUR, 3);\n\n            List<Parent> list = db.selector(Parent.class)\n                    .where(\"id\", \"<\", 54)\n                    .and(\"time\", \">\", calendar.getTime())\n                    .orderBy(\"id\")\n                    .limit(10).findAll();\n            temp += \"find parent size:\" + list.size() + \"\\n\";\n            tv_db_result.setText(temp);\n            if (list.size() > 0) {\n                temp += \"last parent:\" + list.get(list.size() - 1) + \"\\n\";\n                tv_db_result.setText(temp);\n            }\n\n            // test update\n            parent.name = \"hahaha123\";\n            parent.setEmail(\"wyouflf@gmail.com\");\n            db.update(parent);\n            db.update(parent, \"name\", \"email\");\n            db.update(Parent.class,\n                    WhereBuilder.b(\"id\", \"=\", 1).and(\"isAdmin\", \"=\", true),\n                    new KeyValue(\"name\", \"test_name\"), new KeyValue(\"isAdmin\", false));\n\n            Parent entity = child.getParent(db);\n            temp += \"find by id:\" + entity.toString() + \"\\n\";\n            tv_db_result.setText(temp);\n\n            List<DbModel> dbModels = db.selector(Parent.class)\n                    .groupBy(\"name\")\n                    .select(\"name\", \"count(name) as count\").findAll();\n            temp += \"group by result:\" + dbModels.get(0).getDataMap() + \"\\n\";\n            tv_db_result.setText(temp);\n\n        } catch (Throwable e) {\n            temp += \"error :\" + e.getMessage() + \"\\n\";\n            tv_db_result.setText(temp);\n        }\n    }\n\n    @Event(R.id.btn_test_db2)\n    private void onTestDb2Click(View view) {\n        tv_db_result.setText(\"wait...\");\n        x.task().run(new Runnable() { // 异步执行\n            @Override\n            public void run() {\n\n                DbManager db = x.getDb(daoConfig);\n                String result = \"\";\n\n                List<Parent> parentList = new ArrayList<Parent>();\n                for (int i = 0; i < 1000; i++) {\n                    Parent parent = new Parent();\n                    parent.setAdmin(true);\n                    parent.setDate(new java.sql.Date(1234));\n                    parent.setTime(new Date());\n                    parent.setEmail(i + \"_@qq.com\");\n                    parentList.add(parent);\n                }\n\n                long start = System.currentTimeMillis();\n                for (Parent parent : parentList) {\n                    try {\n                        db.save(parent);\n                    } catch (DbException ex) {\n                        ex.printStackTrace();\n                    }\n                }\n                result += \"插入1000条数据:\" + (System.currentTimeMillis() - start) + \"ms\\n\";\n\n                start = System.currentTimeMillis();\n                try {\n                    parentList = db.selector(Parent.class).orderBy(\"id\", true).limit(1000).findAll();\n                } catch (DbException ex) {\n                    ex.printStackTrace();\n                }\n                result += \"查找1000条数据:\" + (System.currentTimeMillis() - start) + \"ms\\n\";\n\n                start = System.currentTimeMillis();\n                try {\n                    db.delete(parentList);\n                } catch (DbException ex) {\n                    ex.printStackTrace();\n                }\n                result += \"删除1000条数据:\" + (System.currentTimeMillis() - start) + \"ms\\n\";\n\n                // 批量插入\n                parentList = new ArrayList<Parent>();\n                for (int i = 0; i < 1000; i++) {\n                    Parent parent = new Parent();\n                    parent.setAdmin(true);\n                    parent.setDate(new java.sql.Date(1234));\n                    parent.setTime(new Date());\n                    parent.setEmail(i + \"_@qq.com\");\n                    parentList.add(parent);\n                }\n\n                start = System.currentTimeMillis();\n                try {\n                    db.save(parentList);\n                } catch (DbException ex) {\n                    ex.printStackTrace();\n                }\n                result += \"批量插入1000条数据:\" + (System.currentTimeMillis() - start) + \"ms\\n\";\n\n                try {\n                    parentList = db.selector(Parent.class).orderBy(\"id\", true).limit(1000).findAll();\n                    db.delete(parentList);\n                } catch (DbException ex) {\n                    ex.printStackTrace();\n                }\n\n                final String finalResult = result;\n                x.task().post(new Runnable() { // UI同步执行\n                    @Override\n                    public void run() {\n                        tv_db_result.setText(finalResult);\n                    }\n                });\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/DownloadActivity.java",
    "content": "package org.xutils.sample;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.Button;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport org.xutils.common.Callback;\nimport org.xutils.ex.DbException;\nimport org.xutils.sample.download.DownloadInfo;\nimport org.xutils.sample.download.DownloadManager;\nimport org.xutils.sample.download.DownloadState;\nimport org.xutils.sample.download.DownloadViewHolder;\nimport org.xutils.view.annotation.ContentView;\nimport org.xutils.view.annotation.Event;\nimport org.xutils.view.annotation.ViewInject;\nimport org.xutils.x;\n\nimport java.io.File;\n\n@ContentView(R.layout.activity_download)\npublic class DownloadActivity extends BaseActivity {\n\n    @ViewInject(R.id.lv_download)\n    private ListView downloadList;\n\n    private DownloadManager downloadManager;\n    private DownloadListAdapter downloadListAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        downloadManager = DownloadManager.getInstance();\n        downloadListAdapter = new DownloadListAdapter();\n        downloadList.setAdapter(downloadListAdapter);\n    }\n\n    private class DownloadListAdapter extends BaseAdapter {\n\n        private Context mContext;\n        private final LayoutInflater mInflater;\n\n        private DownloadListAdapter() {\n            mContext = getBaseContext();\n            mInflater = LayoutInflater.from(mContext);\n        }\n\n        @Override\n        public int getCount() {\n            if (downloadManager == null) return 0;\n            return downloadManager.getDownloadListCount();\n        }\n\n        @Override\n        public Object getItem(int i) {\n            return downloadManager.getDownloadInfo(i);\n        }\n\n        @Override\n        public long getItemId(int i) {\n            return i;\n        }\n\n        @Override\n        public View getView(int i, View view, ViewGroup viewGroup) {\n            DownloadItemViewHolder holder = null;\n            DownloadInfo downloadInfo = downloadManager.getDownloadInfo(i);\n            if (view == null) {\n                view = mInflater.inflate(R.layout.download_item, null);\n                holder = new DownloadItemViewHolder(view, downloadInfo);\n                view.setTag(holder);\n                holder.refresh();\n            } else {\n                holder = (DownloadItemViewHolder) view.getTag();\n                holder.update(downloadInfo);\n            }\n\n            if (downloadInfo.getState().value() < DownloadState.FINISHED.value()) {\n                try {\n                    downloadManager.startDownload(\n                            downloadInfo.getUrl(),\n                            downloadInfo.getLabel(),\n                            downloadInfo.getFileSavePath(),\n                            downloadInfo.isAutoResume(),\n                            downloadInfo.isAutoRename(),\n                            holder);\n                } catch (DbException ex) {\n                    Toast.makeText(x.app(), \"添加下载失败\", Toast.LENGTH_LONG).show();\n                }\n            }\n\n            return view;\n        }\n    }\n\n    public class DownloadItemViewHolder extends DownloadViewHolder {\n        @ViewInject(R.id.download_label)\n        TextView label;\n        @ViewInject(R.id.download_state)\n        TextView state;\n        @ViewInject(R.id.download_pb)\n        ProgressBar progressBar;\n        @ViewInject(R.id.download_stop_btn)\n        Button stopBtn;\n\n        public DownloadItemViewHolder(View view, DownloadInfo downloadInfo) {\n            super(view, downloadInfo);\n            refresh();\n        }\n\n        @Event(R.id.download_stop_btn)\n        private void toggleEvent(View view) {\n            DownloadState state = downloadInfo.getState();\n            switch (state) {\n                case WAITING:\n                case STARTED:\n                    downloadManager.stopDownload(downloadInfo);\n                    break;\n                case ERROR:\n                case STOPPED:\n                    try {\n                        downloadManager.startDownload(\n                                downloadInfo.getUrl(),\n                                downloadInfo.getLabel(),\n                                downloadInfo.getFileSavePath(),\n                                downloadInfo.isAutoResume(),\n                                downloadInfo.isAutoRename(),\n                                this);\n                    } catch (DbException ex) {\n                        Toast.makeText(x.app(), \"添加下载失败\", Toast.LENGTH_LONG).show();\n                    }\n                    break;\n                case FINISHED:\n                    Toast.makeText(x.app(), \"已经下载完成\", Toast.LENGTH_LONG).show();\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        @Event(R.id.download_remove_btn)\n        private void removeEvent(View view) {\n            try {\n                downloadManager.removeDownload(downloadInfo);\n                downloadListAdapter.notifyDataSetChanged();\n            } catch (DbException e) {\n                Toast.makeText(x.app(), \"移除任务失败\", Toast.LENGTH_LONG).show();\n            }\n        }\n\n        @Override\n        public void update(DownloadInfo downloadInfo) {\n            super.update(downloadInfo);\n            refresh();\n        }\n\n        @Override\n        public void onWaiting() {\n            refresh();\n        }\n\n        @Override\n        public void onStarted() {\n            refresh();\n        }\n\n        @Override\n        public void onLoading(long total, long current) {\n            refresh();\n        }\n\n        @Override\n        public void onSuccess(File result) {\n            refresh();\n        }\n\n        @Override\n        public void onError(Throwable ex, boolean isOnCallback) {\n            refresh();\n        }\n\n        @Override\n        public void onCancelled(Callback.CancelledException cex) {\n            refresh();\n        }\n\n        public void refresh() {\n            label.setText(downloadInfo.getLabel());\n            state.setText(downloadInfo.getState().toString());\n            progressBar.setProgress(downloadInfo.getProgress());\n\n            stopBtn.setVisibility(View.VISIBLE);\n            stopBtn.setText(x.app().getString(R.string.stop));\n            DownloadState state = downloadInfo.getState();\n            switch (state) {\n                case WAITING:\n                case STARTED:\n                    stopBtn.setText(x.app().getString(R.string.stop));\n                    break;\n                case ERROR:\n                case STOPPED:\n                    stopBtn.setText(x.app().getString(R.string.start));\n                    break;\n                case FINISHED:\n                    stopBtn.setVisibility(View.INVISIBLE);\n                    break;\n                default:\n                    stopBtn.setText(x.app().getString(R.string.start));\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/HttpFragment.java",
    "content": "package org.xutils.sample;\n\nimport android.content.Intent;\nimport android.view.View;\nimport android.widget.EditText;\nimport android.widget.Toast;\n\nimport org.xutils.common.Callback;\nimport org.xutils.ex.DbException;\nimport org.xutils.ex.HttpException;\nimport org.xutils.http.RequestParams;\nimport org.xutils.sample.download.DownloadManager;\nimport org.xutils.sample.http.BaiduParams;\nimport org.xutils.sample.http.BaiduResponse;\nimport org.xutils.view.annotation.ContentView;\nimport org.xutils.view.annotation.Event;\nimport org.xutils.view.annotation.ViewInject;\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.net.URLDecoder;\nimport java.util.List;\n\n/**\n * Created by wyouflf on 15/11/4.\n */\n@ContentView(R.layout.fragment_http)\npublic class HttpFragment extends BaseFragment {\n\n\n    /**\n     * 1. 方法必须私有限定,\n     * 2. 方法参数形式必须和type对应的Listener接口一致.\n     * 3. 注解参数value支持数组: value={id1, id2, id3}\n     * 4. 其它参数说明见{@link org.xutils.view.annotation.Event}类的说明.\n     **/\n    @Event(value = R.id.btn_test1,\n            type = View.OnClickListener.class/*可选参数, 默认是View.OnClickListener.class*/)\n    private void onTest1Click(View view) {\n        /**\n         * 自定义实体参数类请参考:\n         * 请求注解 {@link org.xutils.http.annotation.HttpRequest}\n         * 请求注解处理模板接口 {@link org.xutils.http.app.ParamsBuilder}\n         *\n         * 需要自定义类型作为callback的泛型时, 参考:\n         * 响应注解 {@link org.xutils.http.annotation.HttpResponse}\n         * 响应注解处理模板接口 {@link org.xutils.http.app.ResponseParser}\n         *\n         * 示例: 查看 org.xutils.sample.http 包里的代码\n         */\n        BaiduParams params = new BaiduParams();\n        params.wd = \"xUtils\";\n        // 有上传文件时使用multipart表单, 否则上传原始文件流.\n        // params.setMultipart(true);\n        // 上传文件方式 1\n        // params.uploadFile = new File(\"/sdcard/test.txt\");\n        // 上传文件方式 2\n        // params.addBodyParameter(\"uploadFile\", new File(\"/sdcard/test.txt\"));\n        Callback.Cancelable cancelable\n                = x.http().get(params,\n                /**\n                 * 1. callback的泛型:\n                 * callback参数默认支持的泛型类型参见{@link org.xutils.http.loader.LoaderFactory},\n                 * 例如: 指定泛型为File则可实现文件下载, 使用params.setSaveFilePath(path)指定文件保存的全路径.\n                 * 默认支持断点续传(采用了文件锁和尾端校验续传文件的一致性).\n                 * 其他常用类型可以自己在LoaderFactory中注册,\n                 * 也可以使用{@link org.xutils.http.annotation.HttpResponse}\n                 * 将注解HttpResponse加到自定义返回值类型上, 实现自定义ResponseParser接口来统一转换.\n                 * 如果返回值是json形式, 那么利用第三方的json工具将十分容易定义自己的ResponseParser.\n                 * 如示例代码{@link org.xutils.sample.http.BaiduResponse}, 可直接使用BaiduResponse作为\n                 * callback的泛型.\n                 *\n                 * @HttpResponse 注解 和 ResponseParser接口仅适合做json, xml等文本类型数据的解析,\n                 * 如果需要其他数据类型的解析可参考:\n                 * {@link org.xutils.http.loader.LoaderFactory}\n                 * 和 {@link org.xutils.common.Callback.PrepareCallback}.\n                 * LoaderFactory提供PrepareCallback第一个泛型参数类型的自动转换,\n                 * 第二个泛型参数需要在prepare方法中实现.\n                 * (LoaderFactory中已经默认提供了部分常用类型的转换实现, 其他类型需要自己注册.)\n                 *\n                 * 2. callback的组合:\n                 * 可以用基类或接口组合个种类的Callback, 见{@link org.xutils.common.Callback}.\n                 * 例如:\n                 * a. 组合使用CacheCallback将使请求检测缓存或将结果存入缓存(仅GET请求生效).\n                 * b. 组合使用PrepareCallback的prepare方法将为callback提供一次后台执行耗时任务的机会,\n                 * 然后将结果给onCache或onSuccess.\n                 * c. 组合使用ProgressCallback将提供进度回调.\n                 * ...(可参考{@link org.xutils.image.ImageLoader}\n                 * 或 示例代码中的 {@link org.xutils.sample.download.DownloadCallback})\n                 *\n                 * 3. 请求过程拦截或记录日志: 参考 {@link org.xutils.http.app.RequestTracker}\n                 *\n                 * 4. 请求Header获取: 参考 {@link org.xutils.http.app.RequestInterceptListener}\n                 *\n                 * 5. 其他(线程池, 超时, 重定向, 重试, 代理等): 参考 {@link org.xutils.http.RequestParams}\n                 *\n                 **/\n                new Callback.CommonCallback<List<BaiduResponse>>() {\n                    @Override\n                    public void onSuccess(List<BaiduResponse> result) {\n                        Toast.makeText(x.app(), result.get(0).toString(), Toast.LENGTH_LONG).show();\n                    }\n\n                    @Override\n                    public void onError(Throwable ex, boolean isOnCallback) {\n                        Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();\n                        if (ex instanceof HttpException) { // 网络错误\n                            HttpException httpEx = (HttpException) ex;\n                            int responseCode = httpEx.getCode();\n                            String responseMsg = httpEx.getMessage();\n                            String errorResult = httpEx.getResult();\n                            // ...\n                        } else { // 其他错误\n                            // ...\n                        }\n                    }\n\n                    @Override\n                    public void onCancelled(CancelledException cex) {\n                        Toast.makeText(x.app(), \"cancelled\", Toast.LENGTH_LONG).show();\n                    }\n\n                    @Override\n                    public void onFinished() {\n\n                    }\n                });\n\n        // cancelable.cancel(); // 取消请求\n    }\n\n    // 上传多文件示例\n    @Event(value = R.id.btn_test2)\n    private void onTest2Click(View view) {\n        RequestParams params = new RequestParams(\"http://192.168.0.13:8080/upload\");\n        // 加到url里的参数, http://xxxx/s?wd=xUtils\n        params.addQueryStringParameter(\"wd\", \"xUtils\");\n        // 添加到请求body体的参数, 只有POST, PUT, PATCH, DELETE请求支持.\n        // params.addBodyParameter(\"wd\", \"xUtils\");\n\n        // 使用multipart表单上传文件\n        params.setMultipart(true);\n        params.addBodyParameter(\n                \"file\",\n                new File(\"/sdcard/test.jpg\"),\n                null); // 如果文件没有扩展名, 最好设置contentType参数.\n        try {\n            params.addBodyParameter(\n                    \"file2\",\n                    new FileInputStream(new File(\"/sdcard/test2.jpg\")),\n                    \"image/jpeg\",\n                    // 测试中文文件名\n                    \"你+& \\\" 好.jpg\"); // InputStream参数获取不到文件名, 最好设置, 除非服务端不关心这个参数.\n        } catch (FileNotFoundException ex) {\n            ex.printStackTrace();\n        }\n        x.http().post(params, new Callback.CommonCallback<String>() {\n            @Override\n            public void onSuccess(String result) {\n                Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n                Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n                Toast.makeText(x.app(), \"cancelled\", Toast.LENGTH_LONG).show();\n            }\n\n            @Override\n            public void onFinished() {\n\n            }\n        });\n    }\n\n    @ViewInject(R.id.et_url)\n    private EditText et_url;\n\n    // 添加到下载列表\n    @Event(value = R.id.btn_test3)\n    private void onTest3Click(View view) throws DbException {\n        for (int i = 0; i < 5; i++) {\n            String url = et_url.getText().toString();\n            String label = i + \"xUtils_\" + System.nanoTime();\n            DownloadManager.getInstance().startDownload(\n                    url, label,\n                    \"/sdcard/xUtils/\" + label + \".aar\", true, false, null);\n        }\n    }\n\n    // 打开下载列表页\n    @Event(value = R.id.btn_test4)\n    private void onTest4Click(View view) throws DbException {\n        getActivity().startActivity(new Intent(getActivity(), DownloadActivity.class));\n    }\n\n    /**\n     * 缓存示例, 更复杂的例子参考 {@link org.xutils.image.ImageLoader}\n     */\n    @Event(value = R.id.btn_test5)\n    private void onTest5Click(View view) throws FileNotFoundException {\n        BaiduParams params = new BaiduParams();\n        params.wd = \"xUtils\";\n        // 默认缓存存活时间, 单位:毫秒.(如果服务没有返回有效的max-age或Expires)\n        params.setCacheMaxAge(1000 * 60);\n        Callback.Cancelable cancelable\n                // 使用CacheCallback, xUtils将为该请求缓存数据.\n                = x.http().get(params, new Callback.CacheCallback<String>() {\n\n            private boolean hasError = false;\n            private String result = null;\n\n            @Override\n            public boolean onCache(String result) {\n                // 得到缓存数据, 缓存过期后不会进入这个方法.\n                // 如果服务端没有返回过期时间, 参考params.setCacheMaxAge(maxAge)方法.\n                //\n                // * 客户端会根据服务端返回的 header 中 max-age 或 expires 来确定本地缓存是否给 onCache 方法.\n                //   如果服务端没有返回 max-age 或 expires, 那么缓存将一直保存, 除非这里自己定义了返回false的\n                //   逻辑, 那么xUtils将请求新数据, 来覆盖它.\n                //\n                // * 如果信任该缓存返回 true, 将不再请求网络;\n                //   返回 false 继续请求网络, 但会在请求头中加上ETag, Last-Modified等信息,\n                //   如果服务端返回304, 则表示数据没有更新, 不继续加载数据.\n                //\n                this.result = result;\n                return false; // true: 信任缓存数据, 不在发起网络请求; false不信任缓存数据.\n            }\n\n            @Override\n            public void onSuccess(String result) {\n                // 注意: 如果服务返回304 或 onCache 选择了信任缓存, 这时result为null.\n                if (result != null) {\n                    this.result = result;\n                }\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n                hasError = true;\n                Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();\n                if (ex instanceof HttpException) { // 网络错误\n                    HttpException httpEx = (HttpException) ex;\n                    int responseCode = httpEx.getCode();\n                    String responseMsg = httpEx.getMessage();\n                    String errorResult = httpEx.getResult();\n                    // ...\n                } else { // 其他错误\n                    // ...\n                }\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n                Toast.makeText(x.app(), \"cancelled\", Toast.LENGTH_LONG).show();\n            }\n\n            @Override\n            public void onFinished() {\n                if (!hasError && result != null) {\n                    // 成功获取数据\n                    Toast.makeText(x.app(), result, Toast.LENGTH_LONG).show();\n                }\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/ImageFragment.java",
    "content": "package org.xutils.sample;\n\nimport android.content.Intent;\nimport android.graphics.drawable.Drawable;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.BaseAdapter;\nimport android.widget.ImageView;\nimport android.widget.ListView;\nimport android.widget.ProgressBar;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.DensityUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.image.ImageOptions;\nimport org.xutils.view.annotation.ContentView;\nimport org.xutils.view.annotation.Event;\nimport org.xutils.view.annotation.ViewInject;\nimport org.xutils.x;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Created by wyouflf on 15/11/4.\n */\n@ContentView(R.layout.fragment_image)\npublic class ImageFragment extends BaseFragment {\n\n    private String[] imgSites = {\n            \"http://image.baidu.com/\",\n            \"http://www.22mm.cc/\",\n            \"http://www.moko.cc/\",\n            \"http://eladies.sina.com.cn/photo/\",\n            \"http://www.youzi4.com/\"\n    };\n\n    ImageOptions imageOptions;\n\n    @ViewInject(R.id.lv_img)\n    private ListView imageListView;\n\n    @Override\n    public void onViewCreated(View view, Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        imageOptions = new ImageOptions.Builder()\n                .setSize(DensityUtil.dip2px(120), DensityUtil.dip2px(120))\n                .setRadius(DensityUtil.dip2px(5))\n                // 如果ImageView的大小不是定义为wrap_content, 不要crop.\n                .setCrop(true) // 很多时候设置了合适的scaleType也不需要它.\n                // 加载中或错误图片的ScaleType\n                //.setPlaceholderScaleType(ImageView.ScaleType.MATRIX)\n                .setImageScaleType(ImageView.ScaleType.CENTER_CROP)\n                .setLoadingDrawableId(R.mipmap.ic_launcher)\n                .setFailureDrawableId(R.mipmap.ic_launcher)\n                .build();\n\n        imageListAdapter = new ImageListAdapter();\n        imageListView.setAdapter(imageListAdapter);\n\n        // 加载url请求返回的图片连接给listview\n        // 这里只是简单的示例，并非最佳实践，图片较多时，最好上拉加载更多...\n        for (String url : imgSites) {\n            loadImgList(url);\n        }\n    }\n\n    @Event(value = R.id.lv_img, type = AdapterView.OnItemClickListener.class)\n    private void onImageItemClick(AdapterView<?> parent, View view, int position, long id) {\n        Intent intent = new Intent(this.getActivity(), BigImageActivity.class);\n        intent.putExtra(\"url\", imageListAdapter.getItem(position).toString());\n        this.getActivity().startActivity(intent);\n    }\n\n    private void loadImgList(String url) {\n        x.http().get(new RequestParams(url), new Callback.CommonCallback<String>() {\n            @Override\n            public void onSuccess(String result) {\n                imageListAdapter.addSrc(getImgSrcList(result));\n                imageListAdapter.notifyDataSetChanged();//通知listview更新数据\n            }\n\n            @Override\n            public void onError(Throwable ex, boolean isOnCallback) {\n\n            }\n\n            @Override\n            public void onCancelled(CancelledException cex) {\n\n            }\n\n            @Override\n            public void onFinished() {\n\n            }\n        });\n    }\n\n    private ImageListAdapter imageListAdapter;\n\n    private class ImageListAdapter extends BaseAdapter {\n\n        private final LayoutInflater mInflater;\n        private ArrayList<String> imgSrcList;\n\n        public ImageListAdapter() {\n            super();\n            mInflater = LayoutInflater.from(getContext());\n            imgSrcList = new ArrayList<String>();\n        }\n\n        public void addSrc(List<String> imgSrcList) {\n            this.imgSrcList.addAll(imgSrcList);\n        }\n\n        public void addSrc(String imgUrl) {\n            this.imgSrcList.add(imgUrl);\n        }\n\n        @Override\n        public int getCount() {\n            return imgSrcList.size();\n        }\n\n        @Override\n        public Object getItem(int position) {\n            return imgSrcList.get(position);\n        }\n\n        @Override\n        public long getItemId(int i) {\n            return i;\n        }\n\n        @Override\n        public View getView(final int position, View view, ViewGroup parent) {\n            ImageItemHolder holder = null;\n            if (view == null) {\n                view = mInflater.inflate(R.layout.image_item, parent, false);\n                holder = new ImageItemHolder();\n                x.view().inject(holder, view);\n                view.setTag(holder);\n            } else {\n                holder = (ImageItemHolder) view.getTag();\n            }\n            holder.imgPb.setProgress(0);\n            x.image().bind(holder.imgItem,\n                    imgSrcList.get(position),\n                    imageOptions,\n                    new CustomBitmapLoadCallBack(holder));\n            return view;\n        }\n    }\n\n    private class ImageItemHolder {\n        @ViewInject(R.id.img_item)\n        private ImageView imgItem;\n\n        @ViewInject(R.id.img_pb)\n        private ProgressBar imgPb;\n    }\n\n    public class CustomBitmapLoadCallBack implements Callback.ProgressCallback<Drawable> {\n        private final ImageItemHolder holder;\n\n        public CustomBitmapLoadCallBack(ImageItemHolder holder) {\n            this.holder = holder;\n        }\n\n        @Override\n        public void onWaiting() {\n            this.holder.imgPb.setProgress(0);\n        }\n\n        @Override\n        public void onStarted() {\n\n        }\n\n        @Override\n        public void onLoading(long total, long current, boolean isDownloading) {\n            this.holder.imgPb.setProgress((int) (current * 100 / total));\n        }\n\n        @Override\n        public void onSuccess(Drawable result) {\n            this.holder.imgPb.setProgress(100);\n        }\n\n        @Override\n        public void onError(Throwable ex, boolean isOnCallback) {\n        }\n\n        @Override\n        public void onCancelled(CancelledException cex) {\n\n        }\n\n        @Override\n        public void onFinished() {\n\n        }\n    }\n\n    /**\n     * 得到网页中图片的地址\n     */\n    public static List<String> getImgSrcList(String htmlStr) {\n        List<String> pics = new ArrayList<String>();\n\n        String regEx_img = \"<img.*?src=\\\"http://(.*?).jpg\\\"\"; // 图片链接地址\n        Pattern p_image = Pattern.compile(regEx_img, Pattern.CASE_INSENSITIVE);\n        Matcher m_image = p_image.matcher(htmlStr);\n        while (m_image.find()) {\n            String src = m_image.group(1);\n            if (src.length() < 100) {\n                pics.add(\"http://\" + src + \".jpg\");\n                //pics.add(\"http://f.hiphotos.baidu.com/zhidao/pic/item/2fdda3cc7cd98d104cc21595203fb80e7bec907b.jpg\");\n            }\n        }\n        return pics;\n    }\n\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/MainActivity.java",
    "content": "package org.xutils.sample;\n\nimport android.os.Bundle;\nimport android.support.design.widget.TabLayout;\nimport android.support.v4.app.Fragment;\nimport android.support.v4.app.FragmentManager;\nimport android.support.v4.app.FragmentPagerAdapter;\nimport android.support.v4.view.ViewPager;\nimport android.support.v7.widget.Toolbar;\nimport android.view.Menu;\nimport android.view.MenuItem;\n\nimport org.xutils.view.annotation.ContentView;\nimport org.xutils.view.annotation.ViewInject;\n\n@ContentView(R.layout.activity_main)\npublic class MainActivity extends BaseActivity {\n\n    @ViewInject(R.id.container)\n    private ViewPager mViewPager;\n\n    @ViewInject(R.id.toolbar)\n    private Toolbar toolbar;\n\n    @ViewInject(R.id.tabs)\n    private TabLayout tabLayout;\n\n    private SectionsPagerAdapter mSectionsPagerAdapter;\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setSupportActionBar(toolbar);\n        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());\n        mViewPager.setAdapter(mSectionsPagerAdapter);\n        tabLayout.setupWithViewPager(mViewPager);\n    }\n\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        // Handle action bar item clicks here. The action bar will\n        // automatically handle clicks on the Home/Up button, so long\n        // as you specify a parent activity in AndroidManifest.xml.\n        int id = item.getItemId();\n\n        //noinspection SimplifiableIfStatement\n        if (id == R.id.action_settings) {\n            return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n\n    /**\n     * A {@link FragmentPagerAdapter} that returns a fragment corresponding to\n     * one of the sections/tabs/pages.\n     */\n    public class SectionsPagerAdapter extends FragmentPagerAdapter {\n\n        public SectionsPagerAdapter(FragmentManager fm) {\n            super(fm);\n        }\n\n        @Override\n        public Fragment getItem(int position) {\n            switch (position) {\n                case 0: {\n                    return new HttpFragment();\n                }\n                case 1: {\n                    return new DbFragment();\n                }\n                case 2: {\n                    return new ImageFragment();\n                }\n            }\n            return null;\n        }\n\n        @Override\n        public int getCount() {\n            return 3;\n        }\n\n        @Override\n        public CharSequence getPageTitle(int position) {\n            switch (position) {\n                case 0:\n                    return \"Http\";\n                case 1:\n                    return \"Database\";\n                case 2:\n                    return \"Image\";\n            }\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/MyApplication.java",
    "content": "package org.xutils.sample;\n\nimport android.app.Application;\n\nimport org.xutils.x;\n\nimport javax.net.ssl.HostnameVerifier;\nimport javax.net.ssl.HttpsURLConnection;\nimport javax.net.ssl.SSLSession;\n\n/**\n * Created by wyouflf on 15/10/28.\n */\npublic class MyApplication extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        x.Ext.init(this);\n        x.Ext.setDebug(BuildConfig.DEBUG); // 开启debug会影响性能\n\n        // 信任所有https域名\n        /*HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {\n            @Override\n            public boolean verify(String hostname, SSLSession session) {\n                return true;\n            }\n        });*/\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/db/Child.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.sample.db;\n\n\nimport org.xutils.DbManager;\nimport org.xutils.db.annotation.Column;\nimport org.xutils.db.annotation.Table;\nimport org.xutils.ex.DbException;\n\n/**\n * Author: wyouflf\n * Date: 13-7-29\n * Time: 下午5:04\n */\n@Table(name = \"child\")\npublic class Child {\n\n    @Column(name = \"id\", isId = true)\n    private int id;\n\n    @Column(name = \"name\")\n    private String name;\n\n    @Column(name = \"email\")\n    private String email;\n\n    @Column(name = \"parentId\" /*, property = \"UNIQUE\"//如果是一对一加上唯一约束*/)\n    private long parentId; // 外键表id\n\n    // 这个属性被忽略，不存入数据库\n    private String willIgnore;\n\n    @Column(name = \"text\")\n    private String text;\n\n    public Parent getParent(DbManager db) throws DbException {\n        return db.findById(Parent.class, parentId);\n    }\n\n    public long getParentId() {\n        return parentId;\n    }\n\n    public void setParentId(long parentId) {\n        this.parentId = parentId;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public void setEmail(String email) {\n        this.email = email;\n    }\n\n    public String getWillIgnore() {\n        return willIgnore;\n    }\n\n    public void setWillIgnore(String willIgnore) {\n        this.willIgnore = willIgnore;\n    }\n\n    public String getText() {\n        return text;\n    }\n\n    public void setText(String text) {\n        this.text = text;\n    }\n\n    @Override\n    public String toString() {\n        return \"Child{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", email='\" + email + '\\'' +\n                \", parentId=\" + parentId +\n                \", willIgnore='\" + willIgnore + '\\'' +\n                \", text='\" + text + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/db/Parent.java",
    "content": "package org.xutils.sample.db;\n\nimport org.xutils.DbManager;\nimport org.xutils.db.annotation.Column;\nimport org.xutils.db.annotation.Table;\nimport org.xutils.ex.DbException;\n\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Author: wyouflf\n * Date: 13-7-25\n * Time: 下午7:06\n */\n@Table(name = \"parent\", onCreated = \"CREATE UNIQUE INDEX index_name ON parent(name,email)\")\npublic class Parent {\n\n    @Column(name = \"id\", isId = true)\n    private int id;\n\n    @Column(name = \"name\")\n    public String name;\n\n    @Column(name = \"email\")\n    private String email;\n\n    @Column(name = \"isAdmin\")\n    private boolean isAdmin;\n\n    @Column(name = \"time\")\n    private Date time;\n\n    @Column(name = \"date\")\n    private java.sql.Date date;\n\n    public List<Child> getChildren(DbManager db) throws DbException {\n        return db.selector(Child.class).where(\"parentId\", \"=\", this.id).findAll();\n    }\n\n    // 一对一\n    //public Child getChild(DbManager db) throws DbException {\n    //    return db.selector(Child.class).where(\"parentId\", \"=\", this.id).findFirst();\n    //}\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n\n    public boolean isAdmin() {\n        return isAdmin;\n    }\n\n    public void setAdmin(boolean admin) {\n        isAdmin = admin;\n    }\n\n    public Date getTime() {\n        return time;\n    }\n\n    public void setTime(Date time) {\n        this.time = time;\n    }\n\n    public java.sql.Date getDate() {\n        return date;\n    }\n\n    public void setDate(java.sql.Date date) {\n        this.date = date;\n    }\n\n    public String getEmail() {\n        return email;\n    }\n\n    public void setEmail(String email) {\n        this.email = email;\n    }\n\n    @Override\n    public String toString() {\n        return \"Parent{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", email='\" + email + '\\'' +\n                \", isAdmin=\" + isAdmin +\n                \", time=\" + time +\n                \", date=\" + date +\n                '}';\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/download/DefaultDownloadViewHolder.java",
    "content": "package org.xutils.sample.download;\n\nimport android.view.View;\nimport android.widget.Toast;\n\nimport org.xutils.common.Callback;\nimport org.xutils.x;\n\nimport java.io.File;\n\n/**\n * Created by wyouflf on 15/11/11.\n */\npublic class DefaultDownloadViewHolder extends DownloadViewHolder {\n\n    public DefaultDownloadViewHolder(View view, DownloadInfo downloadInfo) {\n        super(view, downloadInfo);\n    }\n\n    @Override\n    public void onWaiting() {\n\n    }\n\n    @Override\n    public void onStarted() {\n\n    }\n\n    @Override\n    public void onLoading(long total, long current) {\n\n    }\n\n    @Override\n    public void onSuccess(File result) {\n        Toast.makeText(x.app(), \"下载完成\", Toast.LENGTH_LONG).show();\n    }\n\n    @Override\n    public void onError(Throwable ex, boolean isOnCallback) {\n        Toast.makeText(x.app(), \"下载失败\", Toast.LENGTH_LONG).show();\n    }\n\n    @Override\n    public void onCancelled(Callback.CancelledException cex) {\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/download/DownloadCallback.java",
    "content": "package org.xutils.sample.download;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.ex.DbException;\n\nimport java.io.File;\nimport java.lang.ref.WeakReference;\n\n/**\n * Created by wyouflf on 15/11/10.\n */\n/*package*/ class DownloadCallback implements\n        Callback.CommonCallback<File>,\n        Callback.ProgressCallback<File>,\n        Callback.Cancelable {\n\n    private DownloadInfo downloadInfo;\n    private WeakReference<DownloadViewHolder> viewHolderRef;\n    private DownloadManager downloadManager;\n    private boolean cancelled = false;\n    private Cancelable cancelable;\n\n    public DownloadCallback(DownloadViewHolder viewHolder) {\n        this.switchViewHolder(viewHolder);\n    }\n\n    public boolean switchViewHolder(DownloadViewHolder viewHolder) {\n        if (viewHolder == null) return false;\n\n        synchronized (DownloadCallback.class) {\n            if (downloadInfo != null) {\n                if (this.isStopped()) {\n                    return false;\n                }\n            }\n            this.downloadInfo = viewHolder.getDownloadInfo();\n            this.viewHolderRef = new WeakReference<DownloadViewHolder>(viewHolder);\n        }\n        return true;\n    }\n\n    public void setDownloadManager(DownloadManager downloadManager) {\n        this.downloadManager = downloadManager;\n    }\n\n    public void setCancelable(Cancelable cancelable) {\n        this.cancelable = cancelable;\n    }\n\n    private DownloadViewHolder getViewHolder() {\n        if (viewHolderRef == null) return null;\n        DownloadViewHolder viewHolder = viewHolderRef.get();\n        if (viewHolder != null) {\n            DownloadInfo downloadInfo = viewHolder.getDownloadInfo();\n            if (this.downloadInfo != null && this.downloadInfo.equals(downloadInfo)) {\n                return viewHolder;\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public void onWaiting() {\n        try {\n            downloadInfo.setState(DownloadState.WAITING);\n            downloadManager.updateDownloadInfo(downloadInfo);\n        } catch (DbException ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        DownloadViewHolder viewHolder = this.getViewHolder();\n        if (viewHolder != null) {\n            viewHolder.onWaiting();\n        }\n    }\n\n    @Override\n    public void onStarted() {\n        try {\n            downloadInfo.setState(DownloadState.STARTED);\n            downloadManager.updateDownloadInfo(downloadInfo);\n        } catch (DbException ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        DownloadViewHolder viewHolder = this.getViewHolder();\n        if (viewHolder != null) {\n            viewHolder.onStarted();\n        }\n    }\n\n    @Override\n    public void onLoading(long total, long current, boolean isDownloading) {\n        if (isDownloading) {\n            try {\n                downloadInfo.setState(DownloadState.STARTED);\n                downloadInfo.setFileLength(total);\n                downloadInfo.setProgress((int) (current * 100 / total));\n                downloadManager.updateDownloadInfo(downloadInfo);\n            } catch (DbException ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n            DownloadViewHolder viewHolder = this.getViewHolder();\n            if (viewHolder != null) {\n                viewHolder.onLoading(total, current);\n            }\n        }\n    }\n\n    @Override\n    public void onSuccess(File result) {\n        synchronized (DownloadCallback.class) {\n            try {\n                downloadInfo.setState(DownloadState.FINISHED);\n                downloadManager.updateDownloadInfo(downloadInfo);\n            } catch (DbException ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n            DownloadViewHolder viewHolder = this.getViewHolder();\n            if (viewHolder != null) {\n                viewHolder.onSuccess(result);\n            }\n        }\n    }\n\n    @Override\n    public void onError(Throwable ex, boolean isOnCallback) {\n        synchronized (DownloadCallback.class) {\n            try {\n                downloadInfo.setState(DownloadState.ERROR);\n                downloadManager.updateDownloadInfo(downloadInfo);\n            } catch (DbException e) {\n                LogUtil.e(e.getMessage(), e);\n            }\n            DownloadViewHolder viewHolder = this.getViewHolder();\n            if (viewHolder != null) {\n                viewHolder.onError(ex, isOnCallback);\n            }\n        }\n    }\n\n    @Override\n    public void onCancelled(CancelledException cex) {\n        synchronized (DownloadCallback.class) {\n            try {\n                downloadInfo.setState(DownloadState.STOPPED);\n                downloadManager.updateDownloadInfo(downloadInfo);\n            } catch (DbException ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n            DownloadViewHolder viewHolder = this.getViewHolder();\n            if (viewHolder != null) {\n                viewHolder.onCancelled(cex);\n            }\n        }\n    }\n\n    @Override\n    public void onFinished() {\n        cancelled = false;\n    }\n\n    private boolean isStopped() {\n        DownloadState state = downloadInfo.getState();\n        return isCancelled() || state.value() > DownloadState.STARTED.value();\n    }\n\n    @Override\n    public void cancel() {\n        cancelled = true;\n        if (cancelable != null) {\n            cancelable.cancel();\n        }\n    }\n\n    @Override\n    public boolean isCancelled() {\n        return cancelled;\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/download/DownloadInfo.java",
    "content": "package org.xutils.sample.download;\n\nimport org.xutils.db.annotation.Column;\nimport org.xutils.db.annotation.Table;\n\n/**\n * Author: wyouflf\n * Date: 13-11-10\n * Time: 下午8:11\n */\n@Table(name = \"download\", onCreated = \"CREATE UNIQUE INDEX index_name ON download(label,fileSavePath)\")\npublic class DownloadInfo {\n\n    public DownloadInfo() {\n    }\n\n    @Column(name = \"id\", isId = true)\n    private long id;\n\n    @Column(name = \"state\")\n    private DownloadState state = DownloadState.STOPPED;\n\n    @Column(name = \"url\")\n    private String url;\n\n    @Column(name = \"label\")\n    private String label;\n\n    @Column(name = \"fileSavePath\")\n    private String fileSavePath;\n\n    @Column(name = \"progress\")\n    private int progress;\n\n    @Column(name = \"fileLength\")\n    private long fileLength;\n\n    @Column(name = \"autoResume\")\n    private boolean autoResume;\n\n    @Column(name = \"autoRename\")\n    private boolean autoRename;\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public DownloadState getState() {\n        return state;\n    }\n\n    public void setState(DownloadState state) {\n        this.state = state;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public String getLabel() {\n        return label;\n    }\n\n    public void setLabel(String label) {\n        this.label = label;\n    }\n\n    public String getFileSavePath() {\n        return fileSavePath;\n    }\n\n    public void setFileSavePath(String fileSavePath) {\n        this.fileSavePath = fileSavePath;\n    }\n\n    public int getProgress() {\n        return progress;\n    }\n\n    public void setProgress(int progress) {\n        this.progress = progress;\n    }\n\n    public long getFileLength() {\n        return fileLength;\n    }\n\n    public void setFileLength(long fileLength) {\n        this.fileLength = fileLength;\n    }\n\n    public boolean isAutoResume() {\n        return autoResume;\n    }\n\n    public void setAutoResume(boolean autoResume) {\n        this.autoResume = autoResume;\n    }\n\n    public boolean isAutoRename() {\n        return autoRename;\n    }\n\n    public void setAutoRename(boolean autoRename) {\n        this.autoRename = autoRename;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (!(o instanceof DownloadInfo)) return false;\n\n        DownloadInfo that = (DownloadInfo) o;\n\n        if (id != that.id) return false;\n\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        return (int) (id ^ (id >>> 32));\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/download/DownloadManager.java",
    "content": "package org.xutils.sample.download;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.Callback;\nimport org.xutils.common.task.PriorityExecutor;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.db.converter.ColumnConverterFactory;\nimport org.xutils.ex.DbException;\nimport org.xutils.http.RequestParams;\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.Executor;\n\n/**\n * Author: wyouflf\n * Date: 13-11-10\n * Time: 下午8:10\n */\npublic final class DownloadManager {\n\n    static {\n        // 注册DownloadState在数据库中的值类型映射\n        ColumnConverterFactory.registerColumnConverter(DownloadState.class, new DownloadStateConverter());\n    }\n\n    private static volatile DownloadManager instance;\n\n    private final static int MAX_DOWNLOAD_THREAD = 2; // 有效的值范围[1, 3], 设置为3时, 可能阻塞图片加载.\n\n    private final DbManager db;\n    private final Executor executor = new PriorityExecutor(MAX_DOWNLOAD_THREAD, true);\n    private final List<DownloadInfo> downloadInfoList = new ArrayList<DownloadInfo>();\n    private final ConcurrentHashMap<DownloadInfo, DownloadCallback>\n            callbackMap = new ConcurrentHashMap<DownloadInfo, DownloadCallback>(5);\n\n    private DownloadManager() {\n        DbManager.DaoConfig daoConfig = new DbManager.DaoConfig()\n                .setDbName(\"download\")\n                .setDbVersion(1);\n        db = x.getDb(daoConfig);\n        try {\n            List<DownloadInfo> infoList = db.selector(DownloadInfo.class).findAll();\n            if (infoList != null) {\n                for (DownloadInfo info : infoList) {\n                    if (info.getState().value() < DownloadState.FINISHED.value()) {\n                        info.setState(DownloadState.STOPPED);\n                    }\n                    downloadInfoList.add(info);\n                }\n            }\n        } catch (DbException ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    /*package*/\n    public static DownloadManager getInstance() {\n        if (instance == null) {\n            synchronized (DownloadManager.class) {\n                if (instance == null) {\n                    instance = new DownloadManager();\n                }\n            }\n        }\n        return instance;\n    }\n\n    public void updateDownloadInfo(DownloadInfo info) throws DbException {\n        db.update(info);\n    }\n\n    public int getDownloadListCount() {\n        return downloadInfoList.size();\n    }\n\n    public DownloadInfo getDownloadInfo(int index) {\n        return downloadInfoList.get(index);\n    }\n\n    public synchronized void startDownload(String url, String label, String savePath,\n                                           boolean autoResume, boolean autoRename,\n                                           DownloadViewHolder viewHolder) throws DbException {\n\n        String fileSavePath = new File(savePath).getAbsolutePath();\n        DownloadInfo downloadInfo = db.selector(DownloadInfo.class)\n                .where(\"label\", \"=\", label)\n                .and(\"fileSavePath\", \"=\", fileSavePath)\n                .findFirst();\n        if (downloadInfo != null) {\n            DownloadCallback callback = callbackMap.get(downloadInfo);\n            if (callback != null) {\n                if (viewHolder == null) {\n                    viewHolder = new DefaultDownloadViewHolder(null, downloadInfo);\n                }\n                if (callback.switchViewHolder(viewHolder)) {\n                    return;\n                } else {\n                    callback.cancel();\n                }\n            }\n        }\n\n        // create download info\n        if (downloadInfo == null) {\n            downloadInfo = new DownloadInfo();\n            downloadInfo.setUrl(url);\n            downloadInfo.setAutoRename(autoRename);\n            downloadInfo.setAutoResume(autoResume);\n            downloadInfo.setLabel(label);\n            downloadInfo.setFileSavePath(fileSavePath);\n            db.saveBindingId(downloadInfo);\n        }\n\n        // start downloading\n        if (viewHolder == null) {\n            viewHolder = new DefaultDownloadViewHolder(null, downloadInfo);\n        } else {\n            viewHolder.update(downloadInfo);\n        }\n        DownloadCallback callback = new DownloadCallback(viewHolder);\n        callback.setDownloadManager(this);\n        callback.switchViewHolder(viewHolder);\n        RequestParams params = new RequestParams(url);\n        params.setAutoResume(downloadInfo.isAutoResume());\n        params.setAutoRename(downloadInfo.isAutoRename());\n        params.setSaveFilePath(downloadInfo.getFileSavePath());\n        params.setExecutor(executor);\n        params.setCancelFast(true);\n        Callback.Cancelable cancelable = x.http().get(params, callback);\n        callback.setCancelable(cancelable);\n        callbackMap.put(downloadInfo, callback);\n\n        if (downloadInfoList.contains(downloadInfo)) {\n            int index = downloadInfoList.indexOf(downloadInfo);\n            downloadInfoList.remove(downloadInfo);\n            downloadInfoList.add(index, downloadInfo);\n        } else {\n            downloadInfoList.add(downloadInfo);\n        }\n    }\n\n    public void stopDownload(int index) {\n        DownloadInfo downloadInfo = downloadInfoList.get(index);\n        stopDownload(downloadInfo);\n    }\n\n    public void stopDownload(DownloadInfo downloadInfo) {\n        Callback.Cancelable cancelable = callbackMap.get(downloadInfo);\n        if (cancelable != null) {\n            cancelable.cancel();\n        }\n    }\n\n    public void stopAllDownload() {\n        for (DownloadInfo downloadInfo : downloadInfoList) {\n            Callback.Cancelable cancelable = callbackMap.get(downloadInfo);\n            if (cancelable != null) {\n                cancelable.cancel();\n            }\n        }\n    }\n\n    public void removeDownload(int index) throws DbException {\n        DownloadInfo downloadInfo = downloadInfoList.get(index);\n        db.delete(downloadInfo);\n        stopDownload(downloadInfo);\n        downloadInfoList.remove(index);\n    }\n\n    public void removeDownload(DownloadInfo downloadInfo) throws DbException {\n        db.delete(downloadInfo);\n        stopDownload(downloadInfo);\n        downloadInfoList.remove(downloadInfo);\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/download/DownloadState.java",
    "content": "package org.xutils.sample.download;\n\n/**\n * Created by wyouflf on 15/11/10.\n */\npublic enum DownloadState {\n    WAITING(0), STARTED(1), FINISHED(2), STOPPED(3), ERROR(4);\n\n    private final int value;\n\n    DownloadState(int value) {\n        this.value = value;\n    }\n\n    public int value() {\n        return value;\n    }\n\n    public static DownloadState valueOf(int value) {\n        switch (value) {\n            case 0:\n                return WAITING;\n            case 1:\n                return STARTED;\n            case 2:\n                return FINISHED;\n            case 3:\n                return STOPPED;\n            case 4:\n                return ERROR;\n            default:\n                return STOPPED;\n        }\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/download/DownloadStateConverter.java",
    "content": "package org.xutils.sample.download;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.converter.ColumnConverter;\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Created by wyouflf on 15/11/10.\n */\npublic class DownloadStateConverter implements ColumnConverter<DownloadState> {\n\n    @Override\n    public DownloadState getFieldValue(Cursor cursor, int index) {\n        int dbValue = cursor.getInt(index);\n        return DownloadState.valueOf(dbValue);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(DownloadState fieldValue) {\n        return fieldValue.value();\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/download/DownloadViewHolder.java",
    "content": "package org.xutils.sample.download;\n\nimport android.view.View;\n\nimport org.xutils.common.Callback;\nimport org.xutils.x;\n\nimport java.io.File;\n\n/**\n * Created by wyouflf on 15/11/10.\n */\npublic abstract class DownloadViewHolder {\n\n    protected DownloadInfo downloadInfo;\n\n    public DownloadViewHolder(View view, DownloadInfo downloadInfo) {\n        this.downloadInfo = downloadInfo;\n        x.view().inject(this, view);\n    }\n\n    public final DownloadInfo getDownloadInfo() {\n        return downloadInfo;\n    }\n\n    public void update(DownloadInfo downloadInfo) {\n        this.downloadInfo = downloadInfo;\n    }\n\n    public abstract void onWaiting();\n\n    public abstract void onStarted();\n\n    public abstract void onLoading(long total, long current);\n\n    public abstract void onSuccess(File result);\n\n    public abstract void onError(Throwable ex, boolean isOnCallback);\n\n    public abstract void onCancelled(Callback.CancelledException cex);\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/http/BaiduParams.java",
    "content": "package org.xutils.sample.http;\n\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.annotation.HttpRequest;\nimport org.xutils.http.app.DefaultParamsBuilder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by wyouflf on 15/11/4.\n */\n@HttpRequest(\n        host = \"https://www.baidu.com\",\n        path = \"s\",\n        builder = DefaultParamsBuilder.class/*可选参数, 控制参数构建过程, 定义参数签名, SSL证书等*/)\npublic class BaiduParams extends RequestParams {\n    public String wd;\n\n    // 数组参数 aa=1&aa=2&aa=4\n    public int[] aa = new int[]{1, 2, 4};\n    public List<String> bb = new ArrayList<String>();\n\n    public BaiduParams() {\n        bb.add(\"a\");\n        bb.add(\"c\");\n        // this.setMultipart(true); // 使用multipart表单\n        // this.setAsJsonContent(true); // 请求body将参数转换为json形式发送\n    }\n\n    //public long timestamp = System.currentTimeMillis();\n    //public File uploadFile; // 上传文件\n    //public List<File> files; // 上传文件数组\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/http/BaiduResponse.java",
    "content": "package org.xutils.sample.http;\n\nimport org.xutils.http.annotation.HttpResponse;\n\n/**\n * Created by wyouflf on 15/11/5.\n * json 返回值示例, 如果它作为Callback的泛型,\n * 那么xUtils将自动调用JsonResponseParser将字符串转换为BaiduResponse.\n *\n * @HttpResponse 注解 和 ResponseParser接口仅适合做json, xml等文本类型数据的解析,\n * 如果需要其他类型的解析可参考:\n * {@link org.xutils.http.loader.LoaderFactory}\n * 和 {@link org.xutils.common.Callback.PrepareCallback}.\n * LoaderFactory提供PrepareCallback第一个泛型参数类型的自动转换,\n * 第二个泛型参数需要在prepare方法中实现.\n * (LoaderFactory中已经默认提供了部分常用类型的转换实现, 其他类型需要自己注册.)\n */\n@HttpResponse(parser = JsonResponseParser.class)\npublic class BaiduResponse {\n    // some properties\n\n    private String test;\n\n    public String getTest() {\n        return test;\n    }\n\n    public void setTest(String test) {\n        this.test = test;\n    }\n\n    @Override\n    public String toString() {\n        return test;\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/http/JsonDemoParams.java",
    "content": "package org.xutils.sample.http;\n\nimport org.xutils.common.Callback;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.annotation.HttpRequest;\nimport org.xutils.x;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by wyouflf on 16/1/23.\n */\n@HttpRequest(\n        host = JsonDemoParamsBuilder.SEEVER_A,\n        path = \"query/test\",\n        builder = JsonDemoParamsBuilder.class\n)\npublic class JsonDemoParams extends RequestParams {\n\n    public String paramStr;\n\n    public int paramInt;\n\n    public List<String> paramList;\n\n\n    // 发送请求的示例\n    // 参数被JsonDemoParamsBuilder重新加工成json的形式发送.\n    // 示例项目的混淆配置会使这个类的字段不被混淆, 字段名作为参数名.\n    public static Callback.Cancelable send(Callback.CommonCallback<BaiduResponse> callback) {\n        JsonDemoParams params = new JsonDemoParams();\n        params.paramStr = \"test\";\n        params.paramInt = 10;\n        params.paramList = new ArrayList<String>();\n        params.paramList.add(\"test\");\n        return x.http().post(params, callback);\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/http/JsonDemoParamsBuilder.java",
    "content": "package org.xutils.sample.http;\n\nimport android.text.TextUtils;\n\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.annotation.HttpRequest;\nimport org.xutils.http.app.ParamsBuilder;\nimport org.xutils.x;\n\nimport java.util.HashMap;\n\nimport javax.net.ssl.SSLSocketFactory;\n\n/**\n * Created by wyouflf on 16/1/23.\n */\npublic class JsonDemoParamsBuilder implements ParamsBuilder {\n\n    public static final String SEEVER_A = \"a\";\n    public static final String SEEVER_B = \"b\";\n\n    private static final HashMap<String, String> SERVER_MAP = new HashMap<String, String>();\n\n    private static final HashMap<String, String> DEBUG_SERVER_MAP = new HashMap<String, String>();\n\n    static {\n        SERVER_MAP.put(SEEVER_A, \"http://a.xxx.xxx\");\n        SERVER_MAP.put(SEEVER_B, \"http://b.xxx.xxx\");\n        DEBUG_SERVER_MAP.put(SEEVER_A, \"http://debug.a.xxx.xxx\");\n        DEBUG_SERVER_MAP.put(SEEVER_B, \"http://debug.b.xxx.xxx\");\n    }\n\n    @Override\n    public String buildUri(RequestParams params, HttpRequest httpRequest) {\n        String url = getHost(httpRequest.host());\n        url += \"/\" + httpRequest.path();\n        return url;\n    }\n\n    @Override\n    public String buildCacheKey(RequestParams params, String[] cacheKeys) {\n        return null;\n    }\n\n    @Override\n    public SSLSocketFactory getSSLSocketFactory() {\n        return null;\n    }\n\n    @Override\n    public void buildParams(RequestParams params) {\n        // 添加公共参数\n        params.addParameter(\"common_a\", \"xxxx\");\n        params.addParameter(\"common_b\", \"xxxx\");\n\n\n        // 将post请求的body参数以json形式提交\n        params.setAsJsonContent(true);\n        // 或者query参数和body参数都json形式\n        /*String json = params.toJSONString();\n        params.clearParams();// 清空参数\n        if (params.getMethod() == HttpMethod.GET) {\n            params.addQueryStringParameter(\"xxx\", json);\n        } else {\n            params.setBodyContent(json);\n        }*/\n    }\n\n    @Override\n    public void buildSign(RequestParams params, String[] signs) {\n        params.addParameter(\"sign\", \"xxxx\");\n    }\n\n\n    private String getHost(String host) {\n        String result = null;\n        if (x.isDebug()) {\n            result = DEBUG_SERVER_MAP.get(host);\n        } else {\n            result = SERVER_MAP.get(host);\n        }\n        return TextUtils.isEmpty(result) ? host : result;\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/java/org/xutils/sample/http/JsonResponseParser.java",
    "content": "package org.xutils.sample.http;\n\nimport org.xutils.http.app.ResponseParser;\nimport org.xutils.http.request.UriRequest;\n\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Created by wyouflf on 15/11/5.\n */\npublic class JsonResponseParser implements ResponseParser {// 如果实现 InputStreamResponseParser, 可实现自定义流数据转换.\n\n    @Override\n    public void checkResponse(UriRequest request) throws Throwable {\n        // custom check ?\n        // get headers ?\n    }\n\n    /**\n     * 转换result为resultType类型的对象\n     *\n     * @param resultType  返回值类型(可能带有泛型信息)\n     * @param resultClass 返回值类型\n     * @param result      字符串数据\n     * @return\n     * @throws Throwable\n     */\n    @Override\n    public Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable {\n        // TODO: json to java bean\n        if (resultClass == List.class) {\n            // 这里只是个示例, 不做json转换.\n            List<BaiduResponse> list = new ArrayList<BaiduResponse>();\n            BaiduResponse baiduResponse = new BaiduResponse();\n            baiduResponse.setTest(result);\n            list.add(baiduResponse);\n            return list;\n            // fastJson 解析:\n            // return JSON.parseArray(result, (Class<?>) ParameterizedTypeUtil.getParameterizedType(resultType, List.class, 0));\n        } else {\n            // 这里只是个示例, 不做json转换.\n            BaiduResponse baiduResponse = new BaiduResponse();\n            baiduResponse.setTest(result);\n            return baiduResponse;\n            // fastjson 解析:\n            // return JSON.parseObject(result, resultClass);\n        }\n\n    }\n}\n"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/activity_big_image.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"org.xutils.sample.BigImageActivity\">\n\n    <ImageView\n        android:id=\"@+id/iv_big_img\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/activity_download.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"org.xutils.sample.DownloadActivity\">\n\n    <ListView\n        android:id=\"@+id/lv_download\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/main_content\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\"org.xutils.sample.MainActivity\">\n\n    <android.support.design.widget.AppBarLayout\n        android:id=\"@+id/appbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingTop=\"@dimen/appbar_padding_top\"\n        android:theme=\"@style/AppTheme.AppBarOverlay\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"?attr/colorPrimary\"\n            app:layout_scrollFlags=\"scroll|enterAlways\"\n            app:popupTheme=\"@style/AppTheme.PopupOverlay\">\n\n        </android.support.v7.widget.Toolbar>\n\n        <android.support.design.widget.TabLayout\n            android:id=\"@+id/tabs\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n\n    </android.support.design.widget.AppBarLayout>\n\n    <android.support.v4.view.ViewPager\n        android:id=\"@+id/container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\" />\n\n</android.support.design.widget.CoordinatorLayout>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/download_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n\n    <TextView\n        android:id=\"@+id/download_label\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:focusable=\"false\"\n        android:text=\"download_label\" />\n\n    <TextView\n        android:id=\"@+id/download_state\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_alignParentTop=\"true\"\n        android:focusable=\"false\"\n        android:text=\"download_state\" />\n\n    <ProgressBar\n        android:id=\"@+id/download_pb\"\n        style=\"?android:attr/progressBarStyleHorizontal\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/download_label\"\n        android:focusable=\"false\" />\n\n    <Button\n        android:id=\"@+id/download_stop_btn\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/download_pb\"\n        android:focusable=\"false\"\n        android:text=\"@string/stop\" />\n\n    <Button\n        android:id=\"@+id/download_remove_btn\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/download_pb\"\n        android:layout_toRightOf=\"@id/download_stop_btn\"\n        android:focusable=\"false\"\n        android:text=\"@string/remove\" />\n\n</RelativeLayout>"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/fragment_db.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <Button\n        android:id=\"@+id/btn_test_db\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"TEST DB\" />\n\n    <Button\n        android:id=\"@+id/btn_test_db2\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"TEST 性能\" />\n\n    <TextView\n        android:id=\"@+id/tv_db_result\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n</LinearLayout>"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/fragment_http.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <Button\n        android:id=\"@+id/btn_test1\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"TEST BAIDU 1\" />\n\n    <Button\n        android:id=\"@+id/btn_test2\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"UPLOAD TEST\" />\n\n    <EditText\n        android:id=\"@+id/et_url\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:singleLine=\"false\"\n        android:text=\"@string/test_download_url\" />\n\n    <Button\n        android:id=\"@+id/btn_test3\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"TEST DOWNLOAD\" />\n\n    <Button\n        android:id=\"@+id/btn_test4\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"OPEN DOWNLOAD LIST\" />\n\n    <Button\n        android:id=\"@+id/btn_test5\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"CACHE TEST\" />\n</LinearLayout>"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/fragment_image.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <ListView\n        android:id=\"@+id/lv_img\"\n        android:layout_width=\"fill_parent\"\n        android:layout_height=\"fill_parent\" />\n</LinearLayout>"
  },
  {
    "path": "xUtils3-master/src/main/res/layout/image_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"202dp\"\n    android:orientation=\"vertical\">\n\n    <ImageView\n        android:id=\"@+id/img_item\"\n        android:layout_width=\"200dp\"\n        android:layout_height=\"200dp\" />\n\n    <ProgressBar\n        android:id=\"@+id/img_pb\"\n        style=\"?android:attr/progressBarStyleHorizontal\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"2dp\" />\n</LinearLayout>"
  },
  {
    "path": "xUtils3-master/src/main/res/menu/menu_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\"org.xutils.sample.MainActivity\">\n    <item\n        android:id=\"@+id/action_settings\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_settings\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n    <dimen name=\"appbar_padding_top\">8dp</dimen>\n</resources>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">xUtils3 Sample</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"remove\">移除</string>\n    <string name=\"stop\">停止</string>\n    <string name=\"start\">开始下载</string>\n    <string name=\"test_download_url\">http://dl.bintray.com/wyouflf/maven/org/xutils/xutils/3.3.40/xutils-3.3.40.aar</string>\n</resources>"
  },
  {
    "path": "xUtils3-master/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n\n    <style name=\"AppTheme.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\" />\n\n    <style name=\"AppTheme.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\" />\n\n</resources>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/values-v21/styles.xml",
    "content": "<resources>>\n\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "xUtils3-master/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "xutils/.gitignore",
    "content": "# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Eclipse project files\n.classpath\n.project\nproject.properties\n\n# IDEA project files\n*.iml\n.idea/\n\n# OS generated files\n.DS_Store\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\n\n# others\nant.properties\nbuild.xml\nmap.txt"
  },
  {
    "path": "xutils/build.gradle",
    "content": "apply plugin: 'com.android.library'\n//apply plugin: 'com.github.dcendents.android-maven'\n//apply plugin: 'com.jfrog.bintray'\n//\n//def siteUrl = 'https://github.com/wyouflf/xUtils3'\n//def gitUrl = 'https://github.com/wyouflf/xUtils3.git'\n//group = \"org.xutils\"\n//version = \"3.3.40\"\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 23\n        versionCode 20161211\n        versionName \"3.3.40\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n        }\n    }\n    sourceSets {\n        main {\n            jniLibs.srcDirs = ['libs']\n            java.srcDirs = ['src/main/java', 'src/main/java_compat']\n        }\n    }\n}\n\n//install {\n//    repositories.mavenInstaller {\n//        // This generates POM.xml with proper parameters\n//        pom {\n//            project {\n//                packaging 'aar'\n//                name 'xUtils'\n//                description 'android orm, bitmap, http, view inject... '\n//                // #CONFIG# // project title\n//                url siteUrl\n//                // Set your license\n//                licenses {\n//                    license {\n//                        name 'The Apache Software License, Version 2.0'\n//                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n//                    }\n//                }\n//                developers {\n//                    developer {\n//                        id 'wyouflf'\n//                        // #CONFIG# // your user id (you can write your nickname)\n//                        name 'lei.jiao'\n//                        // #CONFIG# // your user name\n//                        email 'wyouflf@qq.com'\n//                        // #CONFIG# // your email\n//                    }\n//                }\n//                scm {\n//                    connection gitUrl\n//                    developerConnection gitUrl\n//                    url siteUrl\n//                }\n//            }\n//        }\n//    }\n//}\n//\n//task sourcesJar(type: Jar) {\n//    from android.sourceSets.main.java.srcDirs\n//    classifier = 'sources'\n//}\n//\n//task javadoc(type: Javadoc) {\n//    options.encoding = \"UTF-8\"\n//    source = android.sourceSets.main.java.srcDirs\n//    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n//}\n//\n//task javadocJar(type: Jar, dependsOn: javadoc) {\n//    classifier = 'javadoc'\n//    from javadoc.destinationDir\n//}\n//\n//artifacts {\n//    archives javadocJar\n//    archives sourcesJar\n//}\n//\n//Properties properties = new Properties()\n//properties.load(project.rootProject.file('local.properties').newDataInputStream())\n//bintray {\n//    user = properties.getProperty(\"bintray.user\")\n//    key = properties.getProperty(\"bintray.apikey\")\n//    configurations = ['archives']\n//    pkg {\n//        repo = \"maven\"\n//        name = \"xUtils\"\n//        // #CONFIG# project name in jcenter\n//        websiteUrl = siteUrl\n//        vcsUrl = gitUrl\n//        licenses = [\"Apache-2.0\"]\n//        publish = true\n//    }\n//}"
  },
  {
    "path": "xutils/docs/1.快速使用.md",
    "content": "##xUtils快速使用说明\n\n1. 导入依赖\n#### 使用Gradle构建时添加一下依赖即可:\n```javascript\ncompile 'org.xutils:xutils:3.3.22'\n```\n##### 如果使用eclipse可以 [点击这里下载aar文件](http://dl.bintray.com/wyouflf/maven/org/xutils/xutils/), 然后用zip解压, 取出jar包和so文件.\n\n2. 添加配置混淆设置\n################### region for xUtils\n-keepattributes Signature,*Annotation*\n-keep public class org.xutils.** {\n    public protected *;\n}\n-keep public interface org.xutils.** {\n    public protected *;\n}\n-keepclassmembers class * extends org.xutils.** {\n    public protected *;\n}\n-keepclassmembers @org.xutils.db.annotation.* class * {*;}\n-keepclassmembers @org.xutils.http.annotation.* class * {*;}\n-keepclassmembers class * {\n    @org.xutils.view.annotation.Event <methods>;\n}\n#################### end region\n\n3. 配置权限\n##### 需要的权限\n```xml\n<uses-permission android:name=\"android.permission.INTERNET\" />\n<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n```\n\n4. 初始化\n如果为初始化会报 `RuntimeException`: \"please invoke x.Ext.init(app) on Application#onCreate()\nand register your Application in manifest.\"\n#### 在`AndroidManifest.xml`中注册`自定义Application`, 之后加入下面的代码.\n```java\n// 在application的onCreate中初始化\n@Override\npublic void onCreate() {\n    super.onCreate();\n    x.Ext.init(this); // 这一步之后, 我们就可以在任何地方使用x.app()来获取Application的实例了.\n    x.Ext.setDebug(true); // 是否输出debug日志\n    ...\n}\n```\n\n## 现在可以开始使用xUtils了\n常用的API可以参考 [README](http://)\n接下来我们较为详细的介绍没一个模块的api和特性."
  },
  {
    "path": "xutils/docs/2.任务和回调.md",
    "content": "## xUtils中的任务和回调接口\n任务和回调是网络请求的基础接口, xUtils的任务接口支持异步和同步任务的实现.\n\n1. 为什么要设计新的接口, 而不使用Android系统API `AsyncTask`?\n    原因有几个方面, AsyncTask在各个版本系统中表现不一致, 包括默认任务的并发数, 是否可在子线程中使用等.\n2. xUtils中的任务和回调接口特点是什么?\n    * 可以在任何线程中直接使用.\n    * 支持设置优先级.\n    * 支持异步调用和同步调用.\n    * 默认支持FIFO和FILO, 也可以使用自定义Executor.\n    * 异步任务中所有错误(包括异步过程和回调操作)都会进入onError, 安全可靠.\n\n3. 使用xUtils中的任务使用\n```java\nx.task().start(task); // 执行一个异步任务\nresult = x.task().startSync(task); // 同步执行该任务\n```\n看到上面的接口是不是很简单, 定义一个任务后可以很方便的异步执行或同步执行. 接下来我们开始接受怎样定义一个任务.\n```java\n// task的hello world\nx.task().start(new AbsTask<String>() {\n    @Override\n    protected String doBackground() throws Throwable {\n        return \"hello world!\";\n    }\n\n    @Override\n    protected void onSuccess(String result) {\n        Log.d(\"test\", result);\n    }\n\n    @Override\n    protected void onError(Throwable ex, boolean isCallbackError) {\n        Log.e(\"test\", ex.getMessage(), ex);\n    }\n});\n```\n实例中使用task的基类AbsTask定义了一个task的实例,\n并使用异步的方式调用, `doBackground`在默认线程池中进行(后面会介绍怎样自定义线程池).\n`start`的返回值类型为`AbsTask<T>`(示例中T为String),\n但它并非被执行的task的实例, 而是task的代理, 对代理实例的所有方法调用不必关心线程的问题, 框架会自动处理.\n如果使用`startSync`执行这个任务, 则`doBackground`在当前线程同步执行, 并且`doBackground`的返回值作为`startSync`的返回值.\n\n4. AbsTask的重要属性和方法(以`on`开头的方法为UI线程的回调方法, 简称`回调方法`)\n    * `doBackground` 执行任务的主要, 上面已经介绍了它的特性, 虽然它可以同步执行但仍然被这样命名.\n    * `onWaiting` 任务`start`或`startSync`之后会立即执行, 任务尚未被分配执行线程.\n    * `onStarted` 任务被分配执行线程后立即执行, 接下来会执行`doBackground`.\n    * `onSuccess` 在`doBackground`执行完成后立即执行.\n    * `onCancelled` 任务`onSuccess`之前任何线程调用`AbsTask#cancel`或`doBackground`中抛出`CancelledException`之后执行.\n    * `onUpdate(int flag, Object... args)` 任何线程调用了`AbsTask#update(int flag, Object... args)`之后执行.\n    * `onError(Throwable ex, boolean isCallbackError)` 以上任何方法中出现异常时执行, 参数isCallbackError表示是否`回调方法`中的错误.\n    * `onFinished` 总是在最终调用, 即使任务出现error会被cancel.\n\n5. 取消任务 AbsTask#cancel()\n示例:\n```java\nAbsTask<?> proxy = x.task().start(task);\n...\nproxy.cancel(); // 在任何线程调用\n// 如果在任务内部取消, 建议使用抛出CancelledException的方法, 这样可以使用message或自定义的CancelledException表明取消的原因.\n...\n```\n\n6. 自定义更新通知 AbsTask#update(int flag, Object... args)\n示例:\n```java\nx.task().start(new AbsTask<String>() {\n\n    private static final int FLAG_UPDATE = 1;\n\n    @Override\n    protected String doBackground() throws Throwable {\n        this.update(FLAG_UPDATE, 0);\n        ...\n        this.update(FLAG_UPDATE, 50);\n        ...\n        this.update(FLAG_UPDATE, 100);\n        return \"hello world!\";\n    }\n\n    @Override\n    protected void onUpdate(int flag, Object... args) {\n        switch (flag) {\n            case FLAG_UPDATE: {\n                if (args == null || args.length != 1) return;\n                int value = (Integer) args[0];\n                ...\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n\n    ...\n});\n```\n\n7. 设置优先级, 覆盖方法 AbsTask#getPriority\n\n8. 设置自定义线程池, 覆盖方法 AbsTask#getExecutor\n\n9. 取消任务时是否不等待任务彻底结束, 立即收到取消的通知, 覆盖方法 AbsTask#isCancelFast\n\n10. 任务是否被取消 `final` AbsTask#isCancelled,\n\n11. 任务是都已经执行完成 `final` AbsTask#isFinished\n\n12. 获取任务的状态 `final` AbsTask#getState\n\n13. 获取执行结果 `final` AbsTask#getResult\n\n14. 其他, 使用AbsTask(Callback.Cancelable cancelHandler)构造函数可自定义可扩展的取消逻辑."
  },
  {
    "path": "xutils/docs/3.网络请求.md",
    "content": "## xUtils中的网络请求使用\n网络请求接口主要分两部分介绍, 基础请求的实现和自定义参数和返回值模板的应用.\n\n\n\n### 基础请求的实现\n\n\n\n\n### 自定义参数和返回值模板的应用"
  },
  {
    "path": "xutils/docs/4.图片绑定.md",
    "content": ""
  },
  {
    "path": "xutils/docs/5.Sqlite数据库使用.md",
    "content": ""
  },
  {
    "path": "xutils/docs/6.View注入.md",
    "content": ""
  },
  {
    "path": "xutils/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"org.xutils\">\n\n    <application />\n\n</manifest>\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/DbManager.java",
    "content": "package org.xutils;\n\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.text.TextUtils;\n\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.db.Selector;\nimport org.xutils.db.sqlite.SqlInfo;\nimport org.xutils.db.sqlite.WhereBuilder;\nimport org.xutils.db.table.DbModel;\nimport org.xutils.db.table.TableEntity;\nimport org.xutils.ex.DbException;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.List;\n\n/**\n * 数据库访问接口\n */\npublic interface DbManager extends Closeable {\n\n    DaoConfig getDaoConfig();\n\n    SQLiteDatabase getDatabase();\n\n    /**\n     * 保存实体类或实体类的List到数据库,\n     * 如果该类型的id是自动生成的, 则保存完后会给id赋值.\n     *\n     * @param entity\n     * @return\n     * @throws DbException\n     */\n    boolean saveBindingId(Object entity) throws DbException;\n\n    /**\n     * 保存或更新实体类或实体类的List到数据库, 根据id对应的数据是否存在.\n     *\n     * @param entity\n     * @throws DbException\n     */\n    void saveOrUpdate(Object entity) throws DbException;\n\n    /**\n     * 保存实体类或实体类的List到数据库\n     *\n     * @param entity\n     * @throws DbException\n     */\n    void save(Object entity) throws DbException;\n\n    /**\n     * 保存或更新实体类或实体类的List到数据库, 根据id和其他唯一索引判断数据是否存在.\n     *\n     * @param entity\n     * @throws DbException\n     */\n    void replace(Object entity) throws DbException;\n\n    ///////////// delete\n    void deleteById(Class<?> entityType, Object idValue) throws DbException;\n\n    void delete(Object entity) throws DbException;\n\n    void delete(Class<?> entityType) throws DbException;\n\n    int delete(Class<?> entityType, WhereBuilder whereBuilder) throws DbException;\n\n    ///////////// update\n    void update(Object entity, String... updateColumnNames) throws DbException;\n\n    int update(Class<?> entityType, WhereBuilder whereBuilder, KeyValue... nameValuePairs) throws DbException;\n\n    ///////////// find\n    <T> T findById(Class<T> entityType, Object idValue) throws DbException;\n\n    <T> T findFirst(Class<T> entityType) throws DbException;\n\n    <T> List<T> findAll(Class<T> entityType) throws DbException;\n\n    <T> Selector<T> selector(Class<T> entityType) throws DbException;\n\n    DbModel findDbModelFirst(SqlInfo sqlInfo) throws DbException;\n\n    List<DbModel> findDbModelAll(SqlInfo sqlInfo) throws DbException;\n\n    ///////////// table\n\n    /**\n     * 获取表信息\n     *\n     * @param entityType\n     * @param <T>\n     * @return\n     * @throws DbException\n     */\n    <T> TableEntity<T> getTable(Class<T> entityType) throws DbException;\n\n    /**\n     * 删除表\n     *\n     * @param entityType\n     * @throws DbException\n     */\n    void dropTable(Class<?> entityType) throws DbException;\n\n    /**\n     * 添加一列,\n     * 新的entityType中必须定义了这个列的属性.\n     *\n     * @param entityType\n     * @param column\n     * @throws DbException\n     */\n    void addColumn(Class<?> entityType, String column) throws DbException;\n\n    ///////////// db\n\n    /**\n     * 删除库\n     *\n     * @throws DbException\n     */\n    void dropDb() throws DbException;\n\n    /**\n     * 关闭数据库,\n     * xUtils对同一个库的链接是单实例的, 一般不需要关闭它.\n     *\n     * @throws IOException\n     */\n    void close() throws IOException;\n\n    ///////////// custom\n    int executeUpdateDelete(SqlInfo sqlInfo) throws DbException;\n\n    int executeUpdateDelete(String sql) throws DbException;\n\n    void execNonQuery(SqlInfo sqlInfo) throws DbException;\n\n    void execNonQuery(String sql) throws DbException;\n\n    Cursor execQuery(SqlInfo sqlInfo) throws DbException;\n\n    Cursor execQuery(String sql) throws DbException;\n\n    public interface DbOpenListener {\n        void onDbOpened(DbManager db);\n    }\n\n    public interface DbUpgradeListener {\n        void onUpgrade(DbManager db, int oldVersion, int newVersion);\n    }\n\n    public interface TableCreateListener {\n        void onTableCreated(DbManager db, TableEntity<?> table);\n    }\n\n    public static class DaoConfig {\n        private File dbDir;\n        private String dbName = \"xUtils.db\"; // default db name\n        private int dbVersion = 1;\n        private boolean allowTransaction = true;\n        private DbUpgradeListener dbUpgradeListener;\n        private TableCreateListener tableCreateListener;\n        private DbOpenListener dbOpenListener;\n\n        public DaoConfig() {\n        }\n\n        public DaoConfig setDbDir(File dbDir) {\n            this.dbDir = dbDir;\n            return this;\n        }\n\n        public DaoConfig setDbName(String dbName) {\n            if (!TextUtils.isEmpty(dbName)) {\n                this.dbName = dbName;\n            }\n            return this;\n        }\n\n        public DaoConfig setDbVersion(int dbVersion) {\n            this.dbVersion = dbVersion;\n            return this;\n        }\n\n        public DaoConfig setAllowTransaction(boolean allowTransaction) {\n            this.allowTransaction = allowTransaction;\n            return this;\n        }\n\n        public DaoConfig setDbOpenListener(DbOpenListener dbOpenListener) {\n            this.dbOpenListener = dbOpenListener;\n            return this;\n        }\n\n        public DaoConfig setDbUpgradeListener(DbUpgradeListener dbUpgradeListener) {\n            this.dbUpgradeListener = dbUpgradeListener;\n            return this;\n        }\n\n        public DaoConfig setTableCreateListener(TableCreateListener tableCreateListener) {\n            this.tableCreateListener = tableCreateListener;\n            return this;\n        }\n\n        public File getDbDir() {\n            return dbDir;\n        }\n\n        public String getDbName() {\n            return dbName;\n        }\n\n        public int getDbVersion() {\n            return dbVersion;\n        }\n\n        public boolean isAllowTransaction() {\n            return allowTransaction;\n        }\n\n        public DbOpenListener getDbOpenListener() {\n            return dbOpenListener;\n        }\n\n        public DbUpgradeListener getDbUpgradeListener() {\n            return dbUpgradeListener;\n        }\n\n        public TableCreateListener getTableCreateListener() {\n            return tableCreateListener;\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            if (this == o) return true;\n            if (o == null || getClass() != o.getClass()) return false;\n\n            DaoConfig daoConfig = (DaoConfig) o;\n\n            if (!dbName.equals(daoConfig.dbName)) return false;\n            return dbDir == null ? daoConfig.dbDir == null : dbDir.equals(daoConfig.dbDir);\n        }\n\n        @Override\n        public int hashCode() {\n            int result = dbName.hashCode();\n            result = 31 * result + (dbDir != null ? dbDir.hashCode() : 0);\n            return result;\n        }\n\n        @Override\n        public String toString() {\n            return String.valueOf(dbDir) + \"/\" + dbName;\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/HttpManager.java",
    "content": "package org.xutils;\n\nimport org.xutils.common.Callback;\nimport org.xutils.http.HttpMethod;\nimport org.xutils.http.RequestParams;\n\n/**\n * Created by wyouflf on 15/6/17.\n * http请求接口\n */\npublic interface HttpManager {\n\n    /**\n     * 异步GET请求\n     *\n     * @param entity\n     * @param callback\n     * @param <T>\n     * @return\n     */\n    <T> Callback.Cancelable get(RequestParams entity, Callback.CommonCallback<T> callback);\n\n    /**\n     * 异步POST请求\n     *\n     * @param entity\n     * @param callback\n     * @param <T>\n     * @return\n     */\n    <T> Callback.Cancelable post(RequestParams entity, Callback.CommonCallback<T> callback);\n\n    /**\n     * 异步请求\n     *\n     * @param method\n     * @param entity\n     * @param callback\n     * @param <T>\n     * @return\n     */\n    <T> Callback.Cancelable request(HttpMethod method, RequestParams entity, Callback.CommonCallback<T> callback);\n\n\n    /**\n     * 同步GET请求\n     *\n     * @param entity\n     * @param resultType\n     * @param <T>\n     * @return\n     * @throws Throwable\n     */\n    <T> T getSync(RequestParams entity, Class<T> resultType) throws Throwable;\n\n    /**\n     * 同步POST请求\n     *\n     * @param entity\n     * @param resultType\n     * @param <T>\n     * @return\n     * @throws Throwable\n     */\n    <T> T postSync(RequestParams entity, Class<T> resultType) throws Throwable;\n\n    /**\n     * 同步请求\n     *\n     * @param method\n     * @param entity\n     * @param resultType\n     * @param <T>\n     * @return\n     * @throws Throwable\n     */\n    <T> T requestSync(HttpMethod method, RequestParams entity, Class<T> resultType) throws Throwable;\n\n    /**\n     * 同步请求\n     *\n     * @param method\n     * @param entity\n     * @param callback\n     * @param <T>\n     * @return\n     * @throws Throwable\n     */\n    <T> T requestSync(HttpMethod method, RequestParams entity, Callback.TypedCallback<T> callback) throws Throwable;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/ImageManager.java",
    "content": "package org.xutils;\n\nimport android.graphics.drawable.Drawable;\nimport android.widget.ImageView;\n\nimport org.xutils.common.Callback;\nimport org.xutils.image.ImageOptions;\n\nimport java.io.File;\n\n/**\n * Created by wyouflf on 15/6/17.\n * 图片绑定接口\n */\npublic interface ImageManager {\n\n    void bind(ImageView view, String url);\n\n    void bind(ImageView view, String url, ImageOptions options);\n\n    void bind(ImageView view, String url, Callback.CommonCallback<Drawable> callback);\n\n    void bind(ImageView view, String url, ImageOptions options, Callback.CommonCallback<Drawable> callback);\n\n    Callback.Cancelable loadDrawable(String url, ImageOptions options, Callback.CommonCallback<Drawable> callback);\n\n    Callback.Cancelable loadFile(String url, ImageOptions options, Callback.CacheCallback<File> callback);\n\n    void clearMemCache();\n\n    void clearCacheFiles();\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/ViewInjector.java",
    "content": "package org.xutils;\n\nimport android.app.Activity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\n/**\n * Created by wyouflf on 15/10/29.\n * view注入接口\n */\npublic interface ViewInjector {\n\n    /**\n     * 注入view\n     *\n     * @param view\n     */\n    void inject(View view);\n\n    /**\n     * 注入activity\n     *\n     * @param activity\n     */\n    void inject(Activity activity);\n\n    /**\n     * 注入view holder\n     *\n     * @param handler view holder\n     * @param view\n     */\n    void inject(Object handler, View view);\n\n    /**\n     * 注入fragment\n     *\n     * @param fragment\n     * @param inflater\n     * @param container\n     * @return\n     */\n    View inject(Object fragment, LayoutInflater inflater, ViewGroup container);\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/cache/DiskCacheEntity.java",
    "content": "package org.xutils.cache;\n\nimport org.xutils.db.annotation.Column;\nimport org.xutils.db.annotation.Table;\n\nimport java.util.Date;\n\n/**\n * Created by wyouflf on 15/8/2.\n * 磁盘缓存对象\n */\n@Table(name = \"disk_cache\")\npublic final class DiskCacheEntity {\n\n    @Column(name = \"id\", isId = true)\n    private long id;\n\n    @Column(name = \"key\", property = \"UNIQUE\")\n    private String key;\n\n    @Column(name = \"path\")\n    private String path;\n\n    @Column(name = \"textContent\")\n    private String textContent;\n\n    // from \"max-age\" (since http 1.1)\n    @Column(name = \"expires\")\n    private long expires = Long.MAX_VALUE;\n\n    @Column(name = \"etag\")\n    private String etag;\n\n    @Column(name = \"hits\")\n    private long hits;\n\n    @Column(name = \"lastModify\")\n    private Date lastModify;\n\n    @Column(name = \"lastAccess\")\n    private long lastAccess;\n\n\n    public DiskCacheEntity() {\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    /*package*/ String getPath() {\n        return path;\n    }\n\n    /*package*/ void setPath(String path) {\n        this.path = path;\n    }\n\n    public String getTextContent() {\n        return textContent;\n    }\n\n    public void setTextContent(String textContent) {\n        this.textContent = textContent;\n    }\n\n    public long getExpires() {\n        return expires;\n    }\n\n    public void setExpires(long expires) {\n        this.expires = expires;\n    }\n\n    public String getEtag() {\n        return etag;\n    }\n\n    public void setEtag(String etag) {\n        this.etag = etag;\n    }\n\n    public long getHits() {\n        return hits;\n    }\n\n    public void setHits(long hits) {\n        this.hits = hits;\n    }\n\n    public Date getLastModify() {\n        return lastModify;\n    }\n\n    public void setLastModify(Date lastModify) {\n        this.lastModify = lastModify;\n    }\n\n    public long getLastAccess() {\n        return lastAccess == 0 ? System.currentTimeMillis() : lastAccess;\n    }\n\n    public void setLastAccess(long lastAccess) {\n        this.lastAccess = lastAccess;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/cache/DiskCacheFile.java",
    "content": "package org.xutils.cache;\n\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.ProcessLock;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\n\n/**\n * Created by wyouflf on 15/8/3.\n * 磁盘缓存文件, 操作完成后必须及时调用close()方法关闭.\n */\npublic final class DiskCacheFile extends File implements Closeable {\n\n    /*package*/ DiskCacheEntity cacheEntity;\n    /*package*/ ProcessLock lock;\n\n    /**\n     * @param cacheEntity\n     * @param path\n     * @param lock        lock name: path\n     */\n    /*package*/ DiskCacheFile(DiskCacheEntity cacheEntity, String path, ProcessLock lock) {\n        super(path);\n        this.cacheEntity = cacheEntity;\n        this.lock = lock;\n    }\n\n    @Override\n    public void close() throws IOException {\n        IOUtil.closeQuietly(lock);\n    }\n\n    public DiskCacheFile commit() throws IOException {\n        return getDiskCache().commitDiskCacheFile(this);\n    }\n\n    public LruDiskCache getDiskCache() {\n        String dirName = this.getParentFile().getName();\n        return LruDiskCache.getDiskCache(dirName);\n    }\n\n    public DiskCacheEntity getCacheEntity() {\n        return cacheEntity;\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        super.finalize();\n        this.close();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/cache/LruCache.java",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.cache;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * Static library version of {@link android.util.LruCache}. Used to write apps\n * that run on API levels prior to 12. When running on API level 12 or above,\n * this implementation is still used; it does not try to switch to the\n * framework's implementation. See the framework SDK documentation for a class\n * overview.\n */\npublic class LruCache<K, V> {\n    private final LinkedHashMap<K, V> map;\n\n    /**\n     * Size of this cache in units. Not necessarily the number of elements.\n     */\n    private int size;\n    private int maxSize;\n\n    private int putCount;\n    private int createCount;\n    private int evictionCount;\n    private int hitCount;\n    private int missCount;\n\n    /**\n     * @param maxSize for caches that do not override {@link #sizeOf}, this is\n     *                the maximum number of entries in the cache. For all other caches,\n     *                this is the maximum sum of the sizes of the entries in this cache.\n     */\n    public LruCache(int maxSize) {\n        if (maxSize <= 0) {\n            throw new IllegalArgumentException(\"maxSize <= 0\");\n        }\n        this.maxSize = maxSize;\n        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);\n    }\n\n    /**\n     * Sets the size of the cache.\n     *\n     * @param maxSize The new maximum size.\n     */\n    public void resize(int maxSize) {\n        if (maxSize <= 0) {\n            throw new IllegalArgumentException(\"maxSize <= 0\");\n        }\n\n        synchronized (this) {\n            this.maxSize = maxSize;\n        }\n        trimToSize(maxSize);\n    }\n\n    /**\n     * Returns the value for {@code key} if it exists in the cache or can be\n     * created by {@code #create}. If a value was returned, it is moved to the\n     * head of the queue. This returns null if a value is not cached and cannot\n     * be created.\n     */\n    public final V get(K key) {\n        if (key == null) {\n            throw new NullPointerException(\"key == null\");\n        }\n\n        V mapValue;\n        synchronized (this) {\n            mapValue = map.get(key);\n            if (mapValue != null) {\n                hitCount++;\n                return mapValue;\n            }\n            missCount++;\n        }\n\n        /*\n         * Attempt to create a value. This may take a long time, and the map\n         * may be different when create() returns. If a conflicting value was\n         * added to the map while create() was working, we leave that value in\n         * the map and release the created value.\n         */\n\n        V createdValue = create(key);\n        if (createdValue == null) {\n            return null;\n        }\n\n        synchronized (this) {\n            createCount++;\n            mapValue = map.put(key, createdValue);\n\n            if (mapValue != null) {\n                // There was a conflict so undo that last put\n                map.put(key, mapValue);\n            } else {\n                size += safeSizeOf(key, createdValue);\n            }\n        }\n\n        if (mapValue != null) {\n            entryRemoved(false, key, createdValue, mapValue);\n            return mapValue;\n        } else {\n            trimToSize(maxSize);\n            return createdValue;\n        }\n    }\n\n    /**\n     * Caches {@code value} for {@code key}. The value is moved to the head of\n     * the queue.\n     *\n     * @return the previous value mapped by {@code key}.\n     */\n    public final V put(K key, V value) {\n        if (key == null || value == null) {\n            throw new NullPointerException(\"key == null || value == null\");\n        }\n\n        V previous;\n        synchronized (this) {\n            putCount++;\n            size += safeSizeOf(key, value);\n            previous = map.put(key, value);\n            if (previous != null) {\n                size -= safeSizeOf(key, previous);\n            }\n        }\n\n        if (previous != null) {\n            entryRemoved(false, key, previous, value);\n        }\n\n        trimToSize(maxSize);\n        return previous;\n    }\n\n    /**\n     * Remove the eldest entries until the total of remaining entries is at or\n     * below the requested size.\n     *\n     * @param maxSize the maximum size of the cache before returning. May be -1\n     *                to evict even 0-sized elements.\n     */\n    public void trimToSize(int maxSize) {\n        while (true) {\n            K key;\n            V value;\n            synchronized (this) {\n                if (size < 0 || (map.isEmpty() && size != 0)) {\n                    throw new IllegalStateException(getClass().getName()\n                            + \".sizeOf() is reporting inconsistent results!\");\n                }\n\n                if (size <= maxSize || map.isEmpty()) {\n                    break;\n                }\n\n                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();\n                key = toEvict.getKey();\n                value = toEvict.getValue();\n                map.remove(key);\n                size -= safeSizeOf(key, value);\n                evictionCount++;\n            }\n\n            entryRemoved(true, key, value, null);\n        }\n    }\n\n    /**\n     * Removes the entry for {@code key} if it exists.\n     *\n     * @return the previous value mapped by {@code key}.\n     */\n    public final V remove(K key) {\n        if (key == null) {\n            throw new NullPointerException(\"key == null\");\n        }\n\n        V previous;\n        synchronized (this) {\n            previous = map.remove(key);\n            if (previous != null) {\n                size -= safeSizeOf(key, previous);\n            }\n        }\n\n        if (previous != null) {\n            entryRemoved(false, key, previous, null);\n        }\n\n        return previous;\n    }\n\n    /**\n     * Called for entries that have been evicted or removed. This method is\n     * invoked when a value is evicted to make space, removed by a call to\n     * {@link #remove}, or replaced by a call to {@link #put}. The default\n     * implementation does nothing.\n     * <p>The method is called without synchronization: other threads may\n     * access the cache while this method is executing.\n     *\n     * @param evicted  true if the entry is being removed to make space, false\n     *                 if the removal was caused by a {@link #put} or {@link #remove}.\n     * @param newValue the new value for {@code key}, if it exists. If non-null,\n     *                 this removal was caused by a {@link #put}. Otherwise it was caused by\n     *                 an eviction or a {@link #remove}.\n     */\n    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {\n    }\n\n    /**\n     * Called after a cache miss to compute a value for the corresponding key.\n     * Returns the computed value or null if no value can be computed. The\n     * default implementation returns null.\n     * <p>The method is called without synchronization: other threads may\n     * access the cache while this method is executing.\n     * <p>If a value for {@code key} exists in the cache when this method\n     * returns, the created value will be released with {@link #entryRemoved}\n     * and discarded. This can occur when multiple threads request the same key\n     * at the same time (causing multiple values to be created), or when one\n     * thread calls {@link #put} while another is creating a value for the same\n     * key.\n     */\n    protected V create(K key) {\n        return null;\n    }\n\n    private int safeSizeOf(K key, V value) {\n        int result = sizeOf(key, value);\n        if (result < 0) {\n            throw new IllegalStateException(\"Negative size: \" + key + \"=\" + value);\n        }\n        return result;\n    }\n\n    /**\n     * Returns the size of the entry for {@code key} and {@code value} in\n     * user-defined units.  The default implementation returns 1 so that size\n     * is the number of entries and max size is the maximum number of entries.\n     * <p>An entry's size must not change while it is in the cache.\n     */\n    protected int sizeOf(K key, V value) {\n        return 1;\n    }\n\n    /**\n     * Clear the cache, calling {@link #entryRemoved} on each removed entry.\n     */\n    public final void evictAll() {\n        trimToSize(-1); // -1 will evict 0-sized elements\n    }\n\n    /**\n     * For caches that do not override {@link #sizeOf}, this returns the number\n     * of entries in the cache. For all other caches, this returns the sum of\n     * the sizes of the entries in this cache.\n     */\n    public synchronized final int size() {\n        return size;\n    }\n\n    /**\n     * For caches that do not override {@link #sizeOf}, this returns the maximum\n     * number of entries in the cache. For all other caches, this returns the\n     * maximum sum of the sizes of the entries in this cache.\n     */\n    public synchronized final int maxSize() {\n        return maxSize;\n    }\n\n    /**\n     * Returns the number of times {@link #get} returned a value that was\n     * already present in the cache.\n     */\n    public synchronized final int hitCount() {\n        return hitCount;\n    }\n\n    /**\n     * Returns the number of times {@link #get} returned null or required a new\n     * value to be created.\n     */\n    public synchronized final int missCount() {\n        return missCount;\n    }\n\n    /**\n     * Returns the number of times {@link #create(Object)} returned a value.\n     */\n    public synchronized final int createCount() {\n        return createCount;\n    }\n\n    /**\n     * Returns the number of times {@link #put} was called.\n     */\n    public synchronized final int putCount() {\n        return putCount;\n    }\n\n    /**\n     * Returns the number of values that have been evicted.\n     */\n    public synchronized final int evictionCount() {\n        return evictionCount;\n    }\n\n    /**\n     * Returns a copy of the current contents of the cache, ordered from least\n     * recently accessed to most recently accessed.\n     */\n    public synchronized final Map<K, V> snapshot() {\n        return new LinkedHashMap<K, V>(map);\n    }\n\n    @Override\n    public synchronized final String toString() {\n        int accesses = hitCount + missCount;\n        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;\n        return String.format(\"LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]\",\n                maxSize, hitCount, missCount, hitPercent);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/cache/LruDiskCache.java",
    "content": "package org.xutils.cache;\n\n\nimport android.text.TextUtils;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.task.PriorityExecutor;\nimport org.xutils.common.util.FileUtil;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.common.util.MD5;\nimport org.xutils.common.util.ProcessLock;\nimport org.xutils.config.DbConfigs;\nimport org.xutils.db.sqlite.WhereBuilder;\nimport org.xutils.ex.DbException;\nimport org.xutils.ex.FileLockedException;\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.concurrent.Executor;\n\n/**\n * Created by wyouflf on 15/7/23.\n * 使用sqlite索引实现的LruDiskCache\n */\npublic final class LruDiskCache {\n\n    private static final HashMap<String, LruDiskCache> DISK_CACHE_MAP = new HashMap<String, LruDiskCache>(5);\n\n    private static final int LIMIT_COUNT = 5000; // 限制最多5000条数据\n    private static final long LIMIT_SIZE = 1024L * 1024L * 100L; // 限制最多100M文件\n\n    private static final int LOCK_WAIT = 1000 * 3; // 3s\n    private static final String CACHE_DIR_NAME = \"xUtils_cache\";\n    private static final String TEMP_FILE_SUFFIX = \".tmp\";\n\n    private boolean available = false;\n    private final DbManager cacheDb;\n    private File cacheDir;\n    private long diskCacheSize = LIMIT_SIZE;\n    private final Executor trimExecutor = new PriorityExecutor(1, true);\n\n    private long lastTrimTime = 0L;\n    private static final long TRIM_TIME_SPAN = 1000;\n\n    public synchronized static LruDiskCache getDiskCache(String dirName) {\n        if (TextUtils.isEmpty(dirName)) dirName = CACHE_DIR_NAME;\n        LruDiskCache cache = DISK_CACHE_MAP.get(dirName);\n        if (cache == null) {\n            cache = new LruDiskCache(dirName);\n            DISK_CACHE_MAP.put(dirName, cache);\n        }\n        return cache;\n    }\n\n    private LruDiskCache(String dirName) {\n        this.cacheDb = x.getDb(DbConfigs.HTTP.getConfig());\n        this.cacheDir = FileUtil.getCacheDir(dirName);\n        if (this.cacheDir != null && (this.cacheDir.exists() || this.cacheDir.mkdirs())) {\n            available = true;\n        }\n        deleteNoIndexFiles();\n    }\n\n    public LruDiskCache setMaxSize(long maxSize) {\n        if (maxSize > 0L) {\n            long diskFreeSize = FileUtil.getDiskAvailableSize();\n            if (diskFreeSize > maxSize) {\n                diskCacheSize = maxSize;\n            } else {\n                diskCacheSize = diskFreeSize;\n            }\n        }\n        return this;\n    }\n\n    public DiskCacheEntity get(String key) {\n        if (!available || TextUtils.isEmpty(key)) return null;\n\n        DiskCacheEntity result = null;\n        try {\n            result = this.cacheDb.selector(DiskCacheEntity.class)\n                    .where(\"key\", \"=\", key).findFirst();\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n        if (result != null) {\n\n            if (result.getExpires() < System.currentTimeMillis()) {\n                return null;\n            }\n\n            { // update hint & lastAccess...\n                final DiskCacheEntity finalResult = result;\n                trimExecutor.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        finalResult.setHits(finalResult.getHits() + 1);\n                        finalResult.setLastAccess(System.currentTimeMillis());\n                        try {\n                            cacheDb.update(finalResult, \"hits\", \"lastAccess\");\n                        } catch (Throwable ex) {\n                            LogUtil.e(ex.getMessage(), ex);\n                        }\n                    }\n                });\n            }\n\n        }\n\n        return result;\n    }\n\n    public void put(DiskCacheEntity entity) {\n        if (!available\n                || entity == null\n                || TextUtils.isEmpty(entity.getTextContent())\n                || entity.getExpires() < System.currentTimeMillis()) {\n            return;\n        }\n\n        try {\n            cacheDb.replace(entity);\n        } catch (DbException ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n        trimSize();\n    }\n\n    public DiskCacheFile getDiskCacheFile(String key) throws InterruptedException {\n        if (!available || TextUtils.isEmpty(key)) {\n            return null;\n        }\n\n        DiskCacheFile result = null;\n        DiskCacheEntity entity = get(key);\n        if (entity != null && new File(entity.getPath()).exists()) {\n            ProcessLock processLock = ProcessLock.tryLock(entity.getPath(), false, LOCK_WAIT);\n            if (processLock != null && processLock.isValid()) {\n                result = new DiskCacheFile(entity, entity.getPath(), processLock);\n                if (!result.exists()) {\n                    try {\n                        cacheDb.delete(entity);\n                    } catch (DbException ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                    result = null;\n                }\n            }\n        }\n\n        return result;\n    }\n\n    public DiskCacheFile createDiskCacheFile(DiskCacheEntity entity) throws IOException {\n        if (!available || entity == null) {\n            return null;\n        }\n\n        DiskCacheFile result = null;\n\n        entity.setPath(new File(this.cacheDir, MD5.md5(entity.getKey())).getAbsolutePath());\n        String tempFilePath = entity.getPath() + TEMP_FILE_SUFFIX;\n        ProcessLock processLock = ProcessLock.tryLock(tempFilePath, true);\n        if (processLock != null && processLock.isValid()) {\n            result = new DiskCacheFile(entity, tempFilePath, processLock);\n            if (!result.getParentFile().exists()) {\n                result.mkdirs();\n            }\n        } else {\n            throw new FileLockedException(entity.getPath());\n        }\n\n        return result;\n    }\n\n    public void clearCacheFiles() {\n        IOUtil.deleteFileOrDir(cacheDir);\n    }\n\n    /**\n     * 添加缓存文件\n     *\n     * @param cacheFile\n     */\n    /*package*/ DiskCacheFile commitDiskCacheFile(DiskCacheFile cacheFile) throws IOException {\n        if (cacheFile != null && cacheFile.length() < 1L) {\n            IOUtil.closeQuietly(cacheFile);\n            return null;\n        }\n        if (!available || cacheFile == null) {\n            return null;\n        }\n\n        DiskCacheFile result = null;\n        DiskCacheEntity cacheEntity = cacheFile.cacheEntity;\n        if (cacheFile.getName().endsWith(TEMP_FILE_SUFFIX)) { // is temp file\n            ProcessLock processLock = null;\n            DiskCacheFile destFile = null;\n            try {\n                String destPath = cacheEntity.getPath();\n                processLock = ProcessLock.tryLock(destPath, true, LOCK_WAIT);\n                if (processLock != null && processLock.isValid()) { // lock\n                    destFile = new DiskCacheFile(cacheEntity, destPath, processLock);\n                    if (cacheFile.renameTo(destFile)) {\n                        try {\n                            result = destFile;\n                            cacheDb.replace(cacheEntity);\n                        } catch (DbException ex) {\n                            LogUtil.e(ex.getMessage(), ex);\n                        }\n\n                        trimSize();\n                    } else {\n                        throw new IOException(\"rename:\" + cacheFile.getAbsolutePath());\n                    }\n                } else {\n                    throw new FileLockedException(destPath);\n                }\n            } catch (InterruptedException ex) {\n                result = cacheFile;\n                LogUtil.e(ex.getMessage(), ex);\n            } finally {\n                if (result == null) {\n                    result = cacheFile;\n                    IOUtil.closeQuietly(destFile);\n                    IOUtil.closeQuietly(processLock);\n                    IOUtil.deleteFileOrDir(destFile);\n                } else {\n                    IOUtil.closeQuietly(cacheFile);\n                    IOUtil.deleteFileOrDir(cacheFile);\n                }\n            }\n        } else {\n            result = cacheFile;\n        }\n\n        return result;\n    }\n\n    private void trimSize() {\n        trimExecutor.execute(new Runnable() {\n            @Override\n            public void run() {\n                if (available) {\n\n                    long current = System.currentTimeMillis();\n                    if (current - lastTrimTime < TRIM_TIME_SPAN) {\n                        return;\n                    } else {\n                        lastTrimTime = current;\n                    }\n\n                    // trim expires\n                    deleteExpiry();\n\n                    // trim db\n                    try {\n                        int count = (int) cacheDb.selector(DiskCacheEntity.class).count();\n                        if (count > LIMIT_COUNT + 10) {\n                            List<DiskCacheEntity> rmList = cacheDb.selector(DiskCacheEntity.class)\n                                    .orderBy(\"lastAccess\").orderBy(\"hits\")\n                                    .limit(count - LIMIT_COUNT).offset(0).findAll();\n                            if (rmList != null && rmList.size() > 0) {\n                                // delete cache files\n                                for (DiskCacheEntity entity : rmList) {\n                                    String path = entity.getPath();\n                                    if (!TextUtils.isEmpty(path)) {\n                                        if (deleteFileWithLock(path)\n                                                && deleteFileWithLock(path + TEMP_FILE_SUFFIX)) {\n                                            // delete db entity\n                                            cacheDb.delete(entity);\n                                        }\n                                    }\n                                }\n\n                            }\n                        }\n                    } catch (DbException ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n\n                    // trim disk\n                    try {\n                        while (FileUtil.getFileOrDirSize(cacheDir) > diskCacheSize) {\n                            List<DiskCacheEntity> rmList = cacheDb.selector(DiskCacheEntity.class)\n                                    .orderBy(\"lastAccess\").orderBy(\"hits\").limit(10).offset(0).findAll();\n                            if (rmList != null && rmList.size() > 0) {\n                                // delete cache files\n                                for (DiskCacheEntity entity : rmList) {\n                                    String path = entity.getPath();\n                                    if (!TextUtils.isEmpty(path)) {\n                                        if (deleteFileWithLock(path)\n                                                && deleteFileWithLock(path + TEMP_FILE_SUFFIX)) {\n                                            // delete db entity\n                                            cacheDb.delete(entity);\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    } catch (DbException ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }\n        });\n    }\n\n    private void deleteExpiry() {\n        try {\n            WhereBuilder whereBuilder = WhereBuilder.b(\"expires\", \"<\", System.currentTimeMillis());\n            List<DiskCacheEntity> rmList = cacheDb.selector(DiskCacheEntity.class).where(whereBuilder).findAll();\n            // delete db entities\n            cacheDb.delete(DiskCacheEntity.class, whereBuilder);\n            if (rmList != null && rmList.size() > 0) {\n                // delete cache files\n                for (DiskCacheEntity entity : rmList) {\n                    String path = entity.getPath();\n                    if (!TextUtils.isEmpty(path)) {\n                        deleteFileWithLock(path);\n                    }\n                }\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    /**\n     * 清理未被数据库索引的历史缓存文件\n     */\n    private void deleteNoIndexFiles() {\n        trimExecutor.execute(new Runnable() {\n            @Override\n            public void run() {\n                if (available) {\n                    try {\n                        File[] fileList = cacheDir.listFiles();\n                        if (fileList != null) {\n                            for (File file : fileList) {\n                                try {\n                                    long count = cacheDb.selector(DiskCacheEntity.class)\n                                            .where(\"path\", \"=\", file.getAbsolutePath()).count();\n                                    if (count < 1) {\n                                        IOUtil.deleteFileOrDir(file);\n                                    }\n                                } catch (Throwable ex) {\n                                    LogUtil.e(ex.getMessage(), ex);\n                                }\n                            }\n                        }\n                    } catch (Throwable ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }\n        });\n    }\n\n    private boolean deleteFileWithLock(String path) {\n        ProcessLock processLock = null;\n        try {\n            processLock = ProcessLock.tryLock(path, true);\n            if (processLock != null && processLock.isValid()) { // lock\n                File file = new File(path);\n                return IOUtil.deleteFileOrDir(file);\n            }\n        } finally {\n            IOUtil.closeQuietly(processLock);\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/Callback.java",
    "content": "package org.xutils.common;\n\nimport java.lang.reflect.Type;\n\n/**\n * Created by wyouflf on 15/6/5.\n * 通用回调接口\n */\npublic interface Callback {\n\n    public interface CommonCallback<ResultType> extends Callback {\n        void onSuccess(ResultType result);\n\n        void onError(Throwable ex, boolean isOnCallback);\n\n        void onCancelled(CancelledException cex);\n\n        void onFinished();\n    }\n\n    public interface TypedCallback<ResultType> extends CommonCallback<ResultType> {\n        Type getLoadType();\n    }\n\n    public interface CacheCallback<ResultType> extends CommonCallback<ResultType> {\n        boolean onCache(ResultType result);\n    }\n\n    public interface ProxyCacheCallback<ResultType> extends CacheCallback<ResultType> {\n        boolean onlyCache();\n    }\n\n    public interface PrepareCallback<PrepareType, ResultType> extends CommonCallback<ResultType> {\n        ResultType prepare(PrepareType rawData);\n    }\n\n    public interface ProgressCallback<ResultType> extends CommonCallback<ResultType> {\n        void onWaiting();\n\n        void onStarted();\n\n        void onLoading(long total, long current, boolean isDownloading);\n    }\n\n    public interface GroupCallback<ItemType> extends Callback {\n        void onSuccess(ItemType item);\n\n        void onError(ItemType item, Throwable ex, boolean isOnCallback);\n\n        void onCancelled(ItemType item, CancelledException cex);\n\n        void onFinished(ItemType item);\n\n        void onAllFinished();\n    }\n\n    public interface Callable<ResultType> {\n        void call(ResultType result);\n    }\n\n    public interface Cancelable {\n        void cancel();\n\n        boolean isCancelled();\n    }\n\n    public static class CancelledException extends RuntimeException {\n        public CancelledException(String detailMessage) {\n            super(detailMessage);\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/TaskController.java",
    "content": "package org.xutils.common;\n\nimport org.xutils.common.task.AbsTask;\n\n/**\n * Created by wyouflf on 15/6/11.\n * 任务管理接口\n */\npublic interface TaskController {\n\n    /**\n     * 在UI线程执行runnable.\n     * 如果已在UI线程, 则直接执行.\n     *\n     * @param runnable\n     */\n    void autoPost(Runnable runnable);\n\n    /**\n     * 在UI线程执行runnable.\n     * post到msg queue.\n     *\n     * @param runnable\n     */\n    void post(Runnable runnable);\n\n    /**\n     * 在UI线程执行runnable.\n     *\n     * @param runnable\n     * @param delayMillis 延迟时间(单位毫秒)\n     */\n    void postDelayed(Runnable runnable, long delayMillis);\n\n    /**\n     * 在后台线程执行runnable\n     *\n     * @param runnable\n     */\n    void run(Runnable runnable);\n\n    /**\n     * 移除post或postDelayed提交的, 未执行的runnable\n     *\n     * @param runnable\n     */\n    void removeCallbacks(Runnable runnable);\n\n    /**\n     * 开始一个异步任务\n     *\n     * @param task\n     * @param <T>\n     * @return\n     */\n    <T> AbsTask<T> start(AbsTask<T> task);\n\n    /**\n     * 同步执行一个任务\n     *\n     * @param task\n     * @param <T>\n     * @return\n     * @throws Throwable\n     */\n    <T> T startSync(AbsTask<T> task) throws Throwable;\n\n    /**\n     * 批量执行异步任务\n     *\n     * @param groupCallback\n     * @param tasks\n     * @param <T>\n     * @return\n     */\n    <T extends AbsTask<?>> Callback.Cancelable startTasks(Callback.GroupCallback<T> groupCallback, T... tasks);\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/task/AbsTask.java",
    "content": "package org.xutils.common.task;\n\nimport org.xutils.common.Callback;\n\nimport java.util.concurrent.Executor;\n\n\n/**\n * Created by wyouflf on 15/6/5.\n * 异步任务基类\n *\n * @param <ResultType>\n */\npublic abstract class AbsTask<ResultType> implements Callback.Cancelable {\n\n    private TaskProxy taskProxy = null;\n    private final Callback.Cancelable cancelHandler;\n\n    private volatile boolean isCancelled = false;\n    private volatile State state = State.IDLE;\n    private ResultType result;\n\n    public AbsTask() {\n        this(null);\n    }\n\n    public AbsTask(Callback.Cancelable cancelHandler) {\n        this.cancelHandler = cancelHandler;\n    }\n\n    protected abstract ResultType doBackground() throws Throwable;\n\n    protected abstract void onSuccess(ResultType result);\n\n    protected abstract void onError(Throwable ex, boolean isCallbackError);\n\n    protected void onWaiting() {\n    }\n\n    protected void onStarted() {\n    }\n\n    protected void onUpdate(int flag, Object... args) {\n    }\n\n    protected void onCancelled(Callback.CancelledException cex) {\n    }\n\n    protected void onFinished() {\n    }\n\n    public Priority getPriority() {\n        return null;\n    }\n\n    public Executor getExecutor() {\n        return null;\n    }\n\n    protected final void update(int flag, Object... args) {\n        if (taskProxy != null) {\n            taskProxy.onUpdate(flag, args);\n        }\n    }\n\n    /**\n     * invoked via cancel()\n     */\n    protected void cancelWorks() {\n    }\n\n    /**\n     * 取消任务时是否不等待任务彻底结束, 立即收到取消的通知.\n     *\n     * @return\n     */\n    protected boolean isCancelFast() {\n        return false;\n    }\n\n    @Override\n    public final synchronized void cancel() {\n        if (!this.isCancelled) {\n            this.isCancelled = true;\n            cancelWorks();\n            if (cancelHandler != null && !cancelHandler.isCancelled()) {\n                cancelHandler.cancel();\n            }\n            if (this.state == State.WAITING || (this.state == State.STARTED && isCancelFast())) {\n                if (taskProxy != null) {\n                    taskProxy.onCancelled(new Callback.CancelledException(\"cancelled by user\"));\n                    taskProxy.onFinished();\n                } else if (this instanceof TaskProxy) {\n                    this.onCancelled(new Callback.CancelledException(\"cancelled by user\"));\n                    this.onFinished();\n                }\n            }\n        }\n    }\n\n    @Override\n    public final boolean isCancelled() {\n        return isCancelled || state == State.CANCELLED ||\n                (cancelHandler != null && cancelHandler.isCancelled());\n    }\n\n    public final boolean isFinished() {\n        return this.state.value() > State.STARTED.value();\n    }\n\n    public final State getState() {\n        return state;\n    }\n\n    public final ResultType getResult() {\n        return result;\n    }\n\n    /*package*/\n    void setState(State state) {\n        this.state = state;\n    }\n\n    /*package*/\n    final void setTaskProxy(TaskProxy taskProxy) {\n        this.taskProxy = taskProxy;\n    }\n\n    /*package*/\n    final void setResult(ResultType result) {\n        this.result = result;\n    }\n\n    public enum State {\n        IDLE(0), WAITING(1), STARTED(2), SUCCESS(3), CANCELLED(4), ERROR(5);\n        private final int value;\n\n        private State(int value) {\n            this.value = value;\n        }\n\n        public int value() {\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/task/Priority.java",
    "content": "package org.xutils.common.task;\n\n/**\n * Created by wyouflf on 15/6/5.\n * 任务的优先级\n */\npublic enum Priority {\n    UI_TOP, UI_NORMAL, UI_LOW, DEFAULT, BG_TOP, BG_NORMAL, BG_LOW;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/task/PriorityExecutor.java",
    "content": "package org.xutils.common.task;\n\nimport java.util.Comparator;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.PriorityBlockingQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * Created by wyouflf on 15/6/5.\n * 支持优先级的线程池管理类\n */\npublic class PriorityExecutor implements Executor {\n\n    private static final int CORE_POOL_SIZE = 5;\n    private static final int MAXIMUM_POOL_SIZE = 256;\n    private static final int KEEP_ALIVE = 1;\n    private static final AtomicLong SEQ_SEED = new AtomicLong(0);\n\n    private static final ThreadFactory sThreadFactory = new ThreadFactory() {\n        private final AtomicInteger mCount = new AtomicInteger(1);\n\n        @Override\n        public Thread newThread(Runnable runnable) {\n            return new Thread(runnable, \"xTID#\" + mCount.getAndIncrement());\n        }\n    };\n\n    private static final Comparator<Runnable> FIFO_CMP = new Comparator<Runnable>() {\n        @Override\n        public int compare(Runnable lhs, Runnable rhs) {\n            if (lhs instanceof PriorityRunnable && rhs instanceof PriorityRunnable) {\n                PriorityRunnable lpr = ((PriorityRunnable) lhs);\n                PriorityRunnable rpr = ((PriorityRunnable) rhs);\n                int result = lpr.priority.ordinal() - rpr.priority.ordinal();\n                return result == 0 ? (int) (lpr.SEQ - rpr.SEQ) : result;\n            } else {\n                return 0;\n            }\n        }\n    };\n\n    private static final Comparator<Runnable> FILO_CMP = new Comparator<Runnable>() {\n        @Override\n        public int compare(Runnable lhs, Runnable rhs) {\n            if (lhs instanceof PriorityRunnable && rhs instanceof PriorityRunnable) {\n                PriorityRunnable lpr = ((PriorityRunnable) lhs);\n                PriorityRunnable rpr = ((PriorityRunnable) rhs);\n                int result = lpr.priority.ordinal() - rpr.priority.ordinal();\n                return result == 0 ? (int) (rpr.SEQ - lpr.SEQ) : result;\n            } else {\n                return 0;\n            }\n        }\n    };\n\n    private final ThreadPoolExecutor mThreadPoolExecutor;\n\n    /**\n     * 默认工作线程数5\n     *\n     * @param fifo 优先级相同时, 等待队列的是否优先执行先加入的任务.\n     */\n    public PriorityExecutor(boolean fifo) {\n        this(CORE_POOL_SIZE, fifo);\n    }\n\n    /**\n     * @param poolSize 工作线程数\n     * @param fifo     优先级相同时, 等待队列的是否优先执行先加入的任务.\n     */\n    public PriorityExecutor(int poolSize, boolean fifo) {\n        BlockingQueue<Runnable> mPoolWorkQueue =\n                new PriorityBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE, fifo ? FIFO_CMP : FILO_CMP);\n        mThreadPoolExecutor = new ThreadPoolExecutor(\n                poolSize,\n                MAXIMUM_POOL_SIZE,\n                KEEP_ALIVE,\n                TimeUnit.SECONDS,\n                mPoolWorkQueue,\n                sThreadFactory);\n    }\n\n    public int getPoolSize() {\n        return mThreadPoolExecutor.getCorePoolSize();\n    }\n\n    public void setPoolSize(int poolSize) {\n        if (poolSize > 0) {\n            mThreadPoolExecutor.setCorePoolSize(poolSize);\n        }\n    }\n\n    public ThreadPoolExecutor getThreadPoolExecutor() {\n        return mThreadPoolExecutor;\n    }\n\n    public boolean isBusy() {\n        return mThreadPoolExecutor.getActiveCount() >= mThreadPoolExecutor.getCorePoolSize();\n    }\n\n    @Override\n    public void execute(Runnable runnable) {\n        if (runnable instanceof PriorityRunnable) {\n            ((PriorityRunnable) runnable).SEQ = SEQ_SEED.getAndIncrement();\n        }\n        mThreadPoolExecutor.execute(runnable);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/task/PriorityRunnable.java",
    "content": "package org.xutils.common.task;\n\n/**\n * Created by wyouflf on 15/6/5.\n * 带有优先级的Runnable类型(仅在task包内可用)\n */\n/*package*/ class PriorityRunnable implements Runnable {\n\n    /*package*/ long SEQ;\n\n    public final Priority priority;\n    private final Runnable runnable;\n\n    public PriorityRunnable(Priority priority, Runnable runnable) {\n        this.priority = priority == null ? Priority.DEFAULT : priority;\n        this.runnable = runnable;\n    }\n\n    @Override\n    public final void run() {\n        this.runnable.run();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/task/TaskControllerImpl.java",
    "content": "package org.xutils.common.task;\n\nimport android.os.Looper;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.TaskController;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.x;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Created by wyouflf on 15/6/5.\n * 异步任务的管理类\n */\npublic final class TaskControllerImpl implements TaskController {\n\n    private TaskControllerImpl() {\n    }\n\n    private static volatile TaskController instance;\n\n    public static void registerInstance() {\n        if (instance == null) {\n            synchronized (TaskController.class) {\n                if (instance == null) {\n                    instance = new TaskControllerImpl();\n                }\n            }\n        }\n        x.Ext.setTaskController(instance);\n    }\n\n    /**\n     * run task\n     *\n     * @param task\n     * @param <T>\n     * @return\n     */\n    @Override\n    public <T> AbsTask<T> start(AbsTask<T> task) {\n        TaskProxy<T> proxy = null;\n        if (task instanceof TaskProxy) {\n            proxy = (TaskProxy<T>) task;\n        } else {\n            proxy = new TaskProxy<T>(task);\n        }\n        try {\n            proxy.doBackground();\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        return proxy;\n    }\n\n    @Override\n    public <T> T startSync(AbsTask<T> task) throws Throwable {\n        T result = null;\n        try {\n            task.onWaiting();\n            task.onStarted();\n            result = task.doBackground();\n            task.onSuccess(result);\n        } catch (Callback.CancelledException cex) {\n            task.onCancelled(cex);\n        } catch (Throwable ex) {\n            task.onError(ex, false);\n            throw ex;\n        } finally {\n            task.onFinished();\n        }\n        return result;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <T extends AbsTask<?>> Callback.Cancelable startTasks(\n            final Callback.GroupCallback<T> groupCallback, final T... tasks) {\n\n        if (tasks == null) {\n            throw new IllegalArgumentException(\"task must not be null\");\n        }\n\n        final Runnable callIfOnAllFinished = new Runnable() {\n            private final int total = tasks.length;\n            private final AtomicInteger count = new AtomicInteger(0);\n\n            @Override\n            public void run() {\n                if (count.incrementAndGet() == total) {\n                    if (groupCallback != null) {\n                        groupCallback.onAllFinished();\n                    }\n                }\n            }\n        };\n\n        for (final T task : tasks) {\n            start(new TaskProxy(task) {\n                @Override\n                protected void onSuccess(Object result) {\n                    super.onSuccess(result);\n                    post(new Runnable() {\n                        @Override\n                        public void run() {\n                            if (groupCallback != null) {\n                                groupCallback.onSuccess(task);\n                            }\n                        }\n                    });\n                }\n\n                @Override\n                protected void onCancelled(final Callback.CancelledException cex) {\n                    super.onCancelled(cex);\n                    post(new Runnable() {\n                        @Override\n                        public void run() {\n                            if (groupCallback != null) {\n                                groupCallback.onCancelled(task, cex);\n                            }\n                        }\n                    });\n                }\n\n                @Override\n                protected void onError(final Throwable ex, final boolean isCallbackError) {\n                    super.onError(ex, isCallbackError);\n                    post(new Runnable() {\n                        @Override\n                        public void run() {\n                            if (groupCallback != null) {\n                                groupCallback.onError(task, ex, isCallbackError);\n                            }\n                        }\n                    });\n                }\n\n                @Override\n                protected void onFinished() {\n                    super.onFinished();\n                    post(new Runnable() {\n                        @Override\n                        public void run() {\n                            if (groupCallback != null) {\n                                groupCallback.onFinished(task);\n                            }\n                            callIfOnAllFinished.run();\n                        }\n                    });\n                }\n            });\n        }\n\n        return new Callback.Cancelable() {\n\n            @Override\n            public void cancel() {\n                for (T task : tasks) {\n                    task.cancel();\n                }\n            }\n\n            @Override\n            public boolean isCancelled() {\n                boolean isCancelled = true;\n                for (T task : tasks) {\n                    if (!task.isCancelled()) {\n                        isCancelled = false;\n                    }\n                }\n                return isCancelled;\n            }\n        };\n    }\n\n    @Override\n    public void autoPost(Runnable runnable) {\n        if (runnable == null) return;\n        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {\n            runnable.run();\n        } else {\n            TaskProxy.sHandler.post(runnable);\n        }\n    }\n\n    /**\n     * run in UI thread\n     *\n     * @param runnable\n     */\n    @Override\n    public void post(Runnable runnable) {\n        if (runnable == null) return;\n        TaskProxy.sHandler.post(runnable);\n    }\n\n    /**\n     * run in UI thread\n     *\n     * @param runnable\n     * @param delayMillis\n     */\n    @Override\n    public void postDelayed(Runnable runnable, long delayMillis) {\n        if (runnable == null) return;\n        TaskProxy.sHandler.postDelayed(runnable, delayMillis);\n    }\n\n    /**\n     * run in background thread\n     *\n     * @param runnable\n     */\n    @Override\n    public void run(Runnable runnable) {\n        if (!TaskProxy.sDefaultExecutor.isBusy()) {\n            TaskProxy.sDefaultExecutor.execute(runnable);\n        } else {\n            new Thread(runnable).start();\n        }\n    }\n\n    /**\n     * 移除post或postDelayed提交的, 未执行的runnable\n     *\n     * @param runnable\n     */\n    @Override\n    public void removeCallbacks(Runnable runnable) {\n        TaskProxy.sHandler.removeCallbacks(runnable);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/task/TaskProxy.java",
    "content": "package org.xutils.common.task;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.x;\n\nimport java.util.concurrent.Executor;\n\n/**\n * 异步任务的代理类(仅在task包内可用)\n *\n * @param <ResultType>\n */\n/*package*/ class TaskProxy<ResultType> extends AbsTask<ResultType> {\n\n    /*package*/ static final InternalHandler sHandler = new InternalHandler();\n    /*package*/ static final PriorityExecutor sDefaultExecutor = new PriorityExecutor(true);\n\n    private final AbsTask<ResultType> task;\n    private final Executor executor;\n    private volatile boolean callOnCanceled = false;\n    private volatile boolean callOnFinished = false;\n\n    /*package*/ TaskProxy(AbsTask<ResultType> task) {\n        super(task);\n        this.task = task;\n        this.task.setTaskProxy(this);\n        this.setTaskProxy(null);\n        Executor taskExecutor = task.getExecutor();\n        if (taskExecutor == null) {\n            taskExecutor = sDefaultExecutor;\n        }\n        this.executor = taskExecutor;\n    }\n\n    @Override\n    protected final ResultType doBackground() throws Throwable {\n        this.onWaiting();\n        PriorityRunnable runnable = new PriorityRunnable(\n                task.getPriority(),\n                new Runnable() {\n                    @Override\n                    public void run() {\n                        try {\n                            // 等待过程中取消\n                            if (callOnCanceled || TaskProxy.this.isCancelled()) {\n                                throw new Callback.CancelledException(\"\");\n                            }\n\n                            // start running\n                            TaskProxy.this.onStarted();\n\n                            if (TaskProxy.this.isCancelled()) { // 开始时取消\n                                throw new Callback.CancelledException(\"\");\n                            }\n\n                            // 执行task, 得到结果.\n                            task.setResult(task.doBackground());\n                            TaskProxy.this.setResult(task.getResult());\n\n                            // 未在doBackground过程中取消成功\n                            if (TaskProxy.this.isCancelled()) {\n                                throw new Callback.CancelledException(\"\");\n                            }\n\n                            // 执行成功\n                            TaskProxy.this.onSuccess(task.getResult());\n                        } catch (Callback.CancelledException cex) {\n                            TaskProxy.this.onCancelled(cex);\n                        } catch (Throwable ex) {\n                            TaskProxy.this.onError(ex, false);\n                        } finally {\n                            TaskProxy.this.onFinished();\n                        }\n                    }\n                });\n        this.executor.execute(runnable);\n        return null;\n    }\n\n    @Override\n    protected void onWaiting() {\n        this.setState(State.WAITING);\n        sHandler.obtainMessage(MSG_WHAT_ON_WAITING, this).sendToTarget();\n    }\n\n    @Override\n    protected void onStarted() {\n        this.setState(State.STARTED);\n        sHandler.obtainMessage(MSG_WHAT_ON_START, this).sendToTarget();\n    }\n\n    @Override\n    protected void onSuccess(ResultType result) {\n        this.setState(State.SUCCESS);\n        sHandler.obtainMessage(MSG_WHAT_ON_SUCCESS, this).sendToTarget();\n    }\n\n    @Override\n    protected void onError(Throwable ex, boolean isCallbackError) {\n        this.setState(State.ERROR);\n        sHandler.obtainMessage(MSG_WHAT_ON_ERROR, new ArgsObj(this, ex)).sendToTarget();\n    }\n\n    @Override\n    protected void onUpdate(int flag, Object... args) {\n        // obtainMessage(int what, int arg1, int arg2, Object obj), arg2 not be used.\n        sHandler.obtainMessage(MSG_WHAT_ON_UPDATE, flag, flag, new ArgsObj(this, args)).sendToTarget();\n    }\n\n    @Override\n    protected void onCancelled(Callback.CancelledException cex) {\n        this.setState(State.CANCELLED);\n        sHandler.obtainMessage(MSG_WHAT_ON_CANCEL, new ArgsObj(this, cex)).sendToTarget();\n    }\n\n    @Override\n    protected void onFinished() {\n        sHandler.obtainMessage(MSG_WHAT_ON_FINISHED, this).sendToTarget();\n    }\n\n    @Override\n    /*package*/ final void setState(State state) {\n        super.setState(state);\n        this.task.setState(state);\n    }\n\n    @Override\n    public final Priority getPriority() {\n        return task.getPriority();\n    }\n\n    @Override\n    public final Executor getExecutor() {\n        return this.executor;\n    }\n\n    // ########################### inner type #############################\n    private static class ArgsObj {\n        final TaskProxy taskProxy;\n        final Object[] args;\n\n        public ArgsObj(TaskProxy taskProxy, Object... args) {\n            this.taskProxy = taskProxy;\n            this.args = args;\n        }\n    }\n\n    private final static int MSG_WHAT_BASE = 1000000000;\n    private final static int MSG_WHAT_ON_WAITING = MSG_WHAT_BASE + 1;\n    private final static int MSG_WHAT_ON_START = MSG_WHAT_BASE + 2;\n    private final static int MSG_WHAT_ON_SUCCESS = MSG_WHAT_BASE + 3;\n    private final static int MSG_WHAT_ON_ERROR = MSG_WHAT_BASE + 4;\n    private final static int MSG_WHAT_ON_UPDATE = MSG_WHAT_BASE + 5;\n    private final static int MSG_WHAT_ON_CANCEL = MSG_WHAT_BASE + 6;\n    private final static int MSG_WHAT_ON_FINISHED = MSG_WHAT_BASE + 7;\n\n    /*package*/ final static class InternalHandler extends Handler {\n\n        private InternalHandler() {\n            super(Looper.getMainLooper());\n        }\n\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public void handleMessage(Message msg) {\n            if (msg.obj == null) {\n                throw new IllegalArgumentException(\"msg must not be null\");\n            }\n            TaskProxy taskProxy = null;\n            Object[] args = null;\n            if (msg.obj instanceof TaskProxy) {\n                taskProxy = (TaskProxy) msg.obj;\n            } else if (msg.obj instanceof ArgsObj) {\n                ArgsObj argsObj = (ArgsObj) msg.obj;\n                taskProxy = argsObj.taskProxy;\n                args = argsObj.args;\n            }\n            if (taskProxy == null) {\n                throw new RuntimeException(\"msg.obj not instanceof TaskProxy\");\n            }\n\n            try {\n                switch (msg.what) {\n                    case MSG_WHAT_ON_WAITING: {\n                        taskProxy.task.onWaiting();\n                        break;\n                    }\n                    case MSG_WHAT_ON_START: {\n                        taskProxy.task.onStarted();\n                        break;\n                    }\n                    case MSG_WHAT_ON_SUCCESS: {\n                        taskProxy.task.onSuccess(taskProxy.getResult());\n                        break;\n                    }\n                    case MSG_WHAT_ON_ERROR: {\n                        assert args != null;\n                        Throwable throwable = (Throwable) args[0];\n                        LogUtil.d(throwable.getMessage(), throwable);\n                        taskProxy.task.onError(throwable, false);\n                        break;\n                    }\n                    case MSG_WHAT_ON_UPDATE: {\n                        taskProxy.task.onUpdate(msg.arg1, args);\n                        break;\n                    }\n                    case MSG_WHAT_ON_CANCEL: {\n                        if (taskProxy.callOnCanceled) return;\n                        taskProxy.callOnCanceled = true;\n                        assert args != null;\n                        taskProxy.task.onCancelled((org.xutils.common.Callback.CancelledException) args[0]);\n                        break;\n                    }\n                    case MSG_WHAT_ON_FINISHED: {\n                        if (taskProxy.callOnFinished) return;\n                        taskProxy.callOnFinished = true;\n                        taskProxy.task.onFinished();\n                        break;\n                    }\n                    default: {\n                        break;\n                    }\n                }\n            } catch (Throwable ex) {\n                taskProxy.setState(State.ERROR);\n                if (msg.what != MSG_WHAT_ON_ERROR) {\n                    taskProxy.task.onError(ex, true);\n                } else if (x.isDebug()) {\n                    throw new RuntimeException(ex);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/DensityUtil.java",
    "content": "package org.xutils.common.util;\n\nimport org.xutils.x;\n\n\npublic final class DensityUtil {\n\n    private static float density = -1F;\n    private static int widthPixels = -1;\n    private static int heightPixels = -1;\n\n    private DensityUtil() {\n    }\n\n    public static float getDensity() {\n        if (density <= 0F) {\n            density = x.app().getResources().getDisplayMetrics().density;\n        }\n        return density;\n    }\n\n    public static int dip2px(float dpValue) {\n        return (int) (dpValue * getDensity() + 0.5F);\n    }\n\n    public static int px2dip(float pxValue) {\n        return (int) (pxValue / getDensity() + 0.5F);\n    }\n\n    public static int getScreenWidth() {\n        if (widthPixels <= 0) {\n            widthPixels = x.app().getResources().getDisplayMetrics().widthPixels;\n        }\n        return widthPixels;\n    }\n\n\n    public static int getScreenHeight() {\n        if (heightPixels <= 0) {\n            heightPixels = x.app().getResources().getDisplayMetrics().heightPixels;\n        }\n        return heightPixels;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/DoubleKeyValueMap.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.common.util;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Created with IntelliJ IDEA.\n * User: wyouflf\n * Date: 13-6-19\n * Time: PM 1:18\n */\npublic class DoubleKeyValueMap<K1, K2, V> {\n\n    private final ConcurrentHashMap<K1, ConcurrentHashMap<K2, V>> k1_k2V_map;\n\n    public DoubleKeyValueMap() {\n        this.k1_k2V_map = new ConcurrentHashMap<K1, ConcurrentHashMap<K2, V>>();\n    }\n\n    public void put(K1 key1, K2 key2, V value) {\n        if (key1 == null || key2 == null || value == null) return;\n        if (k1_k2V_map.containsKey(key1)) {\n            ConcurrentHashMap<K2, V> k2V_map = k1_k2V_map.get(key1);\n            if (k2V_map != null) {\n                k2V_map.put(key2, value);\n            } else {\n                k2V_map = new ConcurrentHashMap<K2, V>();\n                k2V_map.put(key2, value);\n                k1_k2V_map.put(key1, k2V_map);\n            }\n        } else {\n            ConcurrentHashMap<K2, V> k2V_map = new ConcurrentHashMap<K2, V>();\n            k2V_map.put(key2, value);\n            k1_k2V_map.put(key1, k2V_map);\n        }\n    }\n\n    public Set<K1> getFirstKeys() {\n        return k1_k2V_map.keySet();\n    }\n\n    public ConcurrentHashMap<K2, V> get(K1 key1) {\n        return k1_k2V_map.get(key1);\n    }\n\n    public V get(K1 key1, K2 key2) {\n        ConcurrentHashMap<K2, V> k2_v = k1_k2V_map.get(key1);\n        return k2_v == null ? null : k2_v.get(key2);\n    }\n\n    public Collection<V> getAllValues(K1 key1) {\n        ConcurrentHashMap<K2, V> k2_v = k1_k2V_map.get(key1);\n        return k2_v == null ? null : k2_v.values();\n    }\n\n    public Collection<V> getAllValues() {\n        Collection<V> result = null;\n        Set<K1> k1Set = k1_k2V_map.keySet();\n        if (k1Set != null) {\n            result = new ArrayList<V>();\n            for (K1 k1 : k1Set) {\n                Collection<V> values = k1_k2V_map.get(k1).values();\n                if (values != null) {\n                    result.addAll(values);\n                }\n            }\n        }\n        return result;\n    }\n\n    public boolean containsKey(K1 key1, K2 key2) {\n        if (k1_k2V_map.containsKey(key1)) {\n            return k1_k2V_map.get(key1).containsKey(key2);\n        }\n        return false;\n    }\n\n    public boolean containsKey(K1 key1) {\n        return k1_k2V_map.containsKey(key1);\n    }\n\n    public int size() {\n        if (k1_k2V_map.size() == 0) return 0;\n\n        int result = 0;\n        for (ConcurrentHashMap<K2, V> k2V_map : k1_k2V_map.values()) {\n            result += k2V_map.size();\n        }\n        return result;\n    }\n\n    public void remove(K1 key1) {\n        k1_k2V_map.remove(key1);\n    }\n\n    public void remove(K1 key1, K2 key2) {\n        ConcurrentHashMap<K2, V> k2_v = k1_k2V_map.get(key1);\n        if (k2_v != null) {\n            k2_v.remove(key2);\n        }\n        if (k2_v == null || k2_v.isEmpty()) {\n            k1_k2V_map.remove(key1);\n        }\n    }\n\n    public void clear() {\n        if (k1_k2V_map.size() > 0) {\n            for (ConcurrentHashMap<K2, V> k2V_map : k1_k2V_map.values()) {\n                k2V_map.clear();\n            }\n            k1_k2V_map.clear();\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/FileUtil.java",
    "content": "package org.xutils.common.util;\n\nimport android.os.Environment;\nimport android.os.StatFs;\n\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\n\npublic class FileUtil {\n\n    private FileUtil() {\n    }\n\n    public static File getCacheDir(String dirName) {\n        File result;\n        if (existsSdcard()) {\n            File cacheDir = x.app().getExternalCacheDir();\n            if (cacheDir == null) {\n                result = new File(Environment.getExternalStorageDirectory(),\n                        \"Android/data/\" + x.app().getPackageName() + \"/cache/\" + dirName);\n            } else {\n                result = new File(cacheDir, dirName);\n            }\n        } else {\n            result = new File(x.app().getCacheDir(), dirName);\n        }\n        if (result.exists() || result.mkdirs()) {\n            return result;\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * 检查磁盘空间是否大于10mb\n     *\n     * @return true 大于\n     */\n    public static boolean isDiskAvailable() {\n        long size = getDiskAvailableSize();\n        return size > 10 * 1024 * 1024; // > 10bm\n    }\n\n    /**\n     * 获取磁盘可用空间\n     *\n     * @return byte 单位 kb\n     */\n    public static long getDiskAvailableSize() {\n        if (!existsSdcard()) return 0;\n        File path = Environment.getExternalStorageDirectory(); // 取得sdcard文件路径\n        StatFs stat = new StatFs(path.getAbsolutePath());\n        long blockSize = stat.getBlockSize();\n        long availableBlocks = stat.getAvailableBlocks();\n        return availableBlocks * blockSize;\n        // (availableBlocks * blockSize)/1024 KIB 单位\n        // (availableBlocks * blockSize)/1024 /1024 MIB单位\n    }\n\n    public static Boolean existsSdcard() {\n        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);\n    }\n\n    public static long getFileOrDirSize(File file) {\n        if (!file.exists()) return 0;\n        if (!file.isDirectory()) return file.length();\n\n        long length = 0;\n        File[] list = file.listFiles();\n        if (list != null) { // 文件夹被删除时, 子文件正在被写入, 文件属性异常返回null.\n            for (File item : list) {\n                length += getFileOrDirSize(item);\n            }\n        }\n\n        return length;\n    }\n\n    /**\n     * 复制文件到指定文件\n     *\n     * @param fromPath 源文件\n     * @param toPath   复制到的文件\n     * @return true 成功，false 失败\n     */\n    public static boolean copy(String fromPath, String toPath) {\n        boolean result = false;\n        File from = new File(fromPath);\n        if (!from.exists()) {\n            return result;\n        }\n\n        File toFile = new File(toPath);\n        IOUtil.deleteFileOrDir(toFile);\n        File toDir = toFile.getParentFile();\n        if (toDir.exists() || toDir.mkdirs()) {\n            FileInputStream in = null;\n            FileOutputStream out = null;\n            try {\n                in = new FileInputStream(from);\n                out = new FileOutputStream(toFile);\n                IOUtil.copy(in, out);\n                result = true;\n            } catch (Throwable ex) {\n                LogUtil.d(ex.getMessage(), ex);\n                result = false;\n            } finally {\n                IOUtil.closeQuietly(in);\n                IOUtil.closeQuietly(out);\n            }\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/IOUtil.java",
    "content": "package org.xutils.common.util;\n\nimport android.database.Cursor;\nimport android.text.TextUtils;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.Reader;\nimport java.io.Writer;\n\npublic class IOUtil {\n\n    private IOUtil() {\n    }\n\n    public static void closeQuietly(Closeable closeable) {\n        if (closeable != null) {\n            try {\n                closeable.close();\n            } catch (Throwable ignored) {\n                LogUtil.d(ignored.getMessage(), ignored);\n            }\n        }\n    }\n\n    public static void closeQuietly(Cursor cursor) {\n        if (cursor != null) {\n            try {\n                cursor.close();\n            } catch (Throwable ignored) {\n                LogUtil.d(ignored.getMessage(), ignored);\n            }\n        }\n    }\n\n    public static byte[] readBytes(InputStream in) throws IOException {\n        if (!(in instanceof BufferedInputStream)) {\n            in = new BufferedInputStream(in);\n        }\n        ByteArrayOutputStream out = null;\n        try {\n            out = new ByteArrayOutputStream();\n            byte[] buf = new byte[1024];\n            int len;\n            while ((len = in.read(buf)) != -1) {\n                out.write(buf, 0, len);\n            }\n            return out.toByteArray();\n        } finally {\n            closeQuietly(out);\n        }\n    }\n\n    public static byte[] readBytes(InputStream in, long skip, int size) throws IOException {\n        byte[] result = null;\n        if (skip > 0) {\n            long skipped = 0;\n            while (skip > 0 && (skipped = in.skip(skip)) > 0) {\n                skip -= skipped;\n            }\n        }\n        result = new byte[size];\n        for (int i = 0; i < size; i++) {\n            result[i] = (byte) in.read();\n        }\n        return result;\n    }\n\n    public static String readStr(InputStream in) throws IOException {\n        return readStr(in, \"UTF-8\");\n    }\n\n    public static String readStr(InputStream in, String charset) throws IOException {\n        if (TextUtils.isEmpty(charset)) charset = \"UTF-8\";\n\n        if (!(in instanceof BufferedInputStream)) {\n            in = new BufferedInputStream(in);\n        }\n        Reader reader = new InputStreamReader(in, charset);\n        StringBuilder sb = new StringBuilder();\n        char[] buf = new char[1024];\n        int len;\n        while ((len = reader.read(buf)) >= 0) {\n            sb.append(buf, 0, len);\n        }\n        return sb.toString();\n    }\n\n    public static void writeStr(OutputStream out, String str) throws IOException {\n        writeStr(out, str, \"UTF-8\");\n    }\n\n    public static void writeStr(OutputStream out, String str, String charset) throws IOException {\n        if (TextUtils.isEmpty(charset)) charset = \"UTF-8\";\n\n        Writer writer = new OutputStreamWriter(out, charset);\n        writer.write(str);\n        writer.flush();\n    }\n\n    public static void copy(InputStream in, OutputStream out) throws IOException {\n        if (!(in instanceof BufferedInputStream)) {\n            in = new BufferedInputStream(in);\n        }\n        if (!(out instanceof BufferedOutputStream)) {\n            out = new BufferedOutputStream(out);\n        }\n        int len = 0;\n        byte[] buffer = new byte[1024];\n        while ((len = in.read(buffer)) != -1) {\n            out.write(buffer, 0, len);\n        }\n        out.flush();\n    }\n\n    public static boolean deleteFileOrDir(File path) {\n        if (path == null || !path.exists()) {\n            return true;\n        }\n        if (path.isFile()) {\n            return path.delete();\n        }\n        File[] files = path.listFiles();\n        if (files != null) {\n            for (File file : files) {\n                deleteFileOrDir(file);\n            }\n        }\n        return path.delete();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/KeyValue.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.common.util;\n\npublic class KeyValue {\n    public final String key;\n    public final Object value;\n\n    public KeyValue(String key, Object value) {\n        this.key = key;\n        this.value = value;\n    }\n\n    public String getValueStr() {\n        return value == null ? null : value.toString();\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        KeyValue keyValue = (KeyValue) o;\n\n        return key == null ? keyValue.key == null : key.equals(keyValue.key);\n\n    }\n\n    @Override\n    public int hashCode() {\n        return key != null ? key.hashCode() : 0;\n    }\n\n    @Override\n    public String toString() {\n        return \"KeyValue{\" + \"key='\" + key + '\\'' + \", value=\" + value + '}';\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/LogUtil.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.common.util;\n\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport org.xutils.x;\n\n/**\n * Log工具，类似android.util.Log。\n * tag自动产生，格式: customTagPrefix:className.methodName(L:lineNumber),\n * customTagPrefix为空时只输出：className.methodName(L:lineNumber)。\n * Author: wyouflf\n * Date: 13-7-24\n * Time: 下午12:23\n */\npublic class LogUtil {\n\n    public static String customTagPrefix = \"x_log\";\n\n    private LogUtil() {\n    }\n\n    private static String generateTag() {\n        StackTraceElement caller = new Throwable().getStackTrace()[2];\n        String tag = \"%s.%s(L:%d)\";\n        String callerClazzName = caller.getClassName();\n        callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(\".\") + 1);\n        tag = String.format(tag, callerClazzName, caller.getMethodName(), caller.getLineNumber());\n        tag = TextUtils.isEmpty(customTagPrefix) ? tag : customTagPrefix + \":\" + tag;\n        return tag;\n    }\n\n    public static void d(String content) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.d(tag, content);\n    }\n\n    public static void d(String content, Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.d(tag, content, tr);\n    }\n\n    public static void e(String content) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.e(tag, content);\n    }\n\n    public static void e(String content, Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.e(tag, content, tr);\n    }\n\n    public static void i(String content) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.i(tag, content);\n    }\n\n    public static void i(String content, Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.i(tag, content, tr);\n    }\n\n    public static void v(String content) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.v(tag, content);\n    }\n\n    public static void v(String content, Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.v(tag, content, tr);\n    }\n\n    public static void w(String content) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.w(tag, content);\n    }\n\n    public static void w(String content, Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.w(tag, content, tr);\n    }\n\n    public static void w(Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.w(tag, tr);\n    }\n\n\n    public static void wtf(String content) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.wtf(tag, content);\n    }\n\n    public static void wtf(String content, Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.wtf(tag, content, tr);\n    }\n\n    public static void wtf(Throwable tr) {\n        if (!x.isDebug()) return;\n        String tag = generateTag();\n\n        Log.wtf(tag, tr);\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/MD5.java",
    "content": "package org.xutils.common.util;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\npublic final class MD5 {\n\n    private MD5() {\n    }\n\n    private static final char hexDigits[] =\n            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\n\n    public static String toHexString(byte[] bytes) {\n        if (bytes == null) return \"\";\n        StringBuilder hex = new StringBuilder(bytes.length * 2);\n        for (byte b : bytes) {\n            hex.append(hexDigits[(b >> 4) & 0x0F]);\n            hex.append(hexDigits[b & 0x0F]);\n        }\n        return hex.toString();\n    }\n\n    public static String md5(File file) throws IOException {\n        MessageDigest messagedigest = null;\n        FileInputStream in = null;\n        FileChannel ch = null;\n        byte[] encodeBytes = null;\n        try {\n            messagedigest = MessageDigest.getInstance(\"MD5\");\n            in = new FileInputStream(file);\n            ch = in.getChannel();\n            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());\n            messagedigest.update(byteBuffer);\n            encodeBytes = messagedigest.digest();\n        } catch (NoSuchAlgorithmException neverHappened) {\n            throw new RuntimeException(neverHappened);\n        } finally {\n            IOUtil.closeQuietly(in);\n            IOUtil.closeQuietly(ch);\n        }\n\n        return toHexString(encodeBytes);\n    }\n\n    public static String md5(String string) {\n        byte[] encodeBytes = null;\n        try {\n            encodeBytes = MessageDigest.getInstance(\"MD5\").digest(string.getBytes(\"UTF-8\"));\n        } catch (NoSuchAlgorithmException neverHappened) {\n            throw new RuntimeException(neverHappened);\n        } catch (UnsupportedEncodingException neverHappened) {\n            throw new RuntimeException(neverHappened);\n        }\n\n        return toHexString(encodeBytes);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/ParameterizedTypeUtil.java",
    "content": "package org.xutils.common.util;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.GenericArrayType;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.lang.reflect.TypeVariable;\n\npublic class ParameterizedTypeUtil {\n\n    private ParameterizedTypeUtil() {\n    }\n\n    public static Type getParameterizedType(\n\n            final Type ownerType,\n            final Class<?> declaredClass,\n            int paramIndex) {\n\n        Class<?> clazz = null;\n        ParameterizedType pt = null;\n        Type[] ats = null;\n        TypeVariable<?>[] tps = null;\n        if (ownerType instanceof ParameterizedType) {\n            pt = (ParameterizedType) ownerType;\n            clazz = (Class<?>) pt.getRawType();\n            ats = pt.getActualTypeArguments();\n            tps = clazz.getTypeParameters();\n        } else {\n            clazz = (Class<?>) ownerType;\n        }\n        if (declaredClass == clazz) {\n            if (ats != null) {\n                return ats[paramIndex];\n            }\n            return Object.class;\n        }\n\n        Type[] types = clazz.getGenericInterfaces();\n        if (types != null) {\n            for (int i = 0; i < types.length; i++) {\n                Type t = types[i];\n                if (t instanceof ParameterizedType) {\n                    Class<?> cls = (Class<?>) ((ParameterizedType) t).getRawType();\n                    if (declaredClass.isAssignableFrom(cls)) {\n                        try {\n                            return getTrueType(getParameterizedType(t, declaredClass, paramIndex), tps, ats);\n                        } catch (Throwable ignored) {\n                        }\n                    }\n                }\n            }\n        }\n\n        Class<?> superClass = clazz.getSuperclass();\n        if (superClass != null) {\n            if (declaredClass.isAssignableFrom(superClass)) {\n                return getTrueType(\n                        getParameterizedType(clazz.getGenericSuperclass(),\n                                declaredClass, paramIndex), tps, ats);\n            }\n        }\n\n        throw new IllegalArgumentException(\"FindGenericType:\" + ownerType +\n                \", declaredClass: \" + declaredClass + \", index: \" + paramIndex);\n\n    }\n\n\n    private static Type getTrueType(\n\n            Type type,\n            TypeVariable<?>[] typeVariables,\n            Type[] actualTypes) {\n\n        if (type instanceof TypeVariable<?>) {\n            TypeVariable<?> tv = (TypeVariable<?>) type;\n            String name = tv.getName();\n            if (actualTypes != null) {\n                for (int i = 0; i < typeVariables.length; i++) {\n                    if (name.equals(typeVariables[i].getName())) {\n                        return actualTypes[i];\n                    }\n                }\n            }\n            return tv;\n            // }else if (type instanceof Class<?>) {\n            // return type;\n        } else if (type instanceof GenericArrayType) {\n            Type ct = ((GenericArrayType) type).getGenericComponentType();\n            if (ct instanceof Class<?>) {\n                return Array.newInstance((Class<?>) ct, 0).getClass();\n            }\n        }\n        return type;\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/common/util/ProcessLock.java",
    "content": "package org.xutils.common.util;\n\n\nimport android.content.Context;\nimport android.text.TextUtils;\n\nimport org.xutils.x;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.FileLock;\nimport java.text.DecimalFormat;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 进程间锁, 仅在同一个应用中有效.\n */\npublic final class ProcessLock implements Closeable {\n\n    private final String mLockName;\n    private final FileLock mFileLock;\n    private final File mFile;\n    private final Closeable mStream;\n    private final boolean mWriteMode;\n\n    private final static String LOCK_FILE_DIR = \"process_lock\";\n    /**\n     * key1: lockName\n     * key2: fileLock.hashCode()\n     */\n    private final static DoubleKeyValueMap<String, Integer, ProcessLock> LOCK_MAP = new DoubleKeyValueMap<String, Integer, ProcessLock>();\n\n    static {\n        File dir = x.app().getDir(LOCK_FILE_DIR, Context.MODE_PRIVATE);\n        IOUtil.deleteFileOrDir(dir);\n    }\n\n    private ProcessLock(String lockName, File file, FileLock fileLock, Closeable stream, boolean writeMode) {\n        mLockName = lockName;\n        mFileLock = fileLock;\n        mFile = file;\n        mStream = stream;\n        mWriteMode = writeMode;\n    }\n\n    /**\n     * 获取进程锁\n     *\n     * @param lockName\n     * @param writeMode 是否写入模式(支持读并发).\n     * @return null 或 进程锁, 如果锁已经被占用, 返回null.\n     */\n    public static ProcessLock tryLock(final String lockName, final boolean writeMode) {\n        return tryLockInternal(lockName, customHash(lockName), writeMode);\n    }\n\n    /**\n     * 获取进程锁\n     *\n     * @param lockName\n     * @param writeMode         是否写入模式(支持读并发).\n     * @param maxWaitTimeMillis 最大值 1000 * 60\n     * @return null 或 进程锁, 如果锁已经被占用, 则在超时时间内继续尝试获取该锁.\n     */\n    public static ProcessLock tryLock(final String lockName, final boolean writeMode, final long maxWaitTimeMillis) throws InterruptedException {\n        ProcessLock lock = null;\n        long expiryTime = System.currentTimeMillis() + maxWaitTimeMillis;\n        String hash = customHash(lockName);\n        while (System.currentTimeMillis() < expiryTime) {\n            lock = tryLockInternal(lockName, hash, writeMode);\n            if (lock != null) {\n                break;\n            } else {\n                try {\n                    Thread.sleep(1); // milliseconds\n                } catch (InterruptedException iex) {\n                    throw iex;\n                } catch (Throwable ignored) {\n                }\n            }\n        }\n\n        return lock;\n    }\n\n    /**\n     * 锁是否有效\n     *\n     * @return\n     */\n    public boolean isValid() {\n        return isValid(mFileLock);\n    }\n\n    /**\n     * 释放锁\n     */\n    public void release() {\n        release(mLockName, mFileLock, mFile, mStream);\n    }\n\n    /**\n     * 释放锁\n     */\n    @Override\n    public void close() throws IOException {\n        release();\n    }\n\n    private static boolean isValid(FileLock fileLock) {\n        return fileLock != null && fileLock.isValid();\n    }\n\n    private static void release(String lockName, FileLock fileLock, File file, Closeable stream) {\n        synchronized (LOCK_MAP) {\n            if (fileLock != null) {\n                try {\n                    LOCK_MAP.remove(lockName, fileLock.hashCode());\n                    ConcurrentHashMap<Integer, ProcessLock> locks = LOCK_MAP.get(lockName);\n                    if (locks == null || locks.isEmpty()) {\n                        IOUtil.deleteFileOrDir(file);\n                    }\n\n                    if (fileLock.channel().isOpen()) {\n                        fileLock.release();\n                    }\n                } catch (Throwable ex) {\n                    LogUtil.e(ex.getMessage(), ex);\n                } finally {\n                    IOUtil.closeQuietly(fileLock.channel());\n                }\n            }\n\n            IOUtil.closeQuietly(stream);\n        }\n    }\n\n    private final static DecimalFormat FORMAT = new DecimalFormat(\"0.##################\");\n\n    // 取得字符串的自定义hash值, 尽量保证255字节内的hash不重复.\n    private static String customHash(String str) {\n        if (TextUtils.isEmpty(str)) return \"0\";\n        double hash = 0.0;\n        byte[] bytes = str.getBytes();\n        for (int i = 0; i < str.length(); i++) {\n            hash = (255.0 * hash + bytes[i]) * 0.005;\n        }\n        return FORMAT.format(hash);\n    }\n\n    private static ProcessLock tryLockInternal(final String lockName, final String hash, final boolean writeMode) {\n        synchronized (LOCK_MAP) {\n\n            ConcurrentHashMap<Integer, ProcessLock> locks = LOCK_MAP.get(lockName);\n            if (locks != null && !locks.isEmpty()) {\n                Iterator<Map.Entry<Integer, ProcessLock>> itr = locks.entrySet().iterator();\n                while (itr.hasNext()) {\n                    Map.Entry<Integer, ProcessLock> entry = itr.next();\n                    ProcessLock value = entry.getValue();\n                    if (value != null) {\n                        if (!value.isValid()) {\n                            itr.remove();\n                        } else if (writeMode) {\n                            return null;\n                        } else if (value.mWriteMode) {\n                            return null;\n                        }\n                    } else {\n                        itr.remove();\n                    }\n                }\n            }\n\n            FileChannel channel = null;\n            Closeable stream = null;\n            try {\n                File file = new File(\n                        x.app().getDir(LOCK_FILE_DIR, Context.MODE_PRIVATE),\n                        hash);\n                if (file.exists() || file.createNewFile()) {\n\n                    if (writeMode) {\n                        FileOutputStream out = new FileOutputStream(file, false);\n                        channel = out.getChannel();\n                        stream = out;\n                    } else {\n                        FileInputStream in = new FileInputStream(file);\n                        channel = in.getChannel();\n                        stream = in;\n                    }\n                    if (channel != null) {\n                        FileLock fileLock = channel.tryLock(0L, Long.MAX_VALUE, !writeMode);\n                        if (isValid(fileLock)) {\n                            ProcessLock result = new ProcessLock(lockName, file, fileLock, stream, writeMode);\n                            LOCK_MAP.put(lockName, fileLock.hashCode(), result);\n                            return result;\n                        } else {\n                            release(lockName, fileLock, file, stream);\n                        }\n                    } else {\n                        throw new IOException(\"can not get file channel:\" + file.getAbsolutePath());\n                    }\n                }\n            } catch (Throwable ignored) {\n                LogUtil.d(\"tryLock: \" + lockName + \", \" + ignored.getMessage());\n                IOUtil.closeQuietly(stream);\n                IOUtil.closeQuietly(channel);\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public String toString() {\n        return mLockName + \": \" + mFile.getName();\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        super.finalize();\n        this.release();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/config/DbConfigs.java",
    "content": "package org.xutils.config;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.ex.DbException;\n\n/**\n * Created by wyouflf on 15/7/31.\n * 全局db配置\n */\npublic enum DbConfigs {\n    HTTP(new DbManager.DaoConfig()\n            .setDbName(\"xUtils_http_cache.db\")\n            .setDbVersion(1)\n            .setDbOpenListener(new DbManager.DbOpenListener() {\n                @Override\n                public void onDbOpened(DbManager db) {\n                    db.getDatabase().enableWriteAheadLogging();\n                }\n            })\n            .setDbUpgradeListener(new DbManager.DbUpgradeListener() {\n                @Override\n                public void onUpgrade(DbManager db, int oldVersion, int newVersion) {\n                    try {\n                        db.dropDb(); // 默认删除所有表\n                    } catch (DbException ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            })),\n\n    COOKIE(new DbManager.DaoConfig()\n            .setDbName(\"xUtils_http_cookie.db\")\n            .setDbVersion(1)\n            .setDbOpenListener(new DbManager.DbOpenListener() {\n                @Override\n                public void onDbOpened(DbManager db) {\n                    db.getDatabase().enableWriteAheadLogging();\n                }\n            })\n            .setDbUpgradeListener(new DbManager.DbUpgradeListener() {\n                @Override\n                public void onUpgrade(DbManager db, int oldVersion, int newVersion) {\n                    try {\n                        db.dropDb(); // 默认删除所有表\n                    } catch (DbException ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }));\n\n    private DbManager.DaoConfig config;\n\n    DbConfigs(DbManager.DaoConfig config) {\n        this.config = config;\n    }\n\n    public DbManager.DaoConfig getConfig() {\n        return config;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/CursorUtils.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.table.ColumnEntity;\nimport org.xutils.db.table.DbModel;\nimport org.xutils.db.table.TableEntity;\n\nimport java.util.HashMap;\n\n/*package*/ final class CursorUtils {\n\n    public static <T> T getEntity(TableEntity<T> table, final Cursor cursor) throws Throwable {\n        T entity = table.createEntity();\n        HashMap<String, ColumnEntity> columnMap = table.getColumnMap();\n        int columnCount = cursor.getColumnCount();\n        for (int i = 0; i < columnCount; i++) {\n            String columnName = cursor.getColumnName(i);\n            ColumnEntity column = columnMap.get(columnName);\n            if (column != null) {\n                column.setValueFromCursor(entity, cursor, i);\n            }\n        }\n        return entity;\n    }\n\n    public static DbModel getDbModel(final Cursor cursor) {\n        DbModel result = new DbModel();\n        int columnCount = cursor.getColumnCount();\n        for (int i = 0; i < columnCount; i++) {\n            result.add(cursor.getColumnName(i), cursor.getString(i));\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/DbManagerImpl.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db;\n\nimport android.database.Cursor;\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteStatement;\nimport android.os.Build;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.db.sqlite.SqlInfo;\nimport org.xutils.db.sqlite.SqlInfoBuilder;\nimport org.xutils.db.sqlite.WhereBuilder;\nimport org.xutils.db.table.ColumnEntity;\nimport org.xutils.db.table.DbBase;\nimport org.xutils.db.table.DbModel;\nimport org.xutils.db.table.TableEntity;\nimport org.xutils.ex.DbException;\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\n\npublic final class DbManagerImpl extends DbBase {\n\n    //*************************************** create instance ****************************************************\n\n    /**\n     * key: dbName\n     */\n    private final static HashMap<DaoConfig, DbManagerImpl> DAO_MAP = new HashMap<DaoConfig, DbManagerImpl>();\n\n    private SQLiteDatabase database;\n    private DaoConfig daoConfig;\n    private boolean allowTransaction;\n\n    private DbManagerImpl(DaoConfig config) {\n        if (config == null) {\n            throw new IllegalArgumentException(\"daoConfig may not be null\");\n        }\n        this.daoConfig = config;\n        this.allowTransaction = config.isAllowTransaction();\n        this.database = openOrCreateDatabase(config);\n        DbOpenListener dbOpenListener = config.getDbOpenListener();\n        if (dbOpenListener != null) {\n            dbOpenListener.onDbOpened(this);\n        }\n    }\n\n    public synchronized static DbManager getInstance(DaoConfig daoConfig) {\n\n        if (daoConfig == null) {//使用默认配置\n            daoConfig = new DaoConfig();\n        }\n\n        DbManagerImpl dao = DAO_MAP.get(daoConfig);\n        if (dao == null) {\n            dao = new DbManagerImpl(daoConfig);\n            DAO_MAP.put(daoConfig, dao);\n        } else {\n            dao.daoConfig = daoConfig;\n        }\n\n        // update the database if needed\n        SQLiteDatabase database = dao.database;\n        int oldVersion = database.getVersion();\n        int newVersion = daoConfig.getDbVersion();\n        if (oldVersion != newVersion) {\n            if (oldVersion != 0) {\n                DbUpgradeListener upgradeListener = daoConfig.getDbUpgradeListener();\n                if (upgradeListener != null) {\n                    upgradeListener.onUpgrade(dao, oldVersion, newVersion);\n                } else {\n                    try {\n                        dao.dropDb();\n                    } catch (DbException e) {\n                        LogUtil.e(e.getMessage(), e);\n                    }\n                }\n            }\n            database.setVersion(newVersion);\n        }\n\n        return dao;\n    }\n\n    @Override\n    public SQLiteDatabase getDatabase() {\n        return database;\n    }\n\n    @Override\n    public DaoConfig getDaoConfig() {\n        return daoConfig;\n    }\n\n    //*********************************************** operations ********************************************************\n\n    @Override\n    public void saveOrUpdate(Object entity) throws DbException {\n        try {\n            beginTransaction();\n\n            if (entity instanceof List) {\n                List<?> entities = (List<?>) entity;\n                if (entities.isEmpty()) return;\n                TableEntity<?> table = this.getTable(entities.get(0).getClass());\n                createTableIfNotExist(table);\n                for (Object item : entities) {\n                    saveOrUpdateWithoutTransaction(table, item);\n                }\n            } else {\n                TableEntity<?> table = this.getTable(entity.getClass());\n                createTableIfNotExist(table);\n                saveOrUpdateWithoutTransaction(table, entity);\n            }\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n    }\n\n    @Override\n    public void replace(Object entity) throws DbException {\n        try {\n            beginTransaction();\n\n            if (entity instanceof List) {\n                List<?> entities = (List<?>) entity;\n                if (entities.isEmpty()) return;\n                TableEntity<?> table = this.getTable(entities.get(0).getClass());\n                createTableIfNotExist(table);\n                for (Object item : entities) {\n                    execNonQuery(SqlInfoBuilder.buildReplaceSqlInfo(table, item));\n                }\n            } else {\n                TableEntity<?> table = this.getTable(entity.getClass());\n                createTableIfNotExist(table);\n                execNonQuery(SqlInfoBuilder.buildReplaceSqlInfo(table, entity));\n            }\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n    }\n\n    @Override\n    public void save(Object entity) throws DbException {\n        try {\n            beginTransaction();\n\n            if (entity instanceof List) {\n                List<?> entities = (List<?>) entity;\n                if (entities.isEmpty()) return;\n                TableEntity<?> table = this.getTable(entities.get(0).getClass());\n                createTableIfNotExist(table);\n                for (Object item : entities) {\n                    execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, item));\n                }\n            } else {\n                TableEntity<?> table = this.getTable(entity.getClass());\n                createTableIfNotExist(table);\n                execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, entity));\n            }\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n    }\n\n    @Override\n    public boolean saveBindingId(Object entity) throws DbException {\n        boolean result = false;\n        try {\n            beginTransaction();\n\n            if (entity instanceof List) {\n                List<?> entities = (List<?>) entity;\n                if (entities.isEmpty()) return false;\n                TableEntity<?> table = this.getTable(entities.get(0).getClass());\n                createTableIfNotExist(table);\n                for (Object item : entities) {\n                    if (!saveBindingIdWithoutTransaction(table, item)) {\n                        throw new DbException(\"saveBindingId error, transaction will not commit!\");\n                    }\n                }\n            } else {\n                TableEntity<?> table = this.getTable(entity.getClass());\n                createTableIfNotExist(table);\n                result = saveBindingIdWithoutTransaction(table, entity);\n            }\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n        return result;\n    }\n\n    @Override\n    public void deleteById(Class<?> entityType, Object idValue) throws DbException {\n        TableEntity<?> table = this.getTable(entityType);\n        if (!table.tableIsExist()) return;\n        try {\n            beginTransaction();\n\n            execNonQuery(SqlInfoBuilder.buildDeleteSqlInfoById(table, idValue));\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n    }\n\n    @Override\n    public void delete(Object entity) throws DbException {\n        try {\n            beginTransaction();\n\n            if (entity instanceof List) {\n                List<?> entities = (List<?>) entity;\n                if (entities.isEmpty()) return;\n                TableEntity<?> table = this.getTable(entities.get(0).getClass());\n                if (!table.tableIsExist()) return;\n                for (Object item : entities) {\n                    execNonQuery(SqlInfoBuilder.buildDeleteSqlInfo(table, item));\n                }\n            } else {\n                TableEntity<?> table = this.getTable(entity.getClass());\n                if (!table.tableIsExist()) return;\n                execNonQuery(SqlInfoBuilder.buildDeleteSqlInfo(table, entity));\n            }\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n    }\n\n    @Override\n    public void delete(Class<?> entityType) throws DbException {\n        delete(entityType, null);\n    }\n\n    @Override\n    public int delete(Class<?> entityType, WhereBuilder whereBuilder) throws DbException {\n        TableEntity<?> table = this.getTable(entityType);\n        if (!table.tableIsExist()) return 0;\n        int result = 0;\n        try {\n            beginTransaction();\n\n            result = executeUpdateDelete(SqlInfoBuilder.buildDeleteSqlInfo(table, whereBuilder));\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n        return result;\n    }\n\n    @Override\n    public void update(Object entity, String... updateColumnNames) throws DbException {\n        try {\n            beginTransaction();\n\n            if (entity instanceof List) {\n                List<?> entities = (List<?>) entity;\n                if (entities.isEmpty()) return;\n                TableEntity<?> table = this.getTable(entities.get(0).getClass());\n                if (!table.tableIsExist()) return;\n                for (Object item : entities) {\n                    execNonQuery(SqlInfoBuilder.buildUpdateSqlInfo(table, item, updateColumnNames));\n                }\n            } else {\n                TableEntity<?> table = this.getTable(entity.getClass());\n                if (!table.tableIsExist()) return;\n                execNonQuery(SqlInfoBuilder.buildUpdateSqlInfo(table, entity, updateColumnNames));\n            }\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n    }\n\n    @Override\n    public int update(Class<?> entityType, WhereBuilder whereBuilder, KeyValue... nameValuePairs) throws DbException {\n        TableEntity<?> table = this.getTable(entityType);\n        if (!table.tableIsExist()) return 0;\n\n        int result = 0;\n        try {\n            beginTransaction();\n\n            result = executeUpdateDelete(SqlInfoBuilder.buildUpdateSqlInfo(table, whereBuilder, nameValuePairs));\n\n            setTransactionSuccessful();\n        } finally {\n            endTransaction();\n        }\n\n        return result;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> T findById(Class<T> entityType, Object idValue) throws DbException {\n        TableEntity<T> table = this.getTable(entityType);\n        if (!table.tableIsExist()) return null;\n\n        Selector selector = Selector.from(table).where(table.getId().getName(), \"=\", idValue);\n\n        String sql = selector.limit(1).toString();\n        Cursor cursor = execQuery(sql);\n        if (cursor != null) {\n            try {\n                if (cursor.moveToNext()) {\n                    return CursorUtils.getEntity(table, cursor);\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public <T> T findFirst(Class<T> entityType) throws DbException {\n        return this.selector(entityType).findFirst();\n    }\n\n    @Override\n    public <T> List<T> findAll(Class<T> entityType) throws DbException {\n        return this.selector(entityType).findAll();\n    }\n\n    @Override\n    public <T> Selector<T> selector(Class<T> entityType) throws DbException {\n        return Selector.from(this.getTable(entityType));\n    }\n\n    @Override\n    public DbModel findDbModelFirst(SqlInfo sqlInfo) throws DbException {\n        Cursor cursor = execQuery(sqlInfo);\n        if (cursor != null) {\n            try {\n                if (cursor.moveToNext()) {\n                    return CursorUtils.getDbModel(cursor);\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    public List<DbModel> findDbModelAll(SqlInfo sqlInfo) throws DbException {\n        List<DbModel> dbModelList = new ArrayList<DbModel>();\n\n        Cursor cursor = execQuery(sqlInfo);\n        if (cursor != null) {\n            try {\n                while (cursor.moveToNext()) {\n                    dbModelList.add(CursorUtils.getDbModel(cursor));\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return dbModelList;\n    }\n\n    //******************************************** config ******************************************************\n\n    private SQLiteDatabase openOrCreateDatabase(DaoConfig config) {\n        SQLiteDatabase result = null;\n\n        File dbDir = config.getDbDir();\n        if (dbDir != null && (dbDir.exists() || dbDir.mkdirs())) {\n            File dbFile = new File(dbDir, config.getDbName());\n            result = SQLiteDatabase.openOrCreateDatabase(dbFile, null);\n        } else {\n            result = x.app().openOrCreateDatabase(config.getDbName(), 0, null);\n        }\n        return result;\n    }\n\n    //***************************** private operations with out transaction *****************************\n    private void saveOrUpdateWithoutTransaction(TableEntity<?> table, Object entity) throws DbException {\n        ColumnEntity id = table.getId();\n        if (id.isAutoId()) {\n            if (id.getColumnValue(entity) != null) {\n                execNonQuery(SqlInfoBuilder.buildUpdateSqlInfo(table, entity));\n            } else {\n                saveBindingIdWithoutTransaction(table, entity);\n            }\n        } else {\n            execNonQuery(SqlInfoBuilder.buildReplaceSqlInfo(table, entity));\n        }\n    }\n\n    private boolean saveBindingIdWithoutTransaction(TableEntity<?> table, Object entity) throws DbException {\n        ColumnEntity id = table.getId();\n        if (id.isAutoId()) {\n            execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, entity));\n            long idValue = getLastAutoIncrementId(table.getName());\n            if (idValue == -1) {\n                return false;\n            }\n            id.setAutoIdValue(entity, idValue);\n            return true;\n        } else {\n            execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(table, entity));\n            return true;\n        }\n    }\n\n    //************************************************ tools ***********************************\n\n    private long getLastAutoIncrementId(String tableName) throws DbException {\n        long id = -1;\n        Cursor cursor = execQuery(\"SELECT seq FROM sqlite_sequence WHERE name='\" + tableName + \"' LIMIT 1\");\n        if (cursor != null) {\n            try {\n                if (cursor.moveToNext()) {\n                    id = cursor.getLong(0);\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return id;\n    }\n\n    @Override\n    public void close() throws IOException {\n        if (DAO_MAP.containsKey(daoConfig)) {\n            DAO_MAP.remove(daoConfig);\n            this.database.close();\n        }\n    }\n\n    ///////////////////////////////////// exec sql /////////////////////////////////////////////////////\n\n    private void beginTransaction() {\n        if (allowTransaction) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && database.isWriteAheadLoggingEnabled()) {\n                database.beginTransactionNonExclusive();\n            } else {\n                database.beginTransaction();\n            }\n        }\n    }\n\n    private void setTransactionSuccessful() {\n        if (allowTransaction) {\n            database.setTransactionSuccessful();\n        }\n    }\n\n    private void endTransaction() {\n        if (allowTransaction) {\n            database.endTransaction();\n        }\n    }\n\n\n    @Override\n    public int executeUpdateDelete(SqlInfo sqlInfo) throws DbException {\n        SQLiteStatement statement = null;\n        try {\n            statement = sqlInfo.buildStatement(database);\n            return statement.executeUpdateDelete();\n        } catch (Throwable e) {\n            throw new DbException(e);\n        } finally {\n            if (statement != null) {\n                try {\n                    statement.releaseReference();\n                } catch (Throwable ex) {\n                    LogUtil.e(ex.getMessage(), ex);\n                }\n            }\n        }\n    }\n\n    @Override\n    public int executeUpdateDelete(String sql) throws DbException {\n        SQLiteStatement statement = null;\n        try {\n            statement = database.compileStatement(sql);\n            return statement.executeUpdateDelete();\n        } catch (Throwable e) {\n            throw new DbException(e);\n        } finally {\n            if (statement != null) {\n                try {\n                    statement.releaseReference();\n                } catch (Throwable ex) {\n                    LogUtil.e(ex.getMessage(), ex);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void execNonQuery(SqlInfo sqlInfo) throws DbException {\n        SQLiteStatement statement = null;\n        try {\n            statement = sqlInfo.buildStatement(database);\n            statement.execute();\n        } catch (Throwable e) {\n            throw new DbException(e);\n        } finally {\n            if (statement != null) {\n                try {\n                    statement.releaseReference();\n                } catch (Throwable ex) {\n                    LogUtil.e(ex.getMessage(), ex);\n                }\n            }\n        }\n    }\n\n    @Override\n    public void execNonQuery(String sql) throws DbException {\n        try {\n            database.execSQL(sql);\n        } catch (Throwable e) {\n            throw new DbException(e);\n        }\n    }\n\n    @Override\n    public Cursor execQuery(SqlInfo sqlInfo) throws DbException {\n        try {\n            return database.rawQuery(sqlInfo.getSql(), sqlInfo.getBindArgsAsStrArray());\n        } catch (Throwable e) {\n            throw new DbException(e);\n        }\n    }\n\n    @Override\n    public Cursor execQuery(String sql) throws DbException {\n        try {\n            return database.rawQuery(sql, null);\n        } catch (Throwable e) {\n            throw new DbException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/DbModelSelector.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db;\n\nimport android.database.Cursor;\nimport android.text.TextUtils;\n\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.db.sqlite.WhereBuilder;\nimport org.xutils.db.table.DbModel;\nimport org.xutils.db.table.TableEntity;\nimport org.xutils.ex.DbException;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Author: wyouflf\n * Date: 13-8-10\n * Time: 下午2:15\n */\npublic final class DbModelSelector {\n\n    private String[] columnExpressions;\n    private String groupByColumnName;\n    private WhereBuilder having;\n\n    private Selector<?> selector;\n\n    private DbModelSelector(TableEntity<?> table) {\n        selector = Selector.from(table);\n    }\n\n    protected DbModelSelector(Selector<?> selector, String groupByColumnName) {\n        this.selector = selector;\n        this.groupByColumnName = groupByColumnName;\n    }\n\n    protected DbModelSelector(Selector<?> selector, String[] columnExpressions) {\n        this.selector = selector;\n        this.columnExpressions = columnExpressions;\n    }\n\n    /*package*/\n    static DbModelSelector from(TableEntity<?> table) {\n        return new DbModelSelector(table);\n    }\n\n    public DbModelSelector where(WhereBuilder whereBuilder) {\n        selector.where(whereBuilder);\n        return this;\n    }\n\n    public DbModelSelector where(String columnName, String op, Object value) {\n        selector.where(columnName, op, value);\n        return this;\n    }\n\n    public DbModelSelector and(String columnName, String op, Object value) {\n        selector.and(columnName, op, value);\n        return this;\n    }\n\n    public DbModelSelector and(WhereBuilder where) {\n        selector.and(where);\n        return this;\n    }\n\n    public DbModelSelector or(String columnName, String op, Object value) {\n        selector.or(columnName, op, value);\n        return this;\n    }\n\n    public DbModelSelector or(WhereBuilder where) {\n        selector.or(where);\n        return this;\n    }\n\n    public DbModelSelector expr(String expr) {\n        selector.expr(expr);\n        return this;\n    }\n\n    public DbModelSelector groupBy(String columnName) {\n        this.groupByColumnName = columnName;\n        return this;\n    }\n\n    public DbModelSelector having(WhereBuilder whereBuilder) {\n        this.having = whereBuilder;\n        return this;\n    }\n\n    public DbModelSelector select(String... columnExpressions) {\n        this.columnExpressions = columnExpressions;\n        return this;\n    }\n\n    public DbModelSelector orderBy(String columnName) {\n        selector.orderBy(columnName);\n        return this;\n    }\n\n    public DbModelSelector orderBy(String columnName, boolean desc) {\n        selector.orderBy(columnName, desc);\n        return this;\n    }\n\n    public DbModelSelector limit(int limit) {\n        selector.limit(limit);\n        return this;\n    }\n\n    public DbModelSelector offset(int offset) {\n        selector.offset(offset);\n        return this;\n    }\n\n    public TableEntity<?> getTable() {\n        return selector.getTable();\n    }\n\n    public DbModel findFirst() throws DbException {\n        TableEntity<?> table = selector.getTable();\n        if (!table.tableIsExist()) return null;\n\n        this.limit(1);\n        Cursor cursor = table.getDb().execQuery(this.toString());\n        if (cursor != null) {\n            try {\n                if (cursor.moveToNext()) {\n                    return CursorUtils.getDbModel(cursor);\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return null;\n    }\n\n    public List<DbModel> findAll() throws DbException {\n        TableEntity<?> table = selector.getTable();\n        if (!table.tableIsExist()) return null;\n\n        List<DbModel> result = null;\n\n        Cursor cursor = table.getDb().execQuery(this.toString());\n        if (cursor != null) {\n            try {\n                result = new ArrayList<DbModel>();\n                while (cursor.moveToNext()) {\n                    DbModel entity = CursorUtils.getDbModel(cursor);\n                    result.add(entity);\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder result = new StringBuilder();\n        result.append(\"SELECT \");\n        if (columnExpressions != null && columnExpressions.length > 0) {\n            for (String columnExpression : columnExpressions) {\n                result.append(columnExpression);\n                result.append(\",\");\n            }\n            result.deleteCharAt(result.length() - 1);\n        } else {\n            if (!TextUtils.isEmpty(groupByColumnName)) {\n                result.append(groupByColumnName);\n            } else {\n                result.append(\"*\");\n            }\n        }\n        result.append(\" FROM \").append(\"\\\"\").append(selector.getTable().getName()).append(\"\\\"\");\n        WhereBuilder whereBuilder = selector.getWhereBuilder();\n        if (whereBuilder != null && whereBuilder.getWhereItemSize() > 0) {\n            result.append(\" WHERE \").append(whereBuilder.toString());\n        }\n        if (!TextUtils.isEmpty(groupByColumnName)) {\n            result.append(\" GROUP BY \").append(\"\\\"\").append(groupByColumnName).append(\"\\\"\");\n            if (having != null && having.getWhereItemSize() > 0) {\n                result.append(\" HAVING \").append(having.toString());\n            }\n        }\n        List<Selector.OrderBy> orderByList = selector.getOrderByList();\n        if (orderByList != null && orderByList.size() > 0) {\n            for (int i = 0; i < orderByList.size(); i++) {\n                result.append(\" ORDER BY \").append(orderByList.get(i).toString()).append(',');\n            }\n            result.deleteCharAt(result.length() - 1);\n        }\n        if (selector.getLimit() > 0) {\n            result.append(\" LIMIT \").append(selector.getLimit());\n            result.append(\" OFFSET \").append(selector.getOffset());\n        }\n        return result.toString();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/Selector.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db;\n\nimport android.database.Cursor;\n\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.db.sqlite.WhereBuilder;\nimport org.xutils.db.table.DbModel;\nimport org.xutils.db.table.TableEntity;\nimport org.xutils.ex.DbException;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Author: wyouflf\n * Date: 13-8-9\n * Time: 下午10:19\n */\npublic final class Selector<T> {\n\n    private final TableEntity<T> table;\n\n    private WhereBuilder whereBuilder;\n    private List<OrderBy> orderByList;\n    private int limit = 0;\n    private int offset = 0;\n\n    private Selector(TableEntity<T> table) {\n        this.table = table;\n    }\n\n    /*package*/\n    static <T> Selector<T> from(TableEntity<T> table) {\n        return new Selector<T>(table);\n    }\n\n    public Selector<T> where(WhereBuilder whereBuilder) {\n        this.whereBuilder = whereBuilder;\n        return this;\n    }\n\n    public Selector<T> where(String columnName, String op, Object value) {\n        this.whereBuilder = WhereBuilder.b(columnName, op, value);\n        return this;\n    }\n\n    public Selector<T> and(String columnName, String op, Object value) {\n        this.whereBuilder.and(columnName, op, value);\n        return this;\n    }\n\n    public Selector<T> and(WhereBuilder where) {\n        this.whereBuilder.and(where);\n        return this;\n    }\n\n    public Selector<T> or(String columnName, String op, Object value) {\n        this.whereBuilder.or(columnName, op, value);\n        return this;\n    }\n\n    public Selector or(WhereBuilder where) {\n        this.whereBuilder.or(where);\n        return this;\n    }\n\n    public Selector<T> expr(String expr) {\n        if (this.whereBuilder == null) {\n            this.whereBuilder = WhereBuilder.b();\n        }\n        this.whereBuilder.expr(expr);\n        return this;\n    }\n\n    public DbModelSelector groupBy(String columnName) {\n        return new DbModelSelector(this, columnName);\n    }\n\n    public DbModelSelector select(String... columnExpressions) {\n        return new DbModelSelector(this, columnExpressions);\n    }\n\n    public Selector<T> orderBy(String columnName) {\n        if (orderByList == null) {\n            orderByList = new ArrayList<OrderBy>(5);\n        }\n        orderByList.add(new OrderBy(columnName));\n        return this;\n    }\n\n    public Selector<T> orderBy(String columnName, boolean desc) {\n        if (orderByList == null) {\n            orderByList = new ArrayList<OrderBy>(5);\n        }\n        orderByList.add(new OrderBy(columnName, desc));\n        return this;\n    }\n\n    public Selector<T> limit(int limit) {\n        this.limit = limit;\n        return this;\n    }\n\n    public Selector<T> offset(int offset) {\n        this.offset = offset;\n        return this;\n    }\n\n    public TableEntity<T> getTable() {\n        return table;\n    }\n\n    public WhereBuilder getWhereBuilder() {\n        return whereBuilder;\n    }\n\n    public List<OrderBy> getOrderByList() {\n        return orderByList;\n    }\n\n    public int getLimit() {\n        return limit;\n    }\n\n    public int getOffset() {\n        return offset;\n    }\n\n    public T findFirst() throws DbException {\n        if (!table.tableIsExist()) return null;\n\n        this.limit(1);\n        Cursor cursor = table.getDb().execQuery(this.toString());\n        if (cursor != null) {\n            try {\n                if (cursor.moveToNext()) {\n                    return CursorUtils.getEntity(table, cursor);\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return null;\n    }\n\n    public List<T> findAll() throws DbException {\n        if (!table.tableIsExist()) return null;\n\n        List<T> result = null;\n        Cursor cursor = table.getDb().execQuery(this.toString());\n        if (cursor != null) {\n            try {\n                result = new ArrayList<T>();\n                while (cursor.moveToNext()) {\n                    T entity = CursorUtils.getEntity(table, cursor);\n                    result.add(entity);\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n        return result;\n    }\n\n    public long count() throws DbException {\n        if (!table.tableIsExist()) return 0;\n\n        DbModelSelector dmSelector = this.select(\"count(\\\"\" + table.getId().getName() + \"\\\") as count\");\n        DbModel firstModel = dmSelector.findFirst();\n        if (firstModel != null) {\n            return firstModel.getLong(\"count\");\n        }\n        return 0;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder result = new StringBuilder();\n        result.append(\"SELECT \");\n        result.append(\"*\");\n        result.append(\" FROM \").append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n        if (whereBuilder != null && whereBuilder.getWhereItemSize() > 0) {\n            result.append(\" WHERE \").append(whereBuilder.toString());\n        }\n        if (orderByList != null && orderByList.size() > 0) {\n            result.append(\" ORDER BY \");\n            for (OrderBy orderBy : orderByList) {\n                result.append(orderBy.toString()).append(',');\n            }\n            result.deleteCharAt(result.length() - 1);\n        }\n        if (limit > 0) {\n            result.append(\" LIMIT \").append(limit);\n            result.append(\" OFFSET \").append(offset);\n        }\n        return result.toString();\n    }\n\n    public static class OrderBy {\n        private String columnName;\n        private boolean desc;\n\n        public OrderBy(String columnName) {\n            this.columnName = columnName;\n        }\n\n        public OrderBy(String columnName, boolean desc) {\n            this.columnName = columnName;\n            this.desc = desc;\n        }\n\n        @Override\n        public String toString() {\n            return \"\\\"\" + columnName + \"\\\"\" + (desc ? \" DESC\" : \" ASC\");\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/annotation/Column.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Column {\n\n    String name();\n\n    String property() default \"\";\n\n    boolean isId() default false;\n\n    boolean autoGen() default true;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/annotation/Table.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Table {\n\n    String name();\n\n    String onCreated() default \"\";\n}"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/BooleanColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class BooleanColumnConverter implements ColumnConverter<Boolean> {\n    @Override\n    public Boolean getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getInt(index) == 1;\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Boolean fieldValue) {\n        if (fieldValue == null) return null;\n        return fieldValue ? 1 : 0;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/ByteArrayColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class ByteArrayColumnConverter implements ColumnConverter<byte[]> {\n    @Override\n    public byte[] getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getBlob(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(byte[] fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.BLOB;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/ByteColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class ByteColumnConverter implements ColumnConverter<Byte> {\n    @Override\n    public Byte getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : (byte) cursor.getInt(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Byte fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/CharColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class CharColumnConverter implements ColumnConverter<Character> {\n    @Override\n    public Character getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : (char) cursor.getInt(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Character fieldValue) {\n        if (fieldValue == null) return null;\n        return (int) fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/ColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午8:57\n */\npublic interface ColumnConverter<T> {\n\n    T getFieldValue(final Cursor cursor, int index);\n\n    Object fieldValue2DbValue(T fieldValue);\n\n    ColumnDbType getColumnDbType();\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/ColumnConverterFactory.java",
    "content": "package org.xutils.db.converter;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.db.sqlite.ColumnDbType;\n\nimport java.util.Date;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:27\n */\npublic final class ColumnConverterFactory {\n\n    private ColumnConverterFactory() {\n    }\n\n    public static ColumnConverter getColumnConverter(Class columnType) {\n        ColumnConverter result = null;\n        if (columnType_columnConverter_map.containsKey(columnType.getName())) {\n            result = columnType_columnConverter_map.get(columnType.getName());\n        } else if (ColumnConverter.class.isAssignableFrom(columnType)) {\n            try {\n                ColumnConverter columnConverter = (ColumnConverter) columnType.newInstance();\n                if (columnConverter != null) {\n                    columnType_columnConverter_map.put(columnType.getName(), columnConverter);\n                }\n                result = columnConverter;\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n        }\n\n        if (result == null) {\n            throw new RuntimeException(\"Database Column Not Support: \" + columnType.getName() +\n                    \", please impl ColumnConverter or use ColumnConverterFactory#registerColumnConverter(...)\");\n        }\n\n        return result;\n    }\n\n    public static ColumnDbType getDbColumnType(Class columnType) {\n        ColumnConverter converter = getColumnConverter(columnType);\n        return converter.getColumnDbType();\n    }\n\n    public static void registerColumnConverter(Class columnType, ColumnConverter columnConverter) {\n        columnType_columnConverter_map.put(columnType.getName(), columnConverter);\n    }\n\n    public static boolean isSupportColumnConverter(Class columnType) {\n        if (columnType_columnConverter_map.containsKey(columnType.getName())) {\n            return true;\n        } else if (ColumnConverter.class.isAssignableFrom(columnType)) {\n            try {\n                ColumnConverter columnConverter = (ColumnConverter) columnType.newInstance();\n                if (columnConverter != null) {\n                    columnType_columnConverter_map.put(columnType.getName(), columnConverter);\n                }\n                return columnConverter == null;\n            } catch (Throwable e) {\n            }\n        }\n        return false;\n    }\n\n    private static final ConcurrentHashMap<String, ColumnConverter> columnType_columnConverter_map;\n\n    static {\n        columnType_columnConverter_map = new ConcurrentHashMap<String, ColumnConverter>();\n\n        BooleanColumnConverter booleanColumnConverter = new BooleanColumnConverter();\n        columnType_columnConverter_map.put(boolean.class.getName(), booleanColumnConverter);\n        columnType_columnConverter_map.put(Boolean.class.getName(), booleanColumnConverter);\n\n        ByteArrayColumnConverter byteArrayColumnConverter = new ByteArrayColumnConverter();\n        columnType_columnConverter_map.put(byte[].class.getName(), byteArrayColumnConverter);\n\n        ByteColumnConverter byteColumnConverter = new ByteColumnConverter();\n        columnType_columnConverter_map.put(byte.class.getName(), byteColumnConverter);\n        columnType_columnConverter_map.put(Byte.class.getName(), byteColumnConverter);\n\n        CharColumnConverter charColumnConverter = new CharColumnConverter();\n        columnType_columnConverter_map.put(char.class.getName(), charColumnConverter);\n        columnType_columnConverter_map.put(Character.class.getName(), charColumnConverter);\n\n        DateColumnConverter dateColumnConverter = new DateColumnConverter();\n        columnType_columnConverter_map.put(Date.class.getName(), dateColumnConverter);\n\n        DoubleColumnConverter doubleColumnConverter = new DoubleColumnConverter();\n        columnType_columnConverter_map.put(double.class.getName(), doubleColumnConverter);\n        columnType_columnConverter_map.put(Double.class.getName(), doubleColumnConverter);\n\n        FloatColumnConverter floatColumnConverter = new FloatColumnConverter();\n        columnType_columnConverter_map.put(float.class.getName(), floatColumnConverter);\n        columnType_columnConverter_map.put(Float.class.getName(), floatColumnConverter);\n\n        IntegerColumnConverter integerColumnConverter = new IntegerColumnConverter();\n        columnType_columnConverter_map.put(int.class.getName(), integerColumnConverter);\n        columnType_columnConverter_map.put(Integer.class.getName(), integerColumnConverter);\n\n        LongColumnConverter longColumnConverter = new LongColumnConverter();\n        columnType_columnConverter_map.put(long.class.getName(), longColumnConverter);\n        columnType_columnConverter_map.put(Long.class.getName(), longColumnConverter);\n\n        ShortColumnConverter shortColumnConverter = new ShortColumnConverter();\n        columnType_columnConverter_map.put(short.class.getName(), shortColumnConverter);\n        columnType_columnConverter_map.put(Short.class.getName(), shortColumnConverter);\n\n        SqlDateColumnConverter sqlDateColumnConverter = new SqlDateColumnConverter();\n        columnType_columnConverter_map.put(java.sql.Date.class.getName(), sqlDateColumnConverter);\n\n        StringColumnConverter stringColumnConverter = new StringColumnConverter();\n        columnType_columnConverter_map.put(String.class.getName(), stringColumnConverter);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/DateColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\nimport java.util.Date;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class DateColumnConverter implements ColumnConverter<Date> {\n    @Override\n    public Date getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : new Date(cursor.getLong(index));\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Date fieldValue) {\n        if (fieldValue == null) return null;\n        return fieldValue.getTime();\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/DoubleColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class DoubleColumnConverter implements ColumnConverter<Double> {\n    @Override\n    public Double getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getDouble(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Double fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.REAL;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/FloatColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class FloatColumnConverter implements ColumnConverter<Float> {\n    @Override\n    public Float getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getFloat(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Float fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.REAL;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/IntegerColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class IntegerColumnConverter implements ColumnConverter<Integer> {\n    @Override\n    public Integer getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getInt(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Integer fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/LongColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class LongColumnConverter implements ColumnConverter<Long> {\n    @Override\n    public Long getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getLong(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Long fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/ShortColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class ShortColumnConverter implements ColumnConverter<Short> {\n    @Override\n    public Short getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getShort(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(Short fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/SqlDateColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class SqlDateColumnConverter implements ColumnConverter<java.sql.Date> {\n    @Override\n    public java.sql.Date getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : new java.sql.Date(cursor.getLong(index));\n    }\n\n    @Override\n    public Object fieldValue2DbValue(java.sql.Date fieldValue) {\n        if (fieldValue == null) return null;\n        return fieldValue.getTime();\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.INTEGER;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/converter/StringColumnConverter.java",
    "content": "package org.xutils.db.converter;\n\nimport android.database.Cursor;\n\nimport org.xutils.db.sqlite.ColumnDbType;\n\n/**\n * Author: wyouflf\n * Date: 13-11-4\n * Time: 下午10:51\n */\npublic class StringColumnConverter implements ColumnConverter<String> {\n    @Override\n    public String getFieldValue(final Cursor cursor, int index) {\n        return cursor.isNull(index) ? null : cursor.getString(index);\n    }\n\n    @Override\n    public Object fieldValue2DbValue(String fieldValue) {\n        return fieldValue;\n    }\n\n    @Override\n    public ColumnDbType getColumnDbType() {\n        return ColumnDbType.TEXT;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/sqlite/ColumnDbType.java",
    "content": "package org.xutils.db.sqlite;\n\n/**\n * Created by wyouflf on 14-2-20.\n */\npublic enum ColumnDbType {\n\n    INTEGER(\"INTEGER\"), REAL(\"REAL\"), TEXT(\"TEXT\"), BLOB(\"BLOB\");\n\n    private String value;\n\n    ColumnDbType(String value) {\n        this.value = value;\n    }\n\n    @Override\n    public String toString() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/sqlite/SqlInfo.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.sqlite;\n\nimport android.database.sqlite.SQLiteDatabase;\nimport android.database.sqlite.SQLiteStatement;\n\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.db.converter.ColumnConverter;\nimport org.xutils.db.converter.ColumnConverterFactory;\nimport org.xutils.db.table.ColumnUtils;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic final class SqlInfo {\n\n    private String sql;\n    private List<KeyValue> bindArgs;\n\n    public SqlInfo() {\n    }\n\n    public SqlInfo(String sql) {\n        this.sql = sql;\n    }\n\n    public String getSql() {\n        return sql;\n    }\n\n    public void setSql(String sql) {\n        this.sql = sql;\n    }\n\n    public void addBindArg(KeyValue kv) {\n        if (bindArgs == null) {\n            bindArgs = new ArrayList<KeyValue>();\n        }\n        bindArgs.add(kv);\n    }\n\n    public void addBindArgs(List<KeyValue> bindArgs) {\n        if (this.bindArgs == null) {\n            this.bindArgs = bindArgs;\n        } else {\n            this.bindArgs.addAll(bindArgs);\n        }\n    }\n\n    public SQLiteStatement buildStatement(SQLiteDatabase database) {\n        SQLiteStatement result = database.compileStatement(sql);\n        if (bindArgs != null) {\n            for (int i = 1; i < bindArgs.size() + 1; i++) {\n                KeyValue kv = bindArgs.get(i - 1);\n                Object value = ColumnUtils.convert2DbValueIfNeeded(kv.value);\n                if (value == null) {\n                    result.bindNull(i);\n                } else {\n                    ColumnConverter converter = ColumnConverterFactory.getColumnConverter(value.getClass());\n                    ColumnDbType type = converter.getColumnDbType();\n                    switch (type) {\n                        case INTEGER:\n                            result.bindLong(i, ((Number) value).longValue());\n                            break;\n                        case REAL:\n                            result.bindDouble(i, ((Number) value).doubleValue());\n                            break;\n                        case TEXT:\n                            result.bindString(i, value.toString());\n                            break;\n                        case BLOB:\n                            result.bindBlob(i, (byte[]) value);\n                            break;\n                        default:\n                            result.bindNull(i);\n                            break;\n                    } // end switch\n                }\n            }\n        }\n        return result;\n    }\n\n    public Object[] getBindArgs() {\n        Object[] result = null;\n        if (bindArgs != null) {\n            result = new Object[bindArgs.size()];\n            for (int i = 0; i < bindArgs.size(); i++) {\n                result[i] = ColumnUtils.convert2DbValueIfNeeded(bindArgs.get(i).value);\n            }\n        }\n        return result;\n    }\n\n    public String[] getBindArgsAsStrArray() {\n        String[] result = null;\n        if (bindArgs != null) {\n            result = new String[bindArgs.size()];\n            for (int i = 0; i < bindArgs.size(); i++) {\n                Object value = ColumnUtils.convert2DbValueIfNeeded(bindArgs.get(i).value);\n                result[i] = value == null ? null : value.toString();\n            }\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/sqlite/SqlInfoBuilder.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.sqlite;\n\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.db.table.ColumnEntity;\nimport org.xutils.db.table.TableEntity;\nimport org.xutils.ex.DbException;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * Build \"insert\", \"replace\",，\"update\", \"delete\" and \"create\" sql.\n */\npublic final class SqlInfoBuilder {\n\n    private static final ConcurrentHashMap<TableEntity<?>, String> INSERT_SQL_CACHE = new ConcurrentHashMap<TableEntity<?>, String>();\n    private static final ConcurrentHashMap<TableEntity<?>, String> REPLACE_SQL_CACHE = new ConcurrentHashMap<TableEntity<?>, String>();\n\n    private SqlInfoBuilder() {\n    }\n\n    //*********************************************** insert sql ***********************************************\n\n    public static SqlInfo buildInsertSqlInfo(TableEntity<?> table, Object entity) throws DbException {\n\n        List<KeyValue> keyValueList = entity2KeyValueList(table, entity);\n        if (keyValueList.size() == 0) return null;\n\n        SqlInfo result = new SqlInfo();\n        String sql = INSERT_SQL_CACHE.get(table);\n        if (sql == null) {\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"INSERT INTO \");\n            builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n            builder.append(\" (\");\n            for (KeyValue kv : keyValueList) {\n                builder.append(\"\\\"\").append(kv.key).append(\"\\\"\").append(',');\n            }\n            builder.deleteCharAt(builder.length() - 1);\n            builder.append(\") VALUES (\");\n\n            int length = keyValueList.size();\n            for (int i = 0; i < length; i++) {\n                builder.append(\"?,\");\n            }\n            builder.deleteCharAt(builder.length() - 1);\n            builder.append(\")\");\n\n            sql = builder.toString();\n            result.setSql(sql);\n            result.addBindArgs(keyValueList);\n            INSERT_SQL_CACHE.put(table, sql);\n        } else {\n            result.setSql(sql);\n            result.addBindArgs(keyValueList);\n        }\n\n        return result;\n    }\n\n    //*********************************************** replace sql ***********************************************\n\n    public static SqlInfo buildReplaceSqlInfo(TableEntity<?> table, Object entity) throws DbException {\n\n        List<KeyValue> keyValueList = entity2KeyValueList(table, entity);\n        if (keyValueList.size() == 0) return null;\n\n        SqlInfo result = new SqlInfo();\n        String sql = REPLACE_SQL_CACHE.get(table);\n        if (sql == null) {\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"REPLACE INTO \");\n            builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n            builder.append(\" (\");\n            for (KeyValue kv : keyValueList) {\n                builder.append(\"\\\"\").append(kv.key).append(\"\\\"\").append(',');\n            }\n            builder.deleteCharAt(builder.length() - 1);\n            builder.append(\") VALUES (\");\n\n            int length = keyValueList.size();\n            for (int i = 0; i < length; i++) {\n                builder.append(\"?,\");\n            }\n            builder.deleteCharAt(builder.length() - 1);\n            builder.append(\")\");\n\n            sql = builder.toString();\n            result.setSql(sql);\n            result.addBindArgs(keyValueList);\n            REPLACE_SQL_CACHE.put(table, sql);\n        } else {\n            result.setSql(sql);\n            result.addBindArgs(keyValueList);\n        }\n\n        return result;\n    }\n\n    //*********************************************** delete sql ***********************************************\n\n    public static SqlInfo buildDeleteSqlInfo(TableEntity<?> table, Object entity) throws DbException {\n        SqlInfo result = new SqlInfo();\n\n        ColumnEntity id = table.getId();\n        Object idValue = id.getColumnValue(entity);\n\n        if (idValue == null) {\n            throw new DbException(\"this entity[\" + table.getEntityType() + \"]'s id value is null\");\n        }\n        StringBuilder builder = new StringBuilder(\"DELETE FROM \");\n        builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n        builder.append(\" WHERE \").append(WhereBuilder.b(id.getName(), \"=\", idValue));\n\n        result.setSql(builder.toString());\n\n        return result;\n    }\n\n    public static SqlInfo buildDeleteSqlInfoById(TableEntity<?> table, Object idValue) throws DbException {\n        SqlInfo result = new SqlInfo();\n\n        ColumnEntity id = table.getId();\n\n        if (idValue == null) {\n            throw new DbException(\"this entity[\" + table.getEntityType() + \"]'s id value is null\");\n        }\n        StringBuilder builder = new StringBuilder(\"DELETE FROM \");\n        builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n        builder.append(\" WHERE \").append(WhereBuilder.b(id.getName(), \"=\", idValue));\n\n        result.setSql(builder.toString());\n\n        return result;\n    }\n\n    public static SqlInfo buildDeleteSqlInfo(TableEntity<?> table, WhereBuilder whereBuilder) throws DbException {\n        StringBuilder builder = new StringBuilder(\"DELETE FROM \");\n        builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n\n        if (whereBuilder != null && whereBuilder.getWhereItemSize() > 0) {\n            builder.append(\" WHERE \").append(whereBuilder.toString());\n        }\n\n        return new SqlInfo(builder.toString());\n    }\n\n    //*********************************************** update sql ***********************************************\n\n    public static SqlInfo buildUpdateSqlInfo(TableEntity<?> table, Object entity, String... updateColumnNames) throws DbException {\n\n        List<KeyValue> keyValueList = entity2KeyValueList(table, entity);\n        if (keyValueList.size() == 0) return null;\n\n        HashSet<String> updateColumnNameSet = null;\n        if (updateColumnNames != null && updateColumnNames.length > 0) {\n            updateColumnNameSet = new HashSet<String>(updateColumnNames.length);\n            Collections.addAll(updateColumnNameSet, updateColumnNames);\n        }\n\n        ColumnEntity id = table.getId();\n        Object idValue = id.getColumnValue(entity);\n\n        if (idValue == null) {\n            throw new DbException(\"this entity[\" + table.getEntityType() + \"]'s id value is null\");\n        }\n\n        SqlInfo result = new SqlInfo();\n        StringBuilder builder = new StringBuilder(\"UPDATE \");\n        builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n        builder.append(\" SET \");\n        for (KeyValue kv : keyValueList) {\n            if (updateColumnNameSet == null || updateColumnNameSet.contains(kv.key)) {\n                builder.append(\"\\\"\").append(kv.key).append(\"\\\"\").append(\"=?,\");\n                result.addBindArg(kv);\n            }\n        }\n        builder.deleteCharAt(builder.length() - 1);\n        builder.append(\" WHERE \").append(WhereBuilder.b(id.getName(), \"=\", idValue));\n\n        result.setSql(builder.toString());\n        return result;\n    }\n\n    public static SqlInfo buildUpdateSqlInfo(TableEntity<?> table, WhereBuilder whereBuilder, KeyValue... nameValuePairs) throws DbException {\n\n        if (nameValuePairs == null || nameValuePairs.length == 0) return null;\n\n        SqlInfo result = new SqlInfo();\n        StringBuilder builder = new StringBuilder(\"UPDATE \");\n        builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n        builder.append(\" SET \");\n        for (KeyValue kv : nameValuePairs) {\n            builder.append(\"\\\"\").append(kv.key).append(\"\\\"\").append(\"=?,\");\n            result.addBindArg(kv);\n        }\n        builder.deleteCharAt(builder.length() - 1);\n        if (whereBuilder != null && whereBuilder.getWhereItemSize() > 0) {\n            builder.append(\" WHERE \").append(whereBuilder.toString());\n        }\n\n        result.setSql(builder.toString());\n        return result;\n    }\n\n    //*********************************************** others ***********************************************\n\n    public static SqlInfo buildCreateTableSqlInfo(TableEntity<?> table) throws DbException {\n        ColumnEntity id = table.getId();\n\n        StringBuilder builder = new StringBuilder();\n        builder.append(\"CREATE TABLE IF NOT EXISTS \");\n        builder.append(\"\\\"\").append(table.getName()).append(\"\\\"\");\n        builder.append(\" ( \");\n\n        if (id.isAutoId()) {\n            builder.append(\"\\\"\").append(id.getName()).append(\"\\\"\").append(\" INTEGER PRIMARY KEY AUTOINCREMENT, \");\n        } else {\n            builder.append(\"\\\"\").append(id.getName()).append(\"\\\"\").append(id.getColumnDbType()).append(\" PRIMARY KEY, \");\n        }\n\n        Collection<ColumnEntity> columns = table.getColumnMap().values();\n        for (ColumnEntity column : columns) {\n            if (column.isId()) continue;\n            builder.append(\"\\\"\").append(column.getName()).append(\"\\\"\");\n            builder.append(' ').append(column.getColumnDbType());\n            builder.append(' ').append(column.getProperty());\n            builder.append(',');\n        }\n\n        builder.deleteCharAt(builder.length() - 1);\n        builder.append(\" )\");\n        return new SqlInfo(builder.toString());\n    }\n\n    public static List<KeyValue> entity2KeyValueList(TableEntity<?> table, Object entity) {\n\n        Collection<ColumnEntity> columns = table.getColumnMap().values();\n        List<KeyValue> keyValueList = new ArrayList<KeyValue>(columns.size());\n        for (ColumnEntity column : columns) {\n            KeyValue kv = column2KeyValue(entity, column);\n            if (kv != null) {\n                keyValueList.add(kv);\n            }\n        }\n\n        return keyValueList;\n    }\n\n    private static KeyValue column2KeyValue(Object entity, ColumnEntity column) {\n        if (column.isAutoId()) {\n            return null;\n        }\n\n        String key = column.getName();\n        Object value = column.getFieldValue(entity);\n        return new KeyValue(key, value);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/sqlite/WhereBuilder.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.sqlite;\n\nimport android.text.TextUtils;\n\nimport org.xutils.db.converter.ColumnConverterFactory;\nimport org.xutils.db.table.ColumnUtils;\n\nimport java.lang.reflect.Array;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\n/**\n * Author: wyouflf\n * Date: 13-7-29\n * Time: 上午9:35\n */\npublic class WhereBuilder {\n\n    private final List<String> whereItems;\n\n    private WhereBuilder() {\n        this.whereItems = new ArrayList<String>();\n    }\n\n    /**\n     * create new instance\n     *\n     * @return\n     */\n    public static WhereBuilder b() {\n        return new WhereBuilder();\n    }\n\n    /**\n     * create new instance\n     *\n     * @param columnName\n     * @param op         operator: \"=\",\"LIKE\",\"IN\",\"BETWEEN\"...\n     * @param value\n     * @return\n     */\n    public static WhereBuilder b(String columnName, String op, Object value) {\n        WhereBuilder result = new WhereBuilder();\n        result.appendCondition(null, columnName, op, value);\n        return result;\n    }\n\n    /**\n     * add AND condition\n     *\n     * @param columnName\n     * @param op         operator: \"=\",\"LIKE\",\"IN\",\"BETWEEN\"...\n     * @param value\n     * @return\n     */\n    public WhereBuilder and(String columnName, String op, Object value) {\n        appendCondition(whereItems.size() == 0 ? null : \"AND\", columnName, op, value);\n        return this;\n    }\n\n    /**\n     * add AND condition\n     *\n     * @param where expr(\"[AND] (\" + where.toString() + \")\")\n     * @return\n     */\n    public WhereBuilder and(WhereBuilder where) {\n        String condition = whereItems.size() == 0 ? \" \" : \"AND \";\n        return expr(condition + \"(\" + where.toString() + \")\");\n    }\n\n    /**\n     * add OR condition\n     *\n     * @param columnName\n     * @param op         operator: \"=\",\"LIKE\",\"IN\",\"BETWEEN\"...\n     * @param value\n     * @return\n     */\n    public WhereBuilder or(String columnName, String op, Object value) {\n        appendCondition(whereItems.size() == 0 ? null : \"OR\", columnName, op, value);\n        return this;\n    }\n\n    /**\n     * add OR condition\n     *\n     * @param where expr(\"[OR] (\" + where.toString() + \")\")\n     * @return\n     */\n    public WhereBuilder or(WhereBuilder where) {\n        String condition = whereItems.size() == 0 ? \" \" : \"OR \";\n        return expr(condition + \"(\" + where.toString() + \")\");\n    }\n\n    public WhereBuilder expr(String expr) {\n        whereItems.add(\" \" + expr);\n        return this;\n    }\n\n    public int getWhereItemSize() {\n        return whereItems.size();\n    }\n\n    @Override\n    public String toString() {\n        if (whereItems.size() == 0) {\n            return \"\";\n        }\n        StringBuilder sb = new StringBuilder();\n        for (String item : whereItems) {\n            sb.append(item);\n        }\n        return sb.toString();\n    }\n\n    private void appendCondition(String conj, String columnName, String op, Object value) {\n        StringBuilder builder = new StringBuilder();\n\n        if (whereItems.size() > 0) {\n            builder.append(\" \");\n        }\n\n        // append conj\n        if (!TextUtils.isEmpty(conj)) {\n            builder.append(conj).append(\" \");\n        }\n\n        // append columnName\n        builder.append(\"\\\"\").append(columnName).append(\"\\\"\");\n\n        // convert op\n        if (\"!=\".equals(op)) {\n            op = \"<>\";\n        } else if (\"==\".equals(op)) {\n            op = \"=\";\n        }\n\n        // append op & value\n        if (value == null) {\n            if (\"=\".equals(op)) {\n                builder.append(\" IS NULL\");\n            } else if (\"<>\".equals(op)) {\n                builder.append(\" IS NOT NULL\");\n            } else {\n                builder.append(\" \").append(op).append(\" NULL\");\n            }\n        } else {\n            builder.append(\" \").append(op).append(\" \");\n\n            if (\"IN\".equalsIgnoreCase(op)) {\n                Iterable<?> items = null;\n                if (value instanceof Iterable) {\n                    items = (Iterable<?>) value;\n                } else if (value.getClass().isArray()) {\n                    int len = Array.getLength(value);\n                    List<Object> arrayList = new ArrayList<Object>(len);\n                    for (int i = 0; i < len; i++) {\n                        arrayList.add(Array.get(value, i));\n                    }\n                    items = arrayList;\n                }\n                if (items != null) {\n                    StringBuilder inSb = new StringBuilder(\"(\");\n                    for (Object item : items) {\n                        Object itemColValue = ColumnUtils.convert2DbValueIfNeeded(item);\n                        if (ColumnDbType.TEXT.equals(ColumnConverterFactory.getDbColumnType(itemColValue.getClass()))) {\n                            String valueStr = itemColValue.toString();\n                            if (valueStr.indexOf('\\'') != -1) { // convert single quotations\n                                valueStr = valueStr.replace(\"'\", \"''\");\n                            }\n                            inSb.append(\"'\").append(valueStr).append(\"'\");\n                        } else {\n                            inSb.append(itemColValue);\n                        }\n                        inSb.append(\",\");\n                    }\n                    inSb.deleteCharAt(inSb.length() - 1);\n                    inSb.append(\")\");\n                    builder.append(inSb.toString());\n                } else {\n                    throw new IllegalArgumentException(\"value must be an Array or an Iterable.\");\n                }\n            } else if (\"BETWEEN\".equalsIgnoreCase(op)) {\n                Iterable<?> items = null;\n                if (value instanceof Iterable) {\n                    items = (Iterable<?>) value;\n                } else if (value.getClass().isArray()) {\n                    int len = Array.getLength(value);\n                    List<Object> arrayList = new ArrayList<Object>(len);\n                    for (int i = 0; i < len; i++) {\n                        arrayList.add(Array.get(value, i));\n                    }\n                    items = arrayList;\n                }\n                if (items != null) {\n                    Iterator<?> iterator = items.iterator();\n                    if (!iterator.hasNext())\n                        throw new IllegalArgumentException(\"value must have tow items.\");\n                    Object start = iterator.next();\n                    if (!iterator.hasNext())\n                        throw new IllegalArgumentException(\"value must have tow items.\");\n                    Object end = iterator.next();\n\n                    Object startColValue = ColumnUtils.convert2DbValueIfNeeded(start);\n                    Object endColValue = ColumnUtils.convert2DbValueIfNeeded(end);\n\n                    if (ColumnDbType.TEXT.equals(ColumnConverterFactory.getDbColumnType(startColValue.getClass()))) {\n                        String startStr = startColValue.toString();\n                        if (startStr.indexOf('\\'') != -1) { // convert single quotations\n                            startStr = startStr.replace(\"'\", \"''\");\n                        }\n                        String endStr = endColValue.toString();\n                        if (endStr.indexOf('\\'') != -1) { // convert single quotations\n                            endStr = endStr.replace(\"'\", \"''\");\n                        }\n                        builder.append(\"'\").append(startStr).append(\"'\");\n                        builder.append(\" AND \");\n                        builder.append(\"'\").append(endStr).append(\"'\");\n                    } else {\n                        builder.append(startColValue);\n                        builder.append(\" AND \");\n                        builder.append(endColValue);\n                    }\n                } else {\n                    throw new IllegalArgumentException(\"value must be an Array or an Iterable.\");\n                }\n            } else {\n                value = ColumnUtils.convert2DbValueIfNeeded(value);\n                if (ColumnDbType.TEXT.equals(ColumnConverterFactory.getDbColumnType(value.getClass()))) {\n                    String valueStr = value.toString();\n                    if (valueStr.indexOf('\\'') != -1) { // convert single quotations\n                        valueStr = valueStr.replace(\"'\", \"''\");\n                    }\n                    builder.append(\"'\").append(valueStr).append(\"'\");\n                } else {\n                    builder.append(value);\n                }\n            }\n        }\n        whereItems.add(builder.toString());\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/table/ColumnEntity.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.table;\n\nimport android.database.Cursor;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.db.annotation.Column;\nimport org.xutils.db.converter.ColumnConverter;\nimport org.xutils.db.converter.ColumnConverterFactory;\nimport org.xutils.db.sqlite.ColumnDbType;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\npublic final class ColumnEntity {\n\n    protected final String name;\n    private final String property;\n    private final boolean isId;\n    private final boolean isAutoId;\n\n    protected final Method getMethod;\n    protected final Method setMethod;\n\n    protected final Field columnField;\n    protected final ColumnConverter columnConverter;\n\n    /* package */ ColumnEntity(Class<?> entityType, Field field, Column column) {\n        field.setAccessible(true);\n\n        this.columnField = field;\n        this.name = column.name();\n        this.property = column.property();\n        this.isId = column.isId();\n\n        Class<?> fieldType = field.getType();\n        this.isAutoId = this.isId && column.autoGen() && ColumnUtils.isAutoIdType(fieldType);\n        this.columnConverter = ColumnConverterFactory.getColumnConverter(fieldType);\n\n\n        this.getMethod = ColumnUtils.findGetMethod(entityType, field);\n        if (this.getMethod != null && !this.getMethod.isAccessible()) {\n            this.getMethod.setAccessible(true);\n        }\n        this.setMethod = ColumnUtils.findSetMethod(entityType, field);\n        if (this.setMethod != null && !this.setMethod.isAccessible()) {\n            this.setMethod.setAccessible(true);\n        }\n    }\n\n    public void setValueFromCursor(Object entity, Cursor cursor, int index) {\n        Object value = columnConverter.getFieldValue(cursor, index);\n        if (value == null) return;\n\n        if (setMethod != null) {\n            try {\n                setMethod.invoke(entity, value);\n            } catch (Throwable e) {\n                LogUtil.e(e.getMessage(), e);\n            }\n        } else {\n            try {\n                this.columnField.set(entity, value);\n            } catch (Throwable e) {\n                LogUtil.e(e.getMessage(), e);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public Object getColumnValue(Object entity) {\n        Object fieldValue = getFieldValue(entity);\n        if (this.isAutoId && (fieldValue.equals(0L) || fieldValue.equals(0))) {\n            return null;\n        }\n        return columnConverter.fieldValue2DbValue(fieldValue);\n    }\n\n    public void setAutoIdValue(Object entity, long value) {\n        Object idValue = value;\n        if (ColumnUtils.isInteger(columnField.getType())) {\n            idValue = (int) value;\n        }\n\n        if (setMethod != null) {\n            try {\n                setMethod.invoke(entity, idValue);\n            } catch (Throwable e) {\n                LogUtil.e(e.getMessage(), e);\n            }\n        } else {\n            try {\n                this.columnField.set(entity, idValue);\n            } catch (Throwable e) {\n                LogUtil.e(e.getMessage(), e);\n            }\n        }\n    }\n\n    public Object getFieldValue(Object entity) {\n        Object fieldValue = null;\n        if (entity != null) {\n            if (getMethod != null) {\n                try {\n                    fieldValue = getMethod.invoke(entity);\n                } catch (Throwable e) {\n                    LogUtil.e(e.getMessage(), e);\n                }\n            } else {\n                try {\n                    fieldValue = this.columnField.get(entity);\n                } catch (Throwable e) {\n                    LogUtil.e(e.getMessage(), e);\n                }\n            }\n        }\n        return fieldValue;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getProperty() {\n        return property;\n    }\n\n    public boolean isId() {\n        return isId;\n    }\n\n    public boolean isAutoId() {\n        return isAutoId;\n    }\n\n    public Field getColumnField() {\n        return columnField;\n    }\n\n    public ColumnConverter getColumnConverter() {\n        return columnConverter;\n    }\n\n    public ColumnDbType getColumnDbType() {\n        return columnConverter.getColumnDbType();\n    }\n\n    @Override\n    public String toString() {\n        return name;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/table/ColumnUtils.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.table;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.db.converter.ColumnConverter;\nimport org.xutils.db.converter.ColumnConverterFactory;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\n\npublic final class ColumnUtils {\n\n    private ColumnUtils() {\n    }\n\n    private static final HashSet<Class<?>> BOOLEAN_TYPES = new HashSet<Class<?>>(2);\n    private static final HashSet<Class<?>> INTEGER_TYPES = new HashSet<Class<?>>(2);\n    private static final HashSet<Class<?>> AUTO_INCREMENT_TYPES = new HashSet<Class<?>>(4);\n\n    static {\n        BOOLEAN_TYPES.add(boolean.class);\n        BOOLEAN_TYPES.add(Boolean.class);\n\n        INTEGER_TYPES.add(int.class);\n        INTEGER_TYPES.add(Integer.class);\n\n        AUTO_INCREMENT_TYPES.addAll(INTEGER_TYPES);\n        AUTO_INCREMENT_TYPES.add(long.class);\n        AUTO_INCREMENT_TYPES.add(Long.class);\n    }\n\n    public static boolean isAutoIdType(Class<?> fieldType) {\n        return AUTO_INCREMENT_TYPES.contains(fieldType);\n    }\n\n    public static boolean isInteger(Class<?> fieldType) {\n        return INTEGER_TYPES.contains(fieldType);\n    }\n\n    public static boolean isBoolean(Class<?> fieldType) {\n        return BOOLEAN_TYPES.contains(fieldType);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static Object convert2DbValueIfNeeded(final Object value) {\n        Object result = value;\n        if (value != null) {\n            Class<?> valueType = value.getClass();\n            ColumnConverter converter = ColumnConverterFactory.getColumnConverter(valueType);\n            result = converter.fieldValue2DbValue(value);\n        }\n        return result;\n    }\n\n    /* package */\n    static Method findGetMethod(Class<?> entityType, Field field) {\n        if (Object.class.equals(entityType)) return null;\n\n        String fieldName = field.getName();\n        Method getMethod = null;\n        if (isBoolean(field.getType())) {\n            getMethod = findBooleanGetMethod(entityType, fieldName);\n        }\n        if (getMethod == null) {\n            String methodName = \"get\" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);\n            try {\n                getMethod = entityType.getDeclaredMethod(methodName);\n            } catch (NoSuchMethodException e) {\n                LogUtil.d(entityType.getName() + \"#\" + methodName + \" not exist\");\n            }\n        }\n\n        if (getMethod == null) {\n            return findGetMethod(entityType.getSuperclass(), field);\n        }\n        return getMethod;\n    }\n\n    /* package */\n    static Method findSetMethod(Class<?> entityType, Field field) {\n        if (Object.class.equals(entityType)) return null;\n\n        String fieldName = field.getName();\n        Class<?> fieldType = field.getType();\n        Method setMethod = null;\n        if (isBoolean(fieldType)) {\n            setMethod = findBooleanSetMethod(entityType, fieldName, fieldType);\n        }\n        if (setMethod == null) {\n            String methodName = \"set\" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);\n            try {\n                setMethod = entityType.getDeclaredMethod(methodName, fieldType);\n            } catch (NoSuchMethodException e) {\n                LogUtil.d(entityType.getName() + \"#\" + methodName + \" not exist\");\n            }\n        }\n\n        if (setMethod == null) {\n            return findSetMethod(entityType.getSuperclass(), field);\n        }\n        return setMethod;\n    }\n\n    private static Method findBooleanGetMethod(Class<?> entityType, final String fieldName) {\n        String methodName = null;\n        if (fieldName.startsWith(\"is\")) {\n            methodName = fieldName;\n        } else {\n            methodName = \"is\" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);\n        }\n        try {\n            return entityType.getDeclaredMethod(methodName);\n        } catch (NoSuchMethodException e) {\n            LogUtil.d(entityType.getName() + \"#\" + methodName + \" not exist\");\n        }\n        return null;\n    }\n\n    private static Method findBooleanSetMethod(Class<?> entityType, final String fieldName, Class<?> fieldType) {\n        String methodName = null;\n        if (fieldName.startsWith(\"is\")) {\n            methodName = \"set\" + fieldName.substring(2, 3).toUpperCase() + fieldName.substring(3);\n        } else {\n            methodName = \"set\" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);\n        }\n        try {\n            return entityType.getDeclaredMethod(methodName, fieldType);\n        } catch (NoSuchMethodException e) {\n            LogUtil.d(entityType.getName() + \"#\" + methodName + \" not exist\");\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/table/DbBase.java",
    "content": "package org.xutils.db.table;\n\nimport android.database.Cursor;\nimport android.text.TextUtils;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.db.sqlite.SqlInfo;\nimport org.xutils.db.sqlite.SqlInfoBuilder;\nimport org.xutils.ex.DbException;\n\nimport java.util.HashMap;\n\n/**\n * DbManager基类, 包含表结构的基本操作.\n * Created by wyouflf on 16/1/22.\n */\npublic abstract class DbBase implements DbManager {\n\n    private final HashMap<Class<?>, TableEntity<?>> tableMap = new HashMap<Class<?>, TableEntity<?>>();\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T> TableEntity<T> getTable(Class<T> entityType) throws DbException {\n        synchronized (tableMap) {\n            TableEntity<T> table = (TableEntity<T>) tableMap.get(entityType);\n            if (table == null) {\n                try {\n                    table = new TableEntity<T>(this, entityType);\n                } catch (Throwable ex) {\n                    throw new DbException(ex);\n                }\n                tableMap.put(entityType, table);\n            }\n\n            return table;\n        }\n    }\n\n    @Override\n    public void dropTable(Class<?> entityType) throws DbException {\n        TableEntity<?> table = this.getTable(entityType);\n        if (!table.tableIsExist()) return;\n        execNonQuery(\"DROP TABLE \\\"\" + table.getName() + \"\\\"\");\n        table.setCheckedDatabase(false);\n        this.removeTable(entityType);\n    }\n\n    @Override\n    public void dropDb() throws DbException {\n        Cursor cursor = execQuery(\"SELECT name FROM sqlite_master WHERE type='table' AND name<>'sqlite_sequence'\");\n        if (cursor != null) {\n            try {\n                while (cursor.moveToNext()) {\n                    try {\n                        String tableName = cursor.getString(0);\n                        execNonQuery(\"DROP TABLE \" + tableName);\n                    } catch (Throwable e) {\n                        LogUtil.e(e.getMessage(), e);\n                    }\n                }\n\n                synchronized (tableMap) {\n                    for (TableEntity<?> table : tableMap.values()) {\n                        table.setCheckedDatabase(false);\n                    }\n                    tableMap.clear();\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n    }\n\n    @Override\n    public void addColumn(Class<?> entityType, String column) throws DbException {\n        TableEntity<?> table = this.getTable(entityType);\n        ColumnEntity col = table.getColumnMap().get(column);\n        if (col != null) {\n            StringBuilder builder = new StringBuilder();\n            builder.append(\"ALTER TABLE \").append(\"\\\"\").append(table.getName()).append(\"\\\"\").\n                    append(\" ADD COLUMN \").append(\"\\\"\").append(col.getName()).append(\"\\\"\").\n                    append(\" \").append(col.getColumnDbType()).\n                    append(\" \").append(col.getProperty());\n            execNonQuery(builder.toString());\n        }\n    }\n\n    protected void createTableIfNotExist(TableEntity<?> table) throws DbException {\n        if (!table.tableIsExist()) {\n            synchronized (table.getClass()) {\n                if (!table.tableIsExist()) {\n                    SqlInfo sqlInfo = SqlInfoBuilder.buildCreateTableSqlInfo(table);\n                    execNonQuery(sqlInfo);\n                    String execAfterTableCreated = table.getOnCreated();\n                    if (!TextUtils.isEmpty(execAfterTableCreated)) {\n                        execNonQuery(execAfterTableCreated);\n                    }\n                    table.setCheckedDatabase(true);\n                    TableCreateListener listener = this.getDaoConfig().getTableCreateListener();\n                    if (listener != null) {\n                        listener.onTableCreated(this, table);\n                    }\n                }\n            }\n        }\n    }\n\n    protected void removeTable(Class<?> entityType) {\n        synchronized (tableMap) {\n            tableMap.remove(entityType);\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/table/DbModel.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.table;\n\nimport android.text.TextUtils;\n\nimport java.util.Date;\nimport java.util.HashMap;\n\npublic final class DbModel {\n\n    /**\n     * key: columnName\n     * value: valueStr\n     */\n    private HashMap<String, String> dataMap = new HashMap<String, String>();\n\n    public String getString(String columnName) {\n        return dataMap.get(columnName);\n    }\n\n    public int getInt(String columnName) {\n        return Integer.valueOf(dataMap.get(columnName));\n    }\n\n    public boolean getBoolean(String columnName) {\n        String value = dataMap.get(columnName);\n        if (value != null) {\n            return value.length() == 1 ? \"1\".equals(value) : Boolean.valueOf(value);\n        }\n        return false;\n    }\n\n    public double getDouble(String columnName) {\n        return Double.valueOf(dataMap.get(columnName));\n    }\n\n    public float getFloat(String columnName) {\n        return Float.valueOf(dataMap.get(columnName));\n    }\n\n    public long getLong(String columnName) {\n        return Long.valueOf(dataMap.get(columnName));\n    }\n\n    public Date getDate(String columnName) {\n        long date = Long.valueOf(dataMap.get(columnName));\n        return new Date(date);\n    }\n\n    public java.sql.Date getSqlDate(String columnName) {\n        long date = Long.valueOf(dataMap.get(columnName));\n        return new java.sql.Date(date);\n    }\n\n    public void add(String columnName, String valueStr) {\n        dataMap.put(columnName, valueStr);\n    }\n\n    /**\n     * @return key: columnName\n     */\n    public HashMap<String, String> getDataMap() {\n        return dataMap;\n    }\n\n    /**\n     * @param columnName\n     * @return\n     */\n    public boolean isEmpty(String columnName) {\n        return TextUtils.isEmpty(dataMap.get(columnName));\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/table/TableEntity.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.table;\n\nimport android.database.Cursor;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.db.annotation.Table;\nimport org.xutils.ex.DbException;\n\nimport java.lang.reflect.Constructor;\nimport java.util.LinkedHashMap;\n\n\npublic final class TableEntity<T> {\n\n    private final DbManager db;\n    private final String name;\n    private final String onCreated;\n    private ColumnEntity id;\n    private Class<T> entityType;\n    private Constructor<T> constructor;\n    private volatile boolean checkedDatabase;\n\n    /**\n     * key: columnName\n     */\n    private final LinkedHashMap<String, ColumnEntity> columnMap;\n\n    /*package*/ TableEntity(DbManager db, Class<T> entityType) throws Throwable {\n        this.db = db;\n        this.entityType = entityType;\n        this.constructor = entityType.getConstructor();\n        this.constructor.setAccessible(true);\n        Table table = entityType.getAnnotation(Table.class);\n        this.name = table.name();\n        this.onCreated = table.onCreated();\n        this.columnMap = TableUtils.findColumnMap(entityType);\n\n        for (ColumnEntity column : columnMap.values()) {\n            if (column.isId()) {\n                this.id = column;\n                break;\n            }\n        }\n    }\n\n    public T createEntity() throws Throwable {\n        return this.constructor.newInstance();\n    }\n\n    public boolean tableIsExist() throws DbException {\n        if (this.isCheckedDatabase()) {\n            return true;\n        }\n\n        Cursor cursor = db.execQuery(\"SELECT COUNT(*) AS c FROM sqlite_master WHERE type='table' AND name='\" + name + \"'\");\n        if (cursor != null) {\n            try {\n                if (cursor.moveToNext()) {\n                    int count = cursor.getInt(0);\n                    if (count > 0) {\n                        this.setCheckedDatabase(true);\n                        return true;\n                    }\n                }\n            } catch (Throwable e) {\n                throw new DbException(e);\n            } finally {\n                IOUtil.closeQuietly(cursor);\n            }\n        }\n\n        return false;\n    }\n\n    public DbManager getDb() {\n        return db;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public Class<T> getEntityType() {\n        return entityType;\n    }\n\n    public String getOnCreated() {\n        return onCreated;\n    }\n\n    public ColumnEntity getId() {\n        return id;\n    }\n\n    public LinkedHashMap<String, ColumnEntity> getColumnMap() {\n        return columnMap;\n    }\n\n    /*package*/ boolean isCheckedDatabase() {\n        return checkedDatabase;\n    }\n\n    /*package*/ void setCheckedDatabase(boolean checkedDatabase) {\n        this.checkedDatabase = checkedDatabase;\n    }\n\n    @Override\n    public String toString() {\n        return name;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/db/table/TableUtils.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.db.table;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.db.annotation.Column;\nimport org.xutils.db.converter.ColumnConverterFactory;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\n\n/* package */ final class TableUtils {\n\n    private TableUtils() {\n    }\n\n    /* package */\n    static synchronized LinkedHashMap<String, ColumnEntity> findColumnMap(Class<?> entityType) {\n        LinkedHashMap<String, ColumnEntity> columnMap = new LinkedHashMap<String, ColumnEntity>();\n        addColumns2Map(entityType, columnMap);\n        return columnMap;\n    }\n\n    private static void addColumns2Map(Class<?> entityType, HashMap<String, ColumnEntity> columnMap) {\n        if (Object.class.equals(entityType)) return;\n\n        try {\n            Field[] fields = entityType.getDeclaredFields();\n            for (Field field : fields) {\n                int modify = field.getModifiers();\n                if (Modifier.isStatic(modify) || Modifier.isTransient(modify)) {\n                    continue;\n                }\n                Column columnAnn = field.getAnnotation(Column.class);\n                if (columnAnn != null) {\n                    if (ColumnConverterFactory.isSupportColumnConverter(field.getType())) {\n                        ColumnEntity column = new ColumnEntity(entityType, field, columnAnn);\n                        if (!columnMap.containsKey(column.getName())) {\n                            columnMap.put(column.getName(), column);\n                        }\n                    }\n                }\n            }\n\n            addColumns2Map(entityType.getSuperclass(), columnMap);\n        } catch (Throwable e) {\n            LogUtil.e(e.getMessage(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/ex/BaseException.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.ex;\n\nimport java.io.IOException;\n\n/**\n * Author: wyouflf\n * Date: 13-7-24\n * Time: 下午3:00\n */\npublic class BaseException extends IOException {\n    private static final long serialVersionUID = 1L;\n\n    public BaseException() {\n        super();\n    }\n\n    public BaseException(String detailMessage) {\n        super(detailMessage);\n    }\n\n    public BaseException(String detailMessage, Throwable throwable) {\n        super(detailMessage);\n        this.initCause(throwable);\n    }\n\n    public BaseException(Throwable throwable) {\n        super(throwable.getMessage());\n        this.initCause(throwable);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/ex/DbException.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.ex;\n\npublic class DbException extends BaseException {\n    private static final long serialVersionUID = 1L;\n\n    public DbException() {\n    }\n\n    public DbException(String detailMessage) {\n        super(detailMessage);\n    }\n\n    public DbException(String detailMessage, Throwable throwable) {\n        super(detailMessage, throwable);\n    }\n\n    public DbException(Throwable throwable) {\n        super(throwable);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/ex/FileLockedException.java",
    "content": "package org.xutils.ex;\n\n/**\n * Created by wyouflf on 15/10/9.\n */\npublic class FileLockedException extends BaseException {\n    private static final long serialVersionUID = 1L;\n\n    public FileLockedException(String detailMessage) {\n        super(detailMessage);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/ex/HttpException.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.ex;\n\nimport android.text.TextUtils;\n\npublic class HttpException extends BaseException {\n    private static final long serialVersionUID = 1L;\n\n    private int code;\n    private String errorCode;\n    private String customMessage;\n    private String result;\n\n    /**\n     * @param code          The http response status code, 0 if the http request error and has no response.\n     * @param detailMessage The http response message.\n     */\n    public HttpException(int code, String detailMessage) {\n        super(detailMessage);\n        this.code = code;\n    }\n\n    public void setCode(int code) {\n        this.code = code;\n    }\n\n    public void setMessage(String message) {\n        this.customMessage = message;\n    }\n\n    /**\n     * @return The http response status code, 0 if the http request error and has no response.\n     */\n    public int getCode() {\n        return code;\n    }\n\n    public String getErrorCode() {\n        return errorCode == null ? String.valueOf(code) : errorCode;\n    }\n\n    public void setErrorCode(String errorCode) {\n        this.errorCode = errorCode;\n    }\n\n    @Override\n    public String getMessage() {\n        if (!TextUtils.isEmpty(customMessage)) {\n            return customMessage;\n        } else {\n            return super.getMessage();\n        }\n    }\n\n    public String getResult() {\n        return result;\n    }\n\n    public void setResult(String result) {\n        this.result = result;\n    }\n\n    @Override\n    public String toString() {\n        return \"errorCode: \" + getErrorCode() + \", msg: \" + getMessage() + \", result: \" + result;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/ex/HttpRedirectException.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.ex;\n\npublic class HttpRedirectException extends HttpException {\n    private static final long serialVersionUID = 1L;\n\n    public HttpRedirectException(int code, String detailMessage, String result) {\n        super(code, detailMessage);\n        this.setResult(result);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/BaseParams.java",
    "content": "package org.xutils.http;\n\nimport android.text.TextUtils;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.body.BodyItemWrapper;\nimport org.xutils.http.body.FileBody;\nimport org.xutils.http.body.InputStreamBody;\nimport org.xutils.http.body.MultipartBody;\nimport org.xutils.http.body.RequestBody;\nimport org.xutils.http.body.StringBody;\nimport org.xutils.http.body.UrlEncodedParamsBody;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Array;\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 请求的基础参数\n * Created by wyouflf on 16/1/23.\n */\n/*package*/ abstract class BaseParams {\n\n    private String charset = \"UTF-8\";\n    private HttpMethod method;\n    private String bodyContent;\n    private boolean multipart = false; // 是否强制使用multipart表单\n    private boolean asJsonContent = false; // 用json形式的bodyParams上传\n    private RequestBody requestBody; // 生成的表单\n\n    private final List<Header> headers = new ArrayList<Header>();\n    private final List<KeyValue> queryStringParams = new ArrayList<KeyValue>();\n    private final List<KeyValue> bodyParams = new ArrayList<KeyValue>();\n    private final List<KeyValue> fileParams = new ArrayList<KeyValue>();\n\n    public void setCharset(String charset) {\n        if (!TextUtils.isEmpty(charset)) {\n            this.charset = charset;\n        }\n    }\n\n    public String getCharset() {\n        return charset;\n    }\n\n    public void setMethod(HttpMethod method) {\n        this.method = method;\n    }\n\n    public HttpMethod getMethod() {\n        return method;\n    }\n\n    public boolean isMultipart() {\n        return multipart;\n    }\n\n    public void setMultipart(boolean multipart) {\n        this.multipart = multipart;\n    }\n\n    /**\n     * 以json形式提交body参数\n     *\n     * @return\n     */\n    public boolean isAsJsonContent() {\n        return asJsonContent;\n    }\n\n    /**\n     * 以json形式提交body参数\n     *\n     * @param asJsonContent\n     */\n    public void setAsJsonContent(boolean asJsonContent) {\n        this.asJsonContent = asJsonContent;\n    }\n\n    /**\n     * 覆盖header\n     *\n     * @param name\n     * @param value\n     */\n    public void setHeader(String name, String value) {\n        Header header = new Header(name, value, true);\n        Iterator<Header> it = headers.iterator();\n        while (it.hasNext()) {\n            KeyValue kv = it.next();\n            if (name.equals(kv.key)) {\n                it.remove();\n            }\n        }\n        this.headers.add(header);\n    }\n\n    /**\n     * 添加header\n     *\n     * @param name\n     * @param value\n     */\n    public void addHeader(String name, String value) {\n        this.headers.add(new Header(name, value, false));\n    }\n\n    /**\n     * 添加请求参数(根据请求谓词, 将参数加入QueryString或Body.)\n     *\n     * @param name  参数名\n     * @param value 可以是String, File, InputStream 或 byte[]\n     */\n    public void addParameter(String name, Object value) {\n        if (value == null) return;\n\n        if (method == null || HttpMethod.permitsRequestBody(method)) {\n            if (!TextUtils.isEmpty(name)) {\n                if (value instanceof File\n                        || value instanceof InputStream\n                        || value instanceof byte[]) {\n                    this.fileParams.add(new KeyValue(name, value));\n                } else {\n                    if (value instanceof List) {\n                        for (Object item : (List) value) {\n                            this.bodyParams.add(new ArrayItem(name, item));\n                        }\n                    } else if (value instanceof JSONArray) {\n                        JSONArray array = (JSONArray) value;\n                        int len = array.length();\n                        for (int i = 0; i < len; i++) {\n                            this.bodyParams.add(new ArrayItem(name, array.opt(i)));\n                        }\n                    } else if (value.getClass().isArray()) {\n                        int len = Array.getLength(value);\n                        for (int i = 0; i < len; i++) {\n                            this.bodyParams.add(new ArrayItem(name, Array.get(value, i)));\n                        }\n                    } else {\n                        this.bodyParams.add(new KeyValue(name, value));\n                    }\n                }\n            } else {\n                this.bodyContent = value.toString();\n            }\n        } else {\n            if (!TextUtils.isEmpty(name)) {\n                if (value instanceof List) {\n                    for (Object item : (List) value) {\n                        this.queryStringParams.add(new ArrayItem(name, item));\n                    }\n                } else if (value.getClass().isArray()) {\n                    int len = Array.getLength(value);\n                    for (int i = 0; i < len; i++) {\n                        this.queryStringParams.add(new ArrayItem(name, Array.get(value, i)));\n                    }\n                } else {\n                    this.queryStringParams.add(new KeyValue(name, value));\n                }\n            }\n        }\n    }\n\n    /**\n     * 添加参数至Query String\n     *\n     * @param name\n     * @param value\n     */\n    public void addQueryStringParameter(String name, String value) {\n        if (!TextUtils.isEmpty(name)) {\n            this.queryStringParams.add(new KeyValue(name, value));\n        }\n    }\n\n    /**\n     * 添加参数至Body\n     *\n     * @param name\n     * @param value\n     */\n    public void addBodyParameter(String name, String value) {\n        if (!TextUtils.isEmpty(name)) {\n            this.bodyParams.add(new KeyValue(name, value));\n        } else {\n            this.bodyContent = value;\n        }\n    }\n\n    /**\n     * 添加body参数\n     */\n    public void addBodyParameter(String name, File value) {\n        addBodyParameter(name, value, null, null);\n    }\n\n    /**\n     * 添加body参数\n     *\n     * @param name        参数名\n     * @param value       可以是String, File, InputStream 或 byte[]\n     * @param contentType 可为null\n     */\n    public void addBodyParameter(String name, Object value, String contentType) {\n        addBodyParameter(name, value, contentType, null);\n    }\n\n    /**\n     * 添加body参数\n     *\n     * @param name        参数名\n     * @param value       可以是String, File, InputStream 或 byte[]\n     * @param contentType 可为null\n     * @param fileName    服务端看到的文件名\n     */\n    public void addBodyParameter(String name, Object value, String contentType, String fileName) {\n        if (TextUtils.isEmpty(contentType) && TextUtils.isEmpty(fileName)) {\n            this.fileParams.add(new KeyValue(name, value));\n        } else {\n            this.fileParams.add(new KeyValue(name, new BodyItemWrapper(value, contentType, fileName)));\n        }\n    }\n\n    public void setBodyContent(String content) {\n        this.bodyContent = content;\n    }\n\n    public String getBodyContent() {\n        checkBodyParams();\n        return bodyContent;\n    }\n\n    public List<Header> getHeaders() {\n        return new ArrayList<Header>(headers);\n    }\n\n    public List<KeyValue> getQueryStringParams() {\n        checkBodyParams();\n        return new ArrayList<KeyValue>(queryStringParams);\n    }\n\n    public List<KeyValue> getBodyParams() {\n        checkBodyParams();\n        return new ArrayList<KeyValue>(bodyParams);\n    }\n\n    public List<KeyValue> getFileParams() {\n        checkBodyParams();\n        return new ArrayList<KeyValue>(fileParams);\n    }\n\n    public List<KeyValue> getStringParams() {\n        List<KeyValue> result = new ArrayList<KeyValue>(\n                queryStringParams.size() + bodyParams.size());\n        result.addAll(queryStringParams);\n        result.addAll(bodyParams);\n        return result;\n    }\n\n    public String getStringParameter(String name) {\n        for (KeyValue kv : queryStringParams) {\n            if (name == null && kv.key == null) {\n                return kv.getValueStr();\n            } else if (name != null && name.equals(kv.key)) {\n                return kv.getValueStr();\n            }\n        }\n        for (KeyValue kv : bodyParams) {\n            if (name == null && kv.key == null) {\n                return kv.getValueStr();\n            } else if (name != null && name.equals(kv.key)) {\n                return kv.getValueStr();\n            }\n        }\n        return null;\n    }\n\n    public List<KeyValue> getParams(String name) {\n        List<KeyValue> result = new ArrayList<KeyValue>();\n        for (KeyValue kv : queryStringParams) {\n            if (name == null && kv.key == null) {\n                result.add(kv);\n            } else if (name != null && name.equals(kv.key)) {\n                result.add(kv);\n            }\n        }\n        for (KeyValue kv : bodyParams) {\n            if (name == null && kv.key == null) {\n                result.add(kv);\n            } else if (name != null && name.equals(kv.key)) {\n                result.add(kv);\n            }\n        }\n        for (KeyValue kv : fileParams) {\n            if (name == null && kv.key == null) {\n                result.add(kv);\n            } else if (name != null && name.equals(kv.key)) {\n                result.add(kv);\n            }\n        }\n        return result;\n    }\n\n    public void clearParams() {\n        queryStringParams.clear();\n        bodyParams.clear();\n        fileParams.clear();\n        bodyContent = null;\n        requestBody = null;\n    }\n\n    public void removeParameter(String name) {\n        if (!TextUtils.isEmpty(name)) {\n            Iterator<KeyValue> it = queryStringParams.iterator();\n            while (it.hasNext()) {\n                KeyValue kv = it.next();\n                if (name.equals(kv.key)) {\n                    it.remove();\n                }\n            }\n\n            it = bodyParams.iterator();\n            while (it.hasNext()) {\n                KeyValue kv = it.next();\n                if (name.equals(kv.key)) {\n                    it.remove();\n                }\n            }\n\n            it = fileParams.iterator();\n            while (it.hasNext()) {\n                KeyValue kv = it.next();\n                if (name.equals(kv.key)) {\n                    it.remove();\n                }\n            }\n        } else {\n            bodyContent = null;\n        }\n    }\n\n    public void setRequestBody(RequestBody requestBody) {\n        this.requestBody = requestBody;\n    }\n\n    public RequestBody getRequestBody() throws IOException {\n        checkBodyParams();\n        if (this.requestBody != null) {\n            return this.requestBody;\n        }\n        RequestBody result = null;\n        if (!TextUtils.isEmpty(bodyContent)) {\n            result = new StringBody(bodyContent, charset);\n        } else if (multipart || fileParams.size() > 0) {\n            if (!multipart && fileParams.size() == 1) {\n                for (KeyValue kv : fileParams) {\n                    String contentType = null;\n                    Object value = kv.value;\n                    if (value instanceof BodyItemWrapper) {\n                        BodyItemWrapper wrapper = (BodyItemWrapper) value;\n                        value = wrapper.getValue();\n                        contentType = wrapper.getContentType();\n                    }\n                    if (value instanceof File) {\n                        result = new FileBody((File) value, contentType);\n                    } else if (value instanceof InputStream) {\n                        result = new InputStreamBody((InputStream) value, contentType);\n                    } else if (value instanceof byte[]) {\n                        result = new InputStreamBody(new ByteArrayInputStream((byte[]) value), contentType);\n                    } else if (value instanceof String) {\n                        // invoke addBodyParameter(key, stringValue, contentType)\n                        result = new StringBody((String) value, charset);\n                        result.setContentType(contentType);\n                    } else {\n                        LogUtil.w(\"Some params will be ignored for: \" + this.toString());\n                    }\n                    break;\n                }\n            } else {\n                multipart = true;\n                result = new MultipartBody(fileParams, charset);\n            }\n        } else if (bodyParams.size() > 0) {\n            result = new UrlEncodedParamsBody(bodyParams, charset);\n        }\n\n        return result;\n    }\n\n    public String toJSONString() {\n        List<KeyValue> list = new ArrayList<KeyValue>(queryStringParams.size() + bodyParams.size());\n        list.addAll(queryStringParams);\n        list.addAll(bodyParams);\n        try {\n            JSONObject jsonObject = null;\n            if (!TextUtils.isEmpty(bodyContent)) {\n                jsonObject = new JSONObject(bodyContent);\n            } else {\n                jsonObject = new JSONObject();\n            }\n            params2Json(jsonObject, list);\n            return jsonObject.toString();\n        } catch (JSONException ex) {\n            throw new RuntimeException(ex);\n        }\n    }\n\n    @Override\n    public String toString() {\n        checkBodyParams();\n        final StringBuilder sb = new StringBuilder();\n        if (!queryStringParams.isEmpty()) {\n            for (KeyValue kv : queryStringParams) {\n                sb.append(kv.key).append(\"=\").append(kv.value).append(\"&\");\n            }\n            sb.deleteCharAt(sb.length() - 1);\n        }\n\n        if (HttpMethod.permitsRequestBody(this.method)) {\n            sb.append(\"<\");\n            if (!TextUtils.isEmpty(bodyContent)) {\n                sb.append(bodyContent);\n            } else {\n                if (!bodyParams.isEmpty()) {\n                    for (KeyValue kv : bodyParams) {\n                        sb.append(kv.key).append(\"=\").append(kv.value).append(\"&\");\n                    }\n                    sb.deleteCharAt(sb.length() - 1);\n                }\n            }\n            sb.append(\">\");\n        }\n        return sb.toString();\n    }\n\n    private void checkBodyParams() {\n        if (bodyParams.isEmpty()) return;\n\n        if (!HttpMethod.permitsRequestBody(method)\n                || !TextUtils.isEmpty(bodyContent)\n                || requestBody != null) {\n            queryStringParams.addAll(bodyParams);\n            bodyParams.clear();\n        }\n\n        if (!bodyParams.isEmpty() && (multipart || fileParams.size() > 0)) {\n            fileParams.addAll(bodyParams);\n            bodyParams.clear();\n        }\n\n        if (asJsonContent && !bodyParams.isEmpty()) {\n            try {\n                JSONObject jsonObject = null;\n                if (!TextUtils.isEmpty(bodyContent)) {\n                    jsonObject = new JSONObject(bodyContent);\n                } else {\n                    jsonObject = new JSONObject();\n                }\n                params2Json(jsonObject, bodyParams);\n                bodyContent = jsonObject.toString();\n                bodyParams.clear();\n            } catch (JSONException ex) {\n                throw new RuntimeException(ex);\n            }\n        }\n    }\n\n    private void params2Json(final JSONObject jsonObject, final List<KeyValue> paramList) throws JSONException {\n        HashSet<String> arraySet = new HashSet<String>(paramList.size());\n        LinkedHashMap<String, JSONArray> tempData = new LinkedHashMap<String, JSONArray>(paramList.size());\n        for (int i = 0; i < paramList.size(); i++) {\n            KeyValue kv = paramList.get(i);\n            final String key = kv.key;\n            if (TextUtils.isEmpty(key)) continue;\n\n            JSONArray ja = null;\n            if (tempData.containsKey(key)) {\n                ja = tempData.get(key);\n            } else {\n                ja = new JSONArray();\n                tempData.put(key, ja);\n            }\n\n            ja.put(RequestParamsHelper.parseJSONObject(kv.value));\n\n            if (kv instanceof ArrayItem) {\n                arraySet.add(key);\n            }\n        }\n\n        for (Map.Entry<String, JSONArray> entry : tempData.entrySet()) {\n            String key = entry.getKey();\n            JSONArray ja = entry.getValue();\n            if (ja.length() > 1 || arraySet.contains(key)) {\n                jsonObject.put(key, ja);\n            } else {\n                Object value = ja.get(0);\n                jsonObject.put(key, value);\n            }\n        }\n    }\n\n    public final static class ArrayItem extends KeyValue {\n        public ArrayItem(String key, Object value) {\n            super(key, value);\n        }\n    }\n\n    public final static class Header extends KeyValue {\n\n        public final boolean setHeader;\n\n        public Header(String key, String value, boolean setHeader) {\n            super(key, value);\n            this.setHeader = setHeader;\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/HttpManagerImpl.java",
    "content": "package org.xutils.http;\n\nimport org.xutils.HttpManager;\nimport org.xutils.common.Callback;\nimport org.xutils.x;\n\nimport java.lang.reflect.Type;\n\n/**\n * Created by wyouflf on 15/7/23.\n * HttpManager实现\n */\npublic final class HttpManagerImpl implements HttpManager {\n\n    private static final Object lock = new Object();\n    private static volatile HttpManagerImpl instance;\n\n    private HttpManagerImpl() {\n    }\n\n    public static void registerInstance() {\n        if (instance == null) {\n            synchronized (lock) {\n                if (instance == null) {\n                    instance = new HttpManagerImpl();\n                }\n            }\n        }\n        x.Ext.setHttpManager(instance);\n    }\n\n    @Override\n    public <T> Callback.Cancelable get(RequestParams entity, Callback.CommonCallback<T> callback) {\n        return request(HttpMethod.GET, entity, callback);\n    }\n\n    @Override\n    public <T> Callback.Cancelable post(RequestParams entity, Callback.CommonCallback<T> callback) {\n        return request(HttpMethod.POST, entity, callback);\n    }\n\n    @Override\n    public <T> Callback.Cancelable request(HttpMethod method, RequestParams entity, Callback.CommonCallback<T> callback) {\n        entity.setMethod(method);\n        Callback.Cancelable cancelable = null;\n        if (callback instanceof Callback.Cancelable) {\n            cancelable = (Callback.Cancelable) callback;\n        }\n        HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);\n        return x.task().start(task);\n    }\n\n    @Override\n    public <T> T getSync(RequestParams entity, Class<T> resultType) throws Throwable {\n        return requestSync(HttpMethod.GET, entity, resultType);\n    }\n\n    @Override\n    public <T> T postSync(RequestParams entity, Class<T> resultType) throws Throwable {\n        return requestSync(HttpMethod.POST, entity, resultType);\n    }\n\n    @Override\n    public <T> T requestSync(HttpMethod method, RequestParams entity, Class<T> resultType) throws Throwable {\n        DefaultSyncCallback<T> callback = new DefaultSyncCallback<T>(resultType);\n        return requestSync(method, entity, callback);\n    }\n\n    @Override\n    public <T> T requestSync(HttpMethod method, RequestParams entity, Callback.TypedCallback<T> callback) throws Throwable {\n        entity.setMethod(method);\n        HttpTask<T> task = new HttpTask<T>(entity, null, callback);\n        return x.task().startSync(task);\n    }\n\n    private class DefaultSyncCallback<T> implements Callback.TypedCallback<T> {\n\n        private final Class<T> resultType;\n\n        public DefaultSyncCallback(Class<T> resultType) {\n            this.resultType = resultType;\n        }\n\n        @Override\n        public Type getLoadType() {\n            return resultType;\n        }\n\n        @Override\n        public void onSuccess(T result) {\n\n        }\n\n        @Override\n        public void onError(Throwable ex, boolean isOnCallback) {\n\n        }\n\n        @Override\n        public void onCancelled(CancelledException cex) {\n\n        }\n\n        @Override\n        public void onFinished() {\n\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/HttpMethod.java",
    "content": "package org.xutils.http;\n\n/**\n * Created by wyouflf on 15/8/4.\n * HTTP谓词枚举\n */\npublic enum HttpMethod {\n    GET(\"GET\"),\n    POST(\"POST\"),\n    PUT(\"PUT\"),\n    PATCH(\"PATCH\"),\n    HEAD(\"HEAD\"),\n    MOVE(\"MOVE\"),\n    COPY(\"COPY\"),\n    DELETE(\"DELETE\"),\n    OPTIONS(\"OPTIONS\"),\n    TRACE(\"TRACE\"),\n    CONNECT(\"CONNECT\");\n\n    private final String value;\n\n    HttpMethod(String value) {\n        this.value = value;\n    }\n\n    @Override\n    public String toString() {\n        return this.value;\n    }\n\n    public static boolean permitsRetry(HttpMethod method) {\n        return method == GET;\n    }\n\n    public static boolean permitsCache(HttpMethod method) {\n        return method == GET || method == POST;\n    }\n\n    public static boolean permitsRequestBody(HttpMethod method) {\n        return method == POST\n                || method == PUT\n                || method == PATCH\n                || method == DELETE;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/HttpTask.java",
    "content": "package org.xutils.http;\n\nimport android.text.TextUtils;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.task.AbsTask;\nimport org.xutils.common.task.Priority;\nimport org.xutils.common.task.PriorityExecutor;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.common.util.ParameterizedTypeUtil;\nimport org.xutils.ex.HttpException;\nimport org.xutils.ex.HttpRedirectException;\nimport org.xutils.http.app.HttpRetryHandler;\nimport org.xutils.http.app.RedirectHandler;\nimport org.xutils.http.app.RequestInterceptListener;\nimport org.xutils.http.app.RequestTracker;\nimport org.xutils.http.request.UriRequest;\nimport org.xutils.http.request.UriRequestFactory;\nimport org.xutils.x;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Created by wyouflf on 15/7/23.\n * http 请求任务\n */\npublic class HttpTask<ResultType> extends AbsTask<ResultType> implements ProgressHandler {\n\n    // 请求相关\n    private RequestParams params;\n    private UriRequest request;\n    private RequestWorker requestWorker;\n    private final Executor executor;\n    private volatile boolean hasException = false;\n    private final Callback.CommonCallback<ResultType> callback;\n\n    // 缓存控制\n    private Object rawResult = null;\n    private volatile Boolean trustCache = null;\n    private final Object cacheLock = new Object();\n\n    // 扩展callback\n    private Callback.CacheCallback<ResultType> cacheCallback;\n    private Callback.PrepareCallback prepareCallback;\n    private Callback.ProgressCallback progressCallback;\n    private RequestInterceptListener requestInterceptListener;\n\n    // 日志追踪\n    private RequestTracker tracker;\n\n    // 文件下载线程数限制\n    private Type loadType;\n    private final static int MAX_FILE_LOAD_WORKER = 3;\n    private final static AtomicInteger sCurrFileLoadCount = new AtomicInteger(0);\n\n    // 文件下载任务\n    private static final HashMap<String, WeakReference<HttpTask<?>>>\n            DOWNLOAD_TASK = new HashMap<String, WeakReference<HttpTask<?>>>(1);\n\n    private static final PriorityExecutor HTTP_EXECUTOR = new PriorityExecutor(5, true);\n    private static final PriorityExecutor CACHE_EXECUTOR = new PriorityExecutor(5, true);\n\n\n    public HttpTask(RequestParams params, Callback.Cancelable cancelHandler,\n                    Callback.CommonCallback<ResultType> callback) {\n        super(cancelHandler);\n\n        assert params != null;\n        assert callback != null;\n\n        // set params & callback\n        this.params = params;\n        this.callback = callback;\n        if (callback instanceof Callback.CacheCallback) {\n            this.cacheCallback = (Callback.CacheCallback<ResultType>) callback;\n        }\n        if (callback instanceof Callback.PrepareCallback) {\n            this.prepareCallback = (Callback.PrepareCallback) callback;\n        }\n        if (callback instanceof Callback.ProgressCallback) {\n            this.progressCallback = (Callback.ProgressCallback<ResultType>) callback;\n        }\n        if (callback instanceof RequestInterceptListener) {\n            this.requestInterceptListener = (RequestInterceptListener) callback;\n        }\n\n        // init tracker\n        {\n            RequestTracker customTracker = params.getRequestTracker();\n            if (customTracker == null) {\n                if (callback instanceof RequestTracker) {\n                    customTracker = (RequestTracker) callback;\n                } else {\n                    customTracker = UriRequestFactory.getDefaultTracker();\n                }\n            }\n            if (customTracker != null) {\n                tracker = new RequestTrackerWrapper(customTracker);\n            }\n        }\n\n        // init executor\n        if (params.getExecutor() != null) {\n            this.executor = params.getExecutor();\n        } else {\n            if (cacheCallback != null) {\n                this.executor = CACHE_EXECUTOR;\n            } else {\n                this.executor = HTTP_EXECUTOR;\n            }\n        }\n    }\n\n    // 解析loadType\n    private void resolveLoadType() {\n        Class<?> callBackType = callback.getClass();\n        if (callback instanceof Callback.TypedCallback) {\n            loadType = ((Callback.TypedCallback) callback).getLoadType();\n        } else if (callback instanceof Callback.PrepareCallback) {\n            loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.PrepareCallback.class, 0);\n        } else {\n            loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.CommonCallback.class, 0);\n        }\n    }\n\n    // 初始化请求参数\n    private UriRequest createNewRequest() throws Throwable {\n        // init request\n        params.init();\n        UriRequest result = UriRequestFactory.getUriRequest(params, loadType);\n        result.setCallingClassLoader(callback.getClass().getClassLoader());\n        result.setProgressHandler(this);\n        this.loadingUpdateMaxTimeSpan = params.getLoadingUpdateMaxTimeSpan();\n        this.update(FLAG_REQUEST_CREATED, result);\n        return result;\n    }\n\n    // 文件下载冲突检测\n    private void checkDownloadTask() {\n        if (File.class == loadType) {\n            synchronized (DOWNLOAD_TASK) {\n                String downloadTaskKey = this.params.getSaveFilePath();\n                /*{\n                    // 不处理缓存文件下载冲突,\n                    // 缓存文件下载冲突会抛出FileLockedException异常,\n                    // 使用异常处理控制是否重新尝试下载.\n                    if (TextUtils.isEmpty(downloadTaskKey)) {\n                        downloadTaskKey = this.request.getCacheKey();\n                    }\n                }*/\n                if (!TextUtils.isEmpty(downloadTaskKey)) {\n                    WeakReference<HttpTask<?>> taskRef = DOWNLOAD_TASK.get(downloadTaskKey);\n                    if (taskRef != null) {\n                        HttpTask<?> task = taskRef.get();\n                        if (task != null) {\n                            task.cancel();\n                            task.closeRequestSync();\n                        }\n                        DOWNLOAD_TASK.remove(downloadTaskKey);\n                    }\n                    DOWNLOAD_TASK.put(downloadTaskKey, new WeakReference<HttpTask<?>>(this));\n                } // end if (!TextUtils.isEmpty(downloadTaskKey))\n\n                if (DOWNLOAD_TASK.size() > MAX_FILE_LOAD_WORKER) {\n                    Iterator<Map.Entry<String, WeakReference<HttpTask<?>>>>\n                            entryItr = DOWNLOAD_TASK.entrySet().iterator();\n                    while (entryItr.hasNext()) {\n                        Map.Entry<String, WeakReference<HttpTask<?>>> next = entryItr.next();\n                        WeakReference<HttpTask<?>> value = next.getValue();\n                        if (value == null || value.get() == null) {\n                            entryItr.remove();\n                        }\n                    }\n                }\n            } // end synchronized\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    protected ResultType doBackground() throws Throwable {\n\n        if (this.isCancelled()) {\n            throw new Callback.CancelledException(\"cancelled before request\");\n        }\n\n        // 初始化请求参数\n        ResultType result = null;\n        resolveLoadType();\n        request = createNewRequest();\n        checkDownloadTask();\n        // retry 初始化\n        boolean retry = true;\n        int retryCount = 0;\n        Throwable exception = null;\n        HttpRetryHandler retryHandler = this.params.getHttpRetryHandler();\n        if (retryHandler == null) {\n            retryHandler = new HttpRetryHandler();\n        }\n        retryHandler.setMaxRetryCount(this.params.getMaxRetryCount());\n\n        if (this.isCancelled()) {\n            throw new Callback.CancelledException(\"cancelled before request\");\n        }\n\n        // 检查缓存\n        Object cacheResult = null;\n        if (cacheCallback != null && HttpMethod.permitsCache(params.getMethod())) {\n            // 尝试从缓存获取结果, 并为请求头加入缓存控制参数.\n            try {\n                clearRawResult();\n                LogUtil.d(\"load cache: \" + this.request.getRequestUri());\n                rawResult = this.request.loadResultFromCache();\n            } catch (Throwable ex) {\n                LogUtil.w(\"load disk cache error\", ex);\n            }\n\n            if (this.isCancelled()) {\n                clearRawResult();\n                throw new Callback.CancelledException(\"cancelled before request\");\n            }\n\n            if (rawResult != null) {\n                if (prepareCallback != null) {\n                    try {\n                        cacheResult = prepareCallback.prepare(rawResult);\n                    } catch (Throwable ex) {\n                        cacheResult = null;\n                        LogUtil.w(\"prepare disk cache error\", ex);\n                    } finally {\n                        clearRawResult();\n                    }\n                } else {\n                    cacheResult = rawResult;\n                }\n\n                if (this.isCancelled()) {\n                    throw new Callback.CancelledException(\"cancelled before request\");\n                }\n\n                if (cacheResult != null) {\n                    // 同步等待是否信任缓存\n                    this.update(FLAG_CACHE, cacheResult);\n                    while (trustCache == null) {\n                        synchronized (cacheLock) {\n                            try {\n                                cacheLock.wait();\n                            } catch (InterruptedException iex) {\n                                throw new Callback.CancelledException(\"cancelled before request\");\n                            } catch (Throwable ignored) {\n                            }\n                        }\n                    }\n\n                    // 处理完成\n                    if (trustCache) {\n                        return null;\n                    }\n                }\n            }\n        }\n\n        if (trustCache == null) {\n            trustCache = false;\n        }\n\n        if (cacheResult == null) {\n            this.request.clearCacheHeader();\n        }\n\n        // 判断请求的缓存策略\n        if (callback instanceof Callback.ProxyCacheCallback) {\n            if (((Callback.ProxyCacheCallback) callback).onlyCache()) {\n                return null;\n            }\n        }\n\n        // 发起请求\n        retry = true;\n        while (retry) {\n            retry = false;\n\n            try {\n                if (this.isCancelled()) {\n                    throw new Callback.CancelledException(\"cancelled before request\");\n                }\n\n                // 由loader发起请求, 拿到结果.\n                this.request.close(); // retry 前关闭上次请求\n\n                try {\n                    clearRawResult();\n                    // 开始请求工作\n                    LogUtil.d(\"load: \" + this.request.getRequestUri());\n                    requestWorker = new RequestWorker();\n                    requestWorker.request();\n                    if (requestWorker.ex != null) {\n                        throw requestWorker.ex;\n                    }\n                    rawResult = requestWorker.result;\n                } catch (Throwable ex) {\n                    clearRawResult();\n                    if (this.isCancelled()) {\n                        throw new Callback.CancelledException(\"cancelled during request\");\n                    } else {\n                        throw ex;\n                    }\n                }\n\n                if (prepareCallback != null) {\n\n                    if (this.isCancelled()) {\n                        throw new Callback.CancelledException(\"cancelled before request\");\n                    }\n\n                    try {\n                        result = (ResultType) prepareCallback.prepare(rawResult);\n                    } finally {\n                        clearRawResult();\n                    }\n                } else {\n                    result = (ResultType) rawResult;\n                }\n\n                // 保存缓存\n                if (cacheCallback != null && HttpMethod.permitsCache(params.getMethod())) {\n                    this.request.save2Cache();\n                }\n\n                if (this.isCancelled()) {\n                    throw new Callback.CancelledException(\"cancelled after request\");\n                }\n            } catch (HttpRedirectException redirectEx) {\n                retry = true;\n                LogUtil.w(\"Http Redirect:\" + params.getUri());\n            } catch (Throwable ex) {\n                switch (this.request.getResponseCode()) {\n                    case 204: // empty content\n                    case 205: // empty content\n                    case 304: // disk cache is valid.\n                        return null;\n                    default: {\n                        exception = ex;\n                        if (this.isCancelled() && !(exception instanceof Callback.CancelledException)) {\n                            exception = new Callback.CancelledException(\"canceled by user\");\n                        }\n                        retry = retryHandler.canRetry(this.request, exception, ++retryCount);\n                    }\n                }\n            }\n\n        }\n\n        if (exception != null && result == null && !trustCache) {\n            hasException = true;\n            throw exception;\n        }\n\n        return result;\n    }\n\n    private static final int FLAG_REQUEST_CREATED = 1;\n    private static final int FLAG_CACHE = 2;\n    private static final int FLAG_PROGRESS = 3;\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    protected void onUpdate(int flag, Object... args) {\n        switch (flag) {\n            case FLAG_REQUEST_CREATED: {\n                if (this.tracker != null) {\n                    this.tracker.onRequestCreated((UriRequest) args[0]);\n                }\n                break;\n            }\n            case FLAG_CACHE: {\n                synchronized (cacheLock) {\n                    try {\n                        ResultType result = (ResultType) args[0];\n                        if (tracker != null) {\n                            tracker.onCache(request, result);\n                        }\n                        trustCache = this.cacheCallback.onCache(result);\n                    } catch (Throwable ex) {\n                        trustCache = false;\n                        callback.onError(ex, true);\n                    } finally {\n                        cacheLock.notifyAll();\n                    }\n                }\n                break;\n            }\n            case FLAG_PROGRESS: {\n                if (this.progressCallback != null && args.length == 3) {\n                    try {\n                        this.progressCallback.onLoading(\n                                ((Number) args[0]).longValue(),\n                                ((Number) args[1]).longValue(),\n                                (Boolean) args[2]);\n                    } catch (Throwable ex) {\n                        callback.onError(ex, true);\n                    }\n                }\n                break;\n            }\n            default: {\n                break;\n            }\n        }\n    }\n\n    @Override\n    protected void onWaiting() {\n        if (tracker != null) {\n            tracker.onWaiting(params);\n        }\n        if (progressCallback != null) {\n            progressCallback.onWaiting();\n        }\n    }\n\n    @Override\n    protected void onStarted() {\n        if (tracker != null) {\n            tracker.onStart(params);\n        }\n        if (progressCallback != null) {\n            progressCallback.onStarted();\n        }\n    }\n\n    @Override\n    protected void onSuccess(ResultType result) {\n        if (hasException) return;\n        if (tracker != null) {\n            tracker.onSuccess(request, result);\n        }\n        callback.onSuccess(result);\n    }\n\n    @Override\n    protected void onError(Throwable ex, boolean isCallbackError) {\n        if (tracker != null) {\n            tracker.onError(request, ex, isCallbackError);\n        }\n        callback.onError(ex, isCallbackError);\n    }\n\n\n    @Override\n    protected void onCancelled(Callback.CancelledException cex) {\n        if (tracker != null) {\n            tracker.onCancelled(request);\n        }\n        callback.onCancelled(cex);\n    }\n\n    @Override\n    protected void onFinished() {\n        if (tracker != null) {\n            tracker.onFinished(request);\n        }\n        x.task().run(new Runnable() {\n            @Override\n            public void run() {\n                closeRequestSync();\n            }\n        });\n        callback.onFinished();\n    }\n\n    private void clearRawResult() {\n        if (rawResult instanceof Closeable) {\n            IOUtil.closeQuietly((Closeable) rawResult);\n        }\n        rawResult = null;\n    }\n\n    @Override\n    protected void cancelWorks() {\n        x.task().run(new Runnable() {\n            @Override\n            public void run() {\n                closeRequestSync();\n            }\n        });\n    }\n\n    @Override\n    protected boolean isCancelFast() {\n        return params.isCancelFast();\n    }\n\n    private void closeRequestSync() {\n        clearRawResult();\n        IOUtil.closeQuietly(request);\n    }\n\n    @Override\n    public Executor getExecutor() {\n        return this.executor;\n    }\n\n    @Override\n    public Priority getPriority() {\n        return params.getPriority();\n    }\n\n    // ############################### start: region implements ProgressHandler\n    private long lastUpdateTime;\n    private long loadingUpdateMaxTimeSpan = 300; // 300ms\n\n    /**\n     * @param total\n     * @param current\n     * @param forceUpdateUI\n     * @return continue\n     */\n    @Override\n    public boolean updateProgress(long total, long current, boolean forceUpdateUI) {\n\n        if (isCancelled() || isFinished()) {\n            return false;\n        }\n\n        if (progressCallback != null && request != null && total > 0) {\n            if (total < current) {\n                total = current;\n            }\n            if (forceUpdateUI) {\n                lastUpdateTime = System.currentTimeMillis();\n                this.update(FLAG_PROGRESS, total, current, request.isLoading());\n            } else {\n                long currTime = System.currentTimeMillis();\n                if (currTime - lastUpdateTime >= loadingUpdateMaxTimeSpan) {\n                    lastUpdateTime = currTime;\n                    this.update(FLAG_PROGRESS, total, current, request.isLoading());\n                }\n            }\n        }\n\n        return !isCancelled() && !isFinished();\n    }\n\n    // ############################### end: region implements ProgressHandler\n\n    @Override\n    public String toString() {\n        return params.toString();\n    }\n\n\n    /**\n     * 请求发送和加载数据线程.\n     * 该线程被join到HttpTask的工作线程去执行.\n     * 它的主要作用是为了能强行中断请求的链接过程;\n     * 并辅助限制同时下载文件的线程数.\n     */\n    private final class RequestWorker {\n        /*private*/ Object result;\n        /*private*/ Throwable ex;\n\n        private RequestWorker() {\n        }\n\n        public void request() {\n            try {\n                boolean interrupted = false;\n                if (File.class == loadType) {\n                    while (sCurrFileLoadCount.get() >= MAX_FILE_LOAD_WORKER\n                            && !HttpTask.this.isCancelled()) {\n                        synchronized (sCurrFileLoadCount) {\n                            try {\n                                sCurrFileLoadCount.wait(10);\n                            } catch (InterruptedException iex) {\n                                interrupted = true;\n                                break;\n                            } catch (Throwable ignored) {\n                            }\n                        }\n                    }\n                    sCurrFileLoadCount.incrementAndGet();\n                }\n\n                if (interrupted || HttpTask.this.isCancelled()) {\n                    throw new Callback.CancelledException(\"cancelled before request\" + (interrupted ? \"(interrupted)\" : \"\"));\n                }\n\n                try {\n                    request.setRequestInterceptListener(requestInterceptListener);\n                    this.result = request.loadResult();\n                } catch (Throwable ex) {\n                    this.ex = ex;\n                }\n\n                if (this.ex != null) {\n                    throw this.ex;\n                }\n            } catch (Throwable ex) {\n                this.ex = ex;\n                if (ex instanceof HttpException) {\n                    HttpException httpEx = (HttpException) ex;\n                    int errorCode = httpEx.getCode();\n                    if (errorCode == 301 || errorCode == 302) {\n                        RedirectHandler redirectHandler = params.getRedirectHandler();\n                        if (redirectHandler != null) {\n                            try {\n                                RequestParams redirectParams = redirectHandler.getRedirectParams(request);\n                                if (redirectParams != null) {\n                                    if (redirectParams.getMethod() == null) {\n                                        redirectParams.setMethod(params.getMethod());\n                                    }\n                                    // 开始重定向请求\n                                    HttpTask.this.params = redirectParams;\n                                    HttpTask.this.request = createNewRequest();\n                                    this.ex = new HttpRedirectException(errorCode, httpEx.getMessage(), httpEx.getResult());\n                                }\n                            } catch (Throwable throwable) {\n                                this.ex = ex;\n                            }\n                        }\n                    }\n                }\n            } finally {\n                if (File.class == loadType) {\n                    synchronized (sCurrFileLoadCount) {\n                        sCurrFileLoadCount.decrementAndGet();\n                        sCurrFileLoadCount.notifyAll();\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/ProgressHandler.java",
    "content": "package org.xutils.http;\n\n/**\n * 进度控制接口, updateProgress方式中ProgressCallback#onLoading.\n * 默认最长间隔300毫秒调用一次.\n * Author: wyouflf\n * Time: 2014/05/23\n */\npublic interface ProgressHandler {\n    /**\n     * @param total\n     * @param current\n     * @param forceUpdateUI\n     * @return continue\n     */\n    boolean updateProgress(long total, long current, boolean forceUpdateUI);\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/RequestParams.java",
    "content": "package org.xutils.http;\n\nimport android.text.TextUtils;\n\nimport org.xutils.common.task.Priority;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.annotation.HttpRequest;\nimport org.xutils.http.app.DefaultParamsBuilder;\nimport org.xutils.http.app.HttpRetryHandler;\nimport org.xutils.http.app.ParamsBuilder;\nimport org.xutils.http.app.RedirectHandler;\nimport org.xutils.http.app.RequestTracker;\n\nimport java.net.Proxy;\nimport java.util.concurrent.Executor;\n\nimport javax.net.ssl.SSLSocketFactory;\n\n/**\n * Created by wyouflf on 15/7/17.\n * 网络请求参数实体\n */\npublic class RequestParams extends BaseParams {\n\n    // 注解及其扩展参数\n    private HttpRequest httpRequest;\n    private final String uri;\n    private final String[] signs;\n    private final String[] cacheKeys;\n    private ParamsBuilder builder;\n    private String buildUri;\n    private String buildCacheKey;\n    private SSLSocketFactory sslSocketFactory;\n\n    // 扩展参数\n    private Proxy proxy; // 代理\n    private boolean useCookie = true; // 是否在请求过程中启用cookie\n    private String cacheDirName; // 缓存文件夹名称\n    private long cacheSize; // 缓存文件夹大小\n    private long cacheMaxAge; // 默认缓存存活时间, 单位:毫秒.(如果服务没有返回有效的max-age或Expires)\n    private Executor executor; // 自定义线程池\n    private Priority priority = Priority.DEFAULT; // 请求优先级\n    private int connectTimeout = 1000 * 15; // 连接超时时间\n    private boolean autoResume = true; // 是否在下载是自动断点续传\n    private boolean autoRename = false; // 是否根据头信息自动命名文件\n    private int maxRetryCount = 2; // 最大请求错误重试次数\n    private String saveFilePath; // 下载文件时文件保存的路径和文件名\n    private boolean cancelFast = false; // 是否可以被立即停止, true: 为请求创建新的线程, 取消时请求线程被立即中断.\n    private int loadingUpdateMaxTimeSpan = 300; // 进度刷新最大间隔时间(ms)\n    private HttpRetryHandler httpRetryHandler; // 自定义HttpRetryHandler\n    private RedirectHandler redirectHandler; // 自定义重定向接口, 默认系统自动重定向.\n    private RequestTracker requestTracker; // 自定义日志记录接口.\n\n    /**\n     * 使用空构造创建时必须, 必须是带有@HttpRequest注解的子类.\n     */\n    public RequestParams() {\n        this(null, null, null, null);\n    }\n\n    /**\n     * @param uri 不可为空\n     */\n    public RequestParams(String uri) {\n        this(uri, null, null, null);\n    }\n\n    /**\n     * @param uri       不可为空\n     * @param builder\n     * @param signs\n     * @param cacheKeys\n     */\n    public RequestParams(String uri, ParamsBuilder builder, String[] signs, String[] cacheKeys) {\n        if (uri != null && builder == null) {\n            builder = new DefaultParamsBuilder();\n        }\n        this.uri = uri;\n        this.signs = signs;\n        this.cacheKeys = cacheKeys;\n        this.builder = builder;\n    }\n\n    // invoke via HttpTask#createNewRequest\n    /*package*/ void init() throws Throwable {\n        if (!TextUtils.isEmpty(buildUri)) return;\n\n        if (TextUtils.isEmpty(uri) && getHttpRequest() == null) {\n            throw new IllegalStateException(\"uri is empty && @HttpRequest == null\");\n        }\n\n        // init params from entity\n        initEntityParams();\n\n        // build uri & cacheKey\n        buildUri = uri;\n        HttpRequest httpRequest = this.getHttpRequest();\n        if (httpRequest != null) {\n            builder = httpRequest.builder().newInstance();\n            buildUri = builder.buildUri(this, httpRequest);\n            builder.buildParams(this);\n            builder.buildSign(this, httpRequest.signs());\n            if (sslSocketFactory == null) {\n                sslSocketFactory = builder.getSSLSocketFactory();\n            }\n        } else if (this.builder != null) {\n            builder.buildParams(this);\n            builder.buildSign(this, signs);\n            if (sslSocketFactory == null) {\n                sslSocketFactory = builder.getSSLSocketFactory();\n            }\n        }\n    }\n\n    public String getUri() {\n        return TextUtils.isEmpty(buildUri) ? uri : buildUri;\n    }\n\n    public String getCacheKey() {\n        if (TextUtils.isEmpty(buildCacheKey) && builder != null) {\n            HttpRequest httpRequest = this.getHttpRequest();\n            if (httpRequest != null) {\n                buildCacheKey = builder.buildCacheKey(this, httpRequest.cacheKeys());\n            } else {\n                buildCacheKey = builder.buildCacheKey(this, cacheKeys);\n            }\n        }\n        return buildCacheKey;\n    }\n\n    public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {\n        this.sslSocketFactory = sslSocketFactory;\n    }\n\n    public SSLSocketFactory getSslSocketFactory() {\n        return sslSocketFactory;\n    }\n\n    /**\n     * 是否在请求过程中启用cookie, 默认true.\n     *\n     * @return\n     */\n    public boolean isUseCookie() {\n        return useCookie;\n    }\n\n    /**\n     * 是否在请求过程中启用cookie, 默认true.\n     *\n     * @param useCookie\n     */\n    public void setUseCookie(boolean useCookie) {\n        this.useCookie = useCookie;\n    }\n\n    public Proxy getProxy() {\n        return proxy;\n    }\n\n    public void setProxy(Proxy proxy) {\n        this.proxy = proxy;\n    }\n\n    public Priority getPriority() {\n        return priority;\n    }\n\n    public void setPriority(Priority priority) {\n        this.priority = priority;\n    }\n\n    public int getConnectTimeout() {\n        return connectTimeout;\n    }\n\n    public void setConnectTimeout(int connectTimeout) {\n        if (connectTimeout > 0) {\n            this.connectTimeout = connectTimeout;\n        }\n    }\n\n    public String getCacheDirName() {\n        return cacheDirName;\n    }\n\n    public void setCacheDirName(String cacheDirName) {\n        this.cacheDirName = cacheDirName;\n    }\n\n    public long getCacheSize() {\n        return cacheSize;\n    }\n\n    public void setCacheSize(long cacheSize) {\n        this.cacheSize = cacheSize;\n    }\n\n    /**\n     * 默认缓存存活时间, 单位:毫秒.(如果服务没有返回有效的max-age或Expires)\n     *\n     * @return\n     */\n    public long getCacheMaxAge() {\n        return cacheMaxAge;\n    }\n\n    /**\n     * 默认缓存存活时间, 单位:毫秒.(如果服务没有返回有效的max-age或Expires)\n     *\n     * @param cacheMaxAge\n     */\n    public void setCacheMaxAge(long cacheMaxAge) {\n        this.cacheMaxAge = cacheMaxAge;\n    }\n\n    /**\n     * 自定义线程池\n     *\n     * @return\n     */\n    public Executor getExecutor() {\n        return executor;\n    }\n\n    /**\n     * 自定义线程池\n     *\n     * @param executor\n     */\n    public void setExecutor(Executor executor) {\n        this.executor = executor;\n    }\n\n    /**\n     * 是否在下载是自动断点续传\n     */\n    public boolean isAutoResume() {\n        return autoResume;\n    }\n\n    /**\n     * 设置是否在下载是自动断点续传\n     *\n     * @param autoResume\n     */\n    public void setAutoResume(boolean autoResume) {\n        this.autoResume = autoResume;\n    }\n\n    /**\n     * 是否根据头信息自动命名文件\n     */\n    public boolean isAutoRename() {\n        return autoRename;\n    }\n\n    /**\n     * 设置是否根据头信息自动命名文件\n     *\n     * @param autoRename\n     */\n    public void setAutoRename(boolean autoRename) {\n        this.autoRename = autoRename;\n    }\n\n    /**\n     * 获取下载文件时文件保存的路径和文件名\n     */\n    public String getSaveFilePath() {\n        return saveFilePath;\n    }\n\n    /**\n     * 设置下载文件时文件保存的路径和文件名\n     *\n     * @param saveFilePath\n     */\n    public void setSaveFilePath(String saveFilePath) {\n        this.saveFilePath = saveFilePath;\n    }\n\n    public int getMaxRetryCount() {\n        return maxRetryCount;\n    }\n\n    public void setMaxRetryCount(int maxRetryCount) {\n        this.maxRetryCount = maxRetryCount;\n    }\n\n    /**\n     * 是否可以被立即停止.\n     *\n     * @return true: 为请求创建新的线程, 取消时请求线程被立即中断; false: 请求建立过程可能不被立即终止.\n     */\n    public boolean isCancelFast() {\n        return cancelFast;\n    }\n\n    /**\n     * 是否可以被立即停止.\n     *\n     * @param cancelFast true: 为请求创建新的线程, 取消时请求线程被立即中断; false: 请求建立过程可能不被立即终止.\n     */\n    public void setCancelFast(boolean cancelFast) {\n        this.cancelFast = cancelFast;\n    }\n\n    public int getLoadingUpdateMaxTimeSpan() {\n        return loadingUpdateMaxTimeSpan;\n    }\n\n    /**\n     * 进度刷新最大间隔时间(默认300毫秒)\n     *\n     * @param loadingUpdateMaxTimeSpan\n     */\n    public void setLoadingUpdateMaxTimeSpan(int loadingUpdateMaxTimeSpan) {\n        this.loadingUpdateMaxTimeSpan = loadingUpdateMaxTimeSpan;\n    }\n\n    public HttpRetryHandler getHttpRetryHandler() {\n        return httpRetryHandler;\n    }\n\n    public void setHttpRetryHandler(HttpRetryHandler httpRetryHandler) {\n        this.httpRetryHandler = httpRetryHandler;\n    }\n\n    public RedirectHandler getRedirectHandler() {\n        return redirectHandler;\n    }\n\n    /**\n     * 自定义重定向接口, 默认系统自动重定向.\n     *\n     * @param redirectHandler\n     */\n    public void setRedirectHandler(RedirectHandler redirectHandler) {\n        this.redirectHandler = redirectHandler;\n    }\n\n    public RequestTracker getRequestTracker() {\n        return requestTracker;\n    }\n\n    public void setRequestTracker(RequestTracker requestTracker) {\n        this.requestTracker = requestTracker;\n    }\n\n    private void initEntityParams() {\n        RequestParamsHelper.parseKV(this, this.getClass(), new RequestParamsHelper.ParseKVListener() {\n            @Override\n            public void onParseKV(String name, Object value) {\n                addParameter(name, value);\n            }\n        });\n    }\n\n    private boolean invokedGetHttpRequest = false;\n\n    private HttpRequest getHttpRequest() {\n        if (httpRequest == null && !invokedGetHttpRequest) {\n            invokedGetHttpRequest = true;\n            Class<?> thisCls = this.getClass();\n            if (thisCls != RequestParams.class) {\n                httpRequest = thisCls.getAnnotation(HttpRequest.class);\n            }\n        }\n\n        return httpRequest;\n    }\n\n    /**\n     * 在网络请求onStart前, 尽量不要在UI线程调用这个方法, 可能产生性能影响.\n     *\n     * @return\n     */\n    @Override\n    public String toString() {\n        try {\n            this.init();\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        String url = this.getUri();\n        return TextUtils.isEmpty(url) ?\n                super.toString() :\n                url + (url.contains(\"?\") ? \"&\" : \"?\") + super.toString();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/RequestParamsHelper.java",
    "content": "package org.xutils.http;\n\nimport android.os.Parcelable;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.xutils.common.util.LogUtil;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by wyouflf on 16/1/23.\n */\n/*package*/ final class RequestParamsHelper {\n\n    private static final ClassLoader BOOT_CL = String.class.getClassLoader();\n\n    private RequestParamsHelper() {\n    }\n\n    /*package*/ interface ParseKVListener {\n        void onParseKV(String name, Object value);\n    }\n\n    /*package*/\n    static void parseKV(Object entity, Class<?> type, ParseKVListener listener) {\n        if (entity == null || type == null || type == RequestParams.class || type == Object.class) {\n            return;\n        } else {\n            ClassLoader cl = type.getClassLoader();\n            if (cl == null || cl == BOOT_CL) {\n                return;\n            }\n        }\n\n        Field[] fields = type.getDeclaredFields();\n        if (fields != null && fields.length > 0) {\n            for (Field field : fields) {\n                if (!Modifier.isTransient(field.getModifiers())\n                        && field.getType() != Parcelable.Creator.class) {\n                    field.setAccessible(true);\n                    try {\n                        String name = field.getName();\n                        Object value = field.get(entity);\n                        if (value != null) {\n                            listener.onParseKV(name, value);\n                        }\n                    } catch (IllegalAccessException ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }\n        }\n\n        parseKV(entity, type.getSuperclass(), listener);\n    }\n\n    /*package*/\n    static Object parseJSONObject(Object value) throws JSONException {\n        if (value == null) return null;\n\n        Object result = value;\n        Class<?> cls = value.getClass();\n        if (cls.isArray()) {\n            JSONArray array = new JSONArray();\n            int len = Array.getLength(value);\n            for (int i = 0; i < len; i++) {\n                array.put(parseJSONObject(Array.get(value, i)));\n            }\n            result = array;\n        } else if (value instanceof List) {\n            JSONArray array = new JSONArray();\n            List<?> list = (List<?>) value;\n            for (Object item : list) {\n                array.put(parseJSONObject(item));\n            }\n            result = array;\n        } else if (value instanceof Map) {\n            final JSONObject jo = new JSONObject();\n            Map<?, ?> map = (Map<?, ?>) value;\n            for (Map.Entry<?, ?> entry : map.entrySet()) {\n                Object k = entry.getKey();\n                Object v = entry.getValue();\n                if (k != null && v != null) {\n                    jo.put(String.valueOf(k), parseJSONObject(v));\n                }\n            }\n            result = jo;\n        } else {\n            ClassLoader cl = cls.getClassLoader();\n            if (cl != null && cl != BOOT_CL) {\n                final JSONObject jo = new JSONObject();\n                parseKV(value, cls, new ParseKVListener() {\n                    @Override\n                    public void onParseKV(String name, Object value) {\n                        try {\n                            value = parseJSONObject(value);\n                            jo.put(name, value);\n                        } catch (JSONException ex) {\n                            throw new IllegalArgumentException(\"parse RequestParams to json failed\", ex);\n                        }\n                    }\n                });\n                result = jo;\n            }\n        }\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/RequestTrackerWrapper.java",
    "content": "package org.xutils.http;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.app.RequestTracker;\nimport org.xutils.http.request.UriRequest;\n\n/**\n * Created by wyouflf on 15/11/4.\n * Wrapper for tracker\n */\n/*package*/ final class RequestTrackerWrapper implements RequestTracker {\n\n    private final RequestTracker base;\n\n    public RequestTrackerWrapper(RequestTracker base) {\n        this.base = base;\n    }\n\n    @Override\n    public void onWaiting(RequestParams params) {\n        try {\n            base.onWaiting(params);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    @Override\n    public void onStart(RequestParams params) {\n        try {\n            base.onStart(params);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    @Override\n    public void onRequestCreated(UriRequest request) {\n        try {\n            base.onRequestCreated(request);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    @Override\n    public void onCache(UriRequest request, Object result) {\n        try {\n            base.onCache(request, result);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    @Override\n    public void onSuccess(UriRequest request, Object result) {\n        try {\n            base.onSuccess(request, result);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    @Override\n    public void onCancelled(UriRequest request) {\n        try {\n            base.onCancelled(request);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    @Override\n    public void onError(UriRequest request, Throwable ex, boolean isCallbackError) {\n        try {\n            base.onError(request, ex, isCallbackError);\n        } catch (Throwable exOnError) {\n            LogUtil.e(exOnError.getMessage(), exOnError);\n        }\n    }\n\n    @Override\n    public void onFinished(UriRequest request) {\n        try {\n            base.onFinished(request);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/annotation/HttpRequest.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.http.annotation;\n\nimport org.xutils.http.app.DefaultParamsBuilder;\nimport org.xutils.http.app.ParamsBuilder;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface HttpRequest {\n\n    String host() default \"\";\n\n    String path();\n\n    Class<? extends ParamsBuilder> builder() default DefaultParamsBuilder.class;\n\n    String[] signs() default \"\";\n\n    String[] cacheKeys() default \"\";\n}"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/annotation/HttpResponse.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.http.annotation;\n\nimport org.xutils.http.app.ResponseParser;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface HttpResponse {\n\n    Class<? extends ResponseParser> parser();\n\n}"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/DefaultParamsBuilder.java",
    "content": "package org.xutils.http.app;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.annotation.HttpRequest;\n\nimport java.security.cert.X509Certificate;\n\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLSocketFactory;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.X509TrustManager;\n\n/**\n * Created by wyouflf on 15/8/20.\n * 默认参数构造器\n */\npublic class DefaultParamsBuilder implements ParamsBuilder {\n\n    public DefaultParamsBuilder() {\n    }\n\n    /**\n     * 根据@HttpRequest构建请求的url\n     *\n     * @param params\n     * @param httpRequest\n     * @return\n     */\n    @Override\n    public String buildUri(RequestParams params, HttpRequest httpRequest) throws Throwable {\n        return httpRequest.host() + \"/\" + httpRequest.path();\n    }\n\n    /**\n     * 根据注解的cacheKeys构建缓存的自定义key,\n     * 如果返回null, 默认使用 url 和整个 query string 组成.\n     *\n     * @param params\n     * @param cacheKeys\n     * @return\n     */\n    @Override\n    public String buildCacheKey(RequestParams params, String[] cacheKeys) {\n        String cacheKey = null;\n        if (cacheKeys != null && cacheKeys.length > 0) {\n\n            cacheKey = params.getUri() + \"?\";\n\n            // 添加cacheKeys对应的参数\n            for (String key : cacheKeys) {\n                String value = params.getStringParameter(key);\n                if (value != null) {\n                    cacheKey += key + \"=\" + value + \"&\";\n                }\n            }\n        }\n        return cacheKey;\n    }\n\n    /**\n     * 自定义SSLSocketFactory\n     *\n     * @return\n     */\n    @Override\n    public SSLSocketFactory getSSLSocketFactory() throws Throwable {\n        return getTrustAllSSLSocketFactory();\n    }\n\n    /**\n     * 为请求添加通用参数等操作\n     *\n     * @param params\n     */\n    @Override\n    public void buildParams(RequestParams params) throws Throwable {\n    }\n\n    /**\n     * 自定义参数签名\n     *\n     * @param params\n     * @param signs\n     */\n    @Override\n    public void buildSign(RequestParams params, String[] signs) throws Throwable {\n\n    }\n\n    private static SSLSocketFactory trustAllSSlSocketFactory;\n\n    public static SSLSocketFactory getTrustAllSSLSocketFactory() {\n        if (trustAllSSlSocketFactory == null) {\n            synchronized (DefaultParamsBuilder.class) {\n                if (trustAllSSlSocketFactory == null) {\n\n                    // 信任所有证书\n                    TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {\n                        @Override\n                        public X509Certificate[] getAcceptedIssuers() {\n                            return null;\n                        }\n\n                        @Override\n                        public void checkClientTrusted(X509Certificate[] certs, String authType) {\n                        }\n\n                        @Override\n                        public void checkServerTrusted(X509Certificate[] certs, String authType) {\n                        }\n                    }};\n                    try {\n                        SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n                        sslContext.init(null, trustAllCerts, null);\n                        trustAllSSlSocketFactory = sslContext.getSocketFactory();\n                    } catch (Throwable ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }\n        }\n\n        return trustAllSSlSocketFactory;\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/HttpRetryHandler.java",
    "content": "package org.xutils.http.app;\n\n\nimport org.json.JSONException;\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.ex.HttpException;\nimport org.xutils.http.HttpMethod;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.FileNotFoundException;\nimport java.net.MalformedURLException;\nimport java.net.NoRouteToHostException;\nimport java.net.PortUnreachableException;\nimport java.net.ProtocolException;\nimport java.net.URISyntaxException;\nimport java.net.UnknownHostException;\nimport java.util.HashSet;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\npublic class HttpRetryHandler {\n\n    protected int maxRetryCount = 2;\n\n    protected static HashSet<Class<?>> blackList = new HashSet<Class<?>>();\n\n    static {\n        blackList.add(HttpException.class);\n        blackList.add(Callback.CancelledException.class);\n        blackList.add(MalformedURLException.class);\n        blackList.add(URISyntaxException.class);\n        blackList.add(NoRouteToHostException.class);\n        blackList.add(PortUnreachableException.class);\n        blackList.add(ProtocolException.class);\n        blackList.add(NullPointerException.class);\n        blackList.add(FileNotFoundException.class);\n        blackList.add(JSONException.class);\n        blackList.add(UnknownHostException.class);\n        blackList.add(IllegalArgumentException.class);\n    }\n\n    public HttpRetryHandler() {\n    }\n\n    public void setMaxRetryCount(int maxRetryCount) {\n        this.maxRetryCount = maxRetryCount;\n    }\n\n    public boolean canRetry(UriRequest request, Throwable ex, int count) {\n\n        LogUtil.w(ex.getMessage(), ex);\n\n        if (count > maxRetryCount) {\n            LogUtil.w(request.toString());\n            LogUtil.w(\"The Max Retry times has been reached!\");\n            return false;\n        }\n\n        if (!HttpMethod.permitsRetry(request.getParams().getMethod())) {\n            LogUtil.w(request.toString());\n            LogUtil.w(\"The Request Method can not be retried.\");\n            return false;\n        }\n\n        if (blackList.contains(ex.getClass())) {\n            LogUtil.w(request.toString());\n            LogUtil.w(\"The Exception can not be retried.\");\n            return false;\n        }\n\n        return true;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/InputStreamResponseParser.java",
    "content": "package org.xutils.http.app;\n\nimport java.io.InputStream;\nimport java.lang.reflect.Type;\n\n/**\n * Created by wyouflf on 16/2/2.\n */\npublic abstract class InputStreamResponseParser implements ResponseParser {\n\n    public abstract Object parse(Type resultType, Class<?> resultClass, InputStream result) throws Throwable;\n\n    /**\n     * Deprecated, see {@link InputStreamResponseParser#parse(Type, Class, InputStream)}\n     *\n     * @throws Throwable\n     */\n    @Override\n    @Deprecated\n    public final Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable {\n        return null;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/ParamsBuilder.java",
    "content": "package org.xutils.http.app;\n\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.annotation.HttpRequest;\n\nimport javax.net.ssl.SSLSocketFactory;\n\n/**\n * Created by wyouflf on 15/8/20.\n * <p>\n * {@link org.xutils.http.annotation.HttpRequest} 注解的参数构建的模板接口\n */\npublic interface ParamsBuilder {\n\n    /**\n     * 根据@HttpRequest构建请求的url\n     *\n     * @param params\n     * @param httpRequest\n     * @return\n     */\n    String buildUri(RequestParams params, HttpRequest httpRequest) throws Throwable;\n\n    /**\n     * 根据注解的cacheKeys构建缓存的自定义key,\n     * 如果返回null, 默认使用 url 和整个 query string 组成.\n     *\n     * @param params\n     * @param cacheKeys\n     * @return\n     */\n    String buildCacheKey(RequestParams params, String[] cacheKeys);\n\n    /**\n     * 自定义SSLSocketFactory\n     *\n     * @return\n     */\n    SSLSocketFactory getSSLSocketFactory() throws Throwable;\n\n    /**\n     * 为请求添加通用参数等操作\n     *\n     * @param params\n     */\n    void buildParams(RequestParams params) throws Throwable;\n\n    /**\n     * 自定义参数签名\n     *\n     * @param params\n     * @param signs\n     */\n    void buildSign(RequestParams params, String[] signs) throws Throwable;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/RedirectHandler.java",
    "content": "package org.xutils.http.app;\n\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.request.UriRequest;\n\n/**\n * Created by wyouflf on 15/11/12.\n * 请求重定向控制接口\n */\npublic interface RedirectHandler {\n\n    /**\n     * 根据请求信息返回自定义重定向的请求参数\n     *\n     * @param request\n     * @return 返回不为null时进行重定向\n     */\n    RequestParams getRedirectParams(UriRequest request) throws Throwable;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/RequestInterceptListener.java",
    "content": "package org.xutils.http.app;\n\n\nimport org.xutils.http.request.UriRequest;\n\n/**\n * Created by wyouflf on 15/11/10.\n * 拦截请求响应(在后台线程工作).\n * <p>\n * 用法: 请求的callback参数同时实现RequestInterceptListener\n */\npublic interface RequestInterceptListener {\n\n    void beforeRequest(UriRequest request) throws Throwable;\n\n    void afterRequest(UriRequest request) throws Throwable;\n}"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/RequestTracker.java",
    "content": "package org.xutils.http.app;\n\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.request.UriRequest;\n\n/**\n * Created by wyouflf on 15/9/10.\n * 请求过程追踪, 适合用来记录请求日志.\n * 所有回调方法都在主线程进行.\n * <p>\n * 用法:\n * 1. 将RequestTracker实例设置给请求参数RequestParams.\n * 2. 请的callback参数同时实现RequestTracker接口;\n * 3. 注册给UriRequestFactory的默认RequestTracker.\n * 注意: 请求回调RequestTracker时优先级按照上面的顺序,\n * 找到一个RequestTracker的实现会忽略其他.\n */\npublic interface RequestTracker {\n\n    void onWaiting(RequestParams params);\n\n    void onStart(RequestParams params);\n\n    void onRequestCreated(UriRequest request);\n\n    void onCache(UriRequest request, Object result);\n\n    void onSuccess(UriRequest request, Object result);\n\n    void onCancelled(UriRequest request);\n\n    void onError(UriRequest request, Throwable ex, boolean isCallbackError);\n\n    void onFinished(UriRequest request);\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/app/ResponseParser.java",
    "content": "package org.xutils.http.app;\n\n\nimport org.xutils.http.request.UriRequest;\n\nimport java.lang.reflect.Type;\n\n/**\n * Created by wyouflf on 15/8/4.\n * {@link org.xutils.http.annotation.HttpResponse} 注解的返回值转换模板\n */\npublic interface ResponseParser {\n\n    /**\n     * 检查请求相应头等处理\n     *\n     * @param request\n     * @throws Throwable\n     */\n    void checkResponse(UriRequest request) throws Throwable;\n\n    /**\n     * 转换result为resultType类型的对象\n     *\n     * @param resultType  返回值类型(可能带有泛型信息)\n     * @param resultClass 返回值类型\n     * @param result      字符串数据\n     * @return\n     * @throws Throwable\n     */\n    Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/BodyItemWrapper.java",
    "content": "package org.xutils.http.body;\n\nimport android.text.TextUtils;\n\n/**\n * Created by wyouflf on 15/8/13.\n * Wrapper for RequestBody value.\n */\npublic final class BodyItemWrapper {\n\n    private final Object value;\n    private final String fileName;\n    private final String contentType;\n\n    public BodyItemWrapper(Object value, String contentType) {\n        this(value, contentType, null);\n    }\n\n    public BodyItemWrapper(Object value, String contentType, String fileName) {\n        this.value = value;\n        if (TextUtils.isEmpty(contentType)) {\n            this.contentType = \"application/octet-stream\";\n        } else {\n            this.contentType = contentType;\n        }\n        this.fileName = fileName;\n    }\n\n    public Object getValue() {\n        return value;\n    }\n\n    public String getFileName() {\n        return fileName;\n    }\n\n    public String getContentType() {\n        return contentType;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/FileBody.java",
    "content": "package org.xutils.http.body;\n\nimport android.text.TextUtils;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.net.HttpURLConnection;\n\n/**\n * Created by wyouflf on 15/8/13.\n */\npublic class FileBody extends InputStreamBody {\n\n    private File file;\n    private String contentType;\n\n    public FileBody(File file) throws IOException {\n        this(file, null);\n    }\n\n    public FileBody(File file, String contentType) throws IOException {\n        super(new FileInputStream(file));\n        this.file = file;\n        this.contentType = contentType;\n    }\n\n    @Override\n    public void setContentType(String contentType) {\n        this.contentType = contentType;\n    }\n\n    @Override\n    public String getContentType() {\n        if (TextUtils.isEmpty(contentType)) {\n            contentType = getFileContentType(file);\n        }\n        return contentType;\n    }\n\n    public static String getFileContentType(File file) {\n        String filename = file.getName();\n        String contentType = HttpURLConnection.guessContentTypeFromName(filename);\n        if (TextUtils.isEmpty(contentType)) {\n            contentType = \"application/octet-stream\";\n        } else {\n            contentType = contentType.replaceFirst(\"\\\\/jpg$\", \"/jpeg\");\n        }\n        return contentType;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/InputStreamBody.java",
    "content": "package org.xutils.http.body;\n\nimport android.text.TextUtils;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.http.ProgressHandler;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\npublic class InputStreamBody implements ProgressBody {\n\n    private InputStream content;\n    private String contentType;\n\n    private final long total;\n    private long current = 0;\n\n    private ProgressHandler callBackHandler;\n\n    public InputStreamBody(InputStream inputStream) {\n        this(inputStream, null);\n    }\n\n    public InputStreamBody(InputStream inputStream, String contentType) {\n        this.content = inputStream;\n        this.contentType = contentType;\n        this.total = getInputStreamLength(inputStream);\n    }\n\n    @Override\n    public void setProgressHandler(ProgressHandler progressHandler) {\n        this.callBackHandler = progressHandler;\n    }\n\n    @Override\n    public long getContentLength() {\n        return total;\n    }\n\n    @Override\n    public void setContentType(String contentType) {\n        this.contentType = contentType;\n    }\n\n    @Override\n    public String getContentType() {\n        return TextUtils.isEmpty(contentType) ? \"application/octet-stream\" : contentType;\n    }\n\n    @Override\n    public void writeTo(OutputStream out) throws IOException {\n        if (callBackHandler != null && !callBackHandler.updateProgress(total, current, true)) {\n            throw new Callback.CancelledException(\"upload stopped!\");\n        }\n\n        byte[] buffer = new byte[1024];\n        try {\n            int len = 0;\n            while ((len = content.read(buffer)) != -1) {\n                out.write(buffer, 0, len);\n                current += len;\n                if (callBackHandler != null && !callBackHandler.updateProgress(total, current, false)) {\n                    throw new Callback.CancelledException(\"upload stopped!\");\n                }\n            }\n            out.flush();\n\n            if (callBackHandler != null) {\n                callBackHandler.updateProgress(total, total, true);\n            }\n        } finally {\n            IOUtil.closeQuietly(content);\n        }\n    }\n\n    public static long getInputStreamLength(InputStream inputStream) {\n        try {\n            if (inputStream instanceof FileInputStream ||\n                    inputStream instanceof ByteArrayInputStream) {\n                return inputStream.available();\n            }\n        } catch (Throwable ignored) {\n        }\n        return -1L;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/MultipartBody.java",
    "content": "package org.xutils.http.body;\n\n\nimport android.text.TextUtils;\n\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.http.ProgressHandler;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\npublic class MultipartBody implements ProgressBody {\n\n    private static byte[] BOUNDARY_PREFIX_BYTES = \"--------7da3d81520810\".getBytes();\n    private static byte[] END_BYTES = \"\\r\\n\".getBytes();\n    private static byte[] TWO_DASHES_BYTES = \"--\".getBytes();\n    private byte[] boundaryPostfixBytes;\n    private String contentType; // multipart/subtype; boundary=xxx...\n    private String charset = \"UTF-8\";\n\n    private List<KeyValue> multipartParams;\n    private long total = 0;\n    private long current = 0;\n\n    public MultipartBody(List<KeyValue> multipartParams, String charset) {\n        if (!TextUtils.isEmpty(charset)) {\n            this.charset = charset;\n        }\n        this.multipartParams = multipartParams;\n        generateContentType();\n\n        // calc total\n        CounterOutputStream counter = new CounterOutputStream();\n        try {\n            this.writeTo(counter);\n            this.total = counter.total.get();\n        } catch (IOException e) {\n            this.total = -1;\n        }\n    }\n\n    private ProgressHandler callBackHandler;\n\n    @Override\n    public void setProgressHandler(ProgressHandler progressHandler) {\n        this.callBackHandler = progressHandler;\n    }\n\n    private void generateContentType() {\n        String boundaryPostfix = Double.toHexString(Math.random() * 0xFFFF);\n        boundaryPostfixBytes = boundaryPostfix.getBytes();\n        contentType = \"multipart/form-data; boundary=\" + new String(BOUNDARY_PREFIX_BYTES) + boundaryPostfix;\n    }\n\n    @Override\n    public long getContentLength() {\n        return total;\n    }\n\n    /**\n     * only change subType:\n     * \"multipart/subType; boundary=xxx...\"\n     *\n     * @param subType \"form-data\" or \"related\"\n     */\n    @Override\n    public void setContentType(String subType) {\n        int index = contentType.indexOf(\";\");\n        this.contentType = \"multipart/\" + subType + contentType.substring(index);\n    }\n\n    @Override\n    public String getContentType() {\n        return contentType;\n    }\n\n    @Override\n    public void writeTo(OutputStream out) throws IOException {\n\n        if (callBackHandler != null && !callBackHandler.updateProgress(total, current, true)) {\n            throw new Callback.CancelledException(\"upload stopped!\");\n        }\n\n        for (KeyValue kv : multipartParams) {\n            String name = kv.key;\n            Object value = kv.value;\n            if (!TextUtils.isEmpty(name) && value != null) {\n                writeEntry(out, name, value);\n            }\n        }\n        writeLine(out, TWO_DASHES_BYTES, BOUNDARY_PREFIX_BYTES, boundaryPostfixBytes, TWO_DASHES_BYTES);\n        out.flush();\n\n        if (callBackHandler != null) {\n            callBackHandler.updateProgress(total, total, true);\n        }\n    }\n\n    /**\n     * 写入multipart中的一项\n     *\n     * @param out\n     * @param name\n     * @param value\n     * @throws IOException\n     */\n    private void writeEntry(OutputStream out, String name, Object value) throws IOException {\n        writeLine(out, TWO_DASHES_BYTES, BOUNDARY_PREFIX_BYTES, boundaryPostfixBytes);\n\n        String fileName = \"\";\n        String contentType = null;\n        if (value instanceof BodyItemWrapper) {\n            BodyItemWrapper wrapper = (BodyItemWrapper) value;\n            value = wrapper.getValue();\n            fileName = wrapper.getFileName();\n            contentType = wrapper.getContentType();\n        }\n\n        if (value instanceof File) {\n            File file = (File) value;\n            if (TextUtils.isEmpty(fileName)) {\n                fileName = file.getName();\n            }\n            if (TextUtils.isEmpty(contentType)) {\n                contentType = FileBody.getFileContentType(file);\n            }\n            writeLine(out, buildContentDisposition(name, fileName, charset));\n            writeLine(out, buildContentType(value, contentType, charset));\n            writeLine(out); // 内容前空一行\n            writeFile(out, file);\n            writeLine(out);\n        } else {\n            writeLine(out, buildContentDisposition(name, fileName, charset));\n            writeLine(out, buildContentType(value, contentType, charset));\n            writeLine(out); // 内容前空一行\n            if (value instanceof InputStream) {\n                writeStreamAndCloseIn(out, (InputStream) value);\n                writeLine(out);\n            } else {\n                byte[] content;\n                if (value instanceof byte[]) {\n                    content = (byte[]) value;\n                } else {\n                    content = String.valueOf(value).getBytes(charset);\n                }\n                writeLine(out, content);\n                current += content.length;\n                if (callBackHandler != null && !callBackHandler.updateProgress(total, current, false)) {\n                    throw new Callback.CancelledException(\"upload stopped!\");\n                }\n            }\n        }\n    }\n\n    private void writeLine(OutputStream out, byte[]... bs) throws IOException {\n        if (bs != null) {\n            for (byte[] b : bs) {\n                out.write(b);\n            }\n        }\n        out.write(END_BYTES);\n    }\n\n    private void writeFile(OutputStream out, File file) throws IOException {\n        if (out instanceof CounterOutputStream) {\n            ((CounterOutputStream) out).addFile(file);\n        } else {\n            writeStreamAndCloseIn(out, new FileInputStream(file));\n        }\n    }\n\n    private void writeStreamAndCloseIn(OutputStream out, InputStream in) throws IOException {\n        if (out instanceof CounterOutputStream) {\n            ((CounterOutputStream) out).addStream(in);\n        } else {\n            try {\n                int len;\n                byte[] buf = new byte[1024];\n                while ((len = in.read(buf)) >= 0) {\n                    out.write(buf, 0, len);\n                    current += len;\n                    if (callBackHandler != null && !callBackHandler.updateProgress(total, current, false)) {\n                        throw new Callback.CancelledException(\"upload stopped!\");\n                    }\n                }\n            } finally {\n                IOUtil.closeQuietly(in);\n            }\n        }\n    }\n\n    private static byte[] buildContentDisposition(String name, String fileName, String charset) throws UnsupportedEncodingException {\n        StringBuilder result = new StringBuilder(\"Content-Disposition: form-data\");\n        result.append(\"; name=\\\"\").append(name.replace(\"\\\"\", \"\\\\\\\"\")).append(\"\\\"\");\n        if (!TextUtils.isEmpty(fileName)) {\n            result.append(\"; filename=\\\"\").append(fileName.replace(\"\\\"\", \"\\\\\\\"\")).append(\"\\\"\");\n        }\n        return result.toString().getBytes(charset);\n    }\n\n    private static byte[] buildContentType(Object value, String contentType, String charset) throws UnsupportedEncodingException {\n        StringBuilder result = new StringBuilder(\"Content-Type: \");\n        if (TextUtils.isEmpty(contentType)) {\n            if (value instanceof String) {\n                contentType = \"text/plain; charset=\" + charset;\n            } else {\n                contentType = \"application/octet-stream\";\n            }\n        } else {\n            contentType = contentType.replaceFirst(\"\\\\/jpg$\", \"/jpeg\");\n        }\n        result.append(contentType);\n        return result.toString().getBytes(charset);\n    }\n\n    private class CounterOutputStream extends OutputStream {\n\n        final AtomicLong total = new AtomicLong(0L);\n\n        public CounterOutputStream() {\n        }\n\n        public void addFile(File file) {\n            if (total.get() == -1L) return;\n            total.addAndGet(file.length());\n        }\n\n        public void addStream(InputStream inputStream) {\n            if (total.get() == -1L) return;\n            long length = InputStreamBody.getInputStreamLength(inputStream);\n            if (length > 0) {\n                total.addAndGet(length);\n            } else {\n                total.set(-1L);\n            }\n        }\n\n        @Override\n        public void write(int oneByte) throws IOException {\n            if (total.get() == -1L) return;\n            total.incrementAndGet();\n        }\n\n        @Override\n        public void write(byte[] buffer) throws IOException {\n            if (total.get() == -1L) return;\n            total.addAndGet(buffer.length);\n        }\n\n        @Override\n        public void write(byte[] buffer, int offset, int count) throws IOException {\n            if (total.get() == -1L) return;\n            total.addAndGet(count);\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/ProgressBody.java",
    "content": "package org.xutils.http.body;\n\n\nimport org.xutils.http.ProgressHandler;\n\n/**\n * Created by wyouflf on 15/8/13.\n */\npublic interface ProgressBody extends RequestBody {\n    void setProgressHandler(ProgressHandler progressHandler);\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/RequestBody.java",
    "content": "package org.xutils.http.body;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\n\n/**\n * Created by wyouflf on 15/10/29.\n */\npublic interface RequestBody {\n\n    long getContentLength();\n\n    void setContentType(String contentType);\n\n    String getContentType();\n\n    void writeTo(OutputStream out) throws IOException;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/StringBody.java",
    "content": "package org.xutils.http.body;\n\nimport android.text.TextUtils;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.UnsupportedEncodingException;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\npublic class StringBody implements RequestBody {\n\n    private byte[] content;\n    private String contentType;\n    private String charset = \"UTF-8\";\n\n    public StringBody(String str, String charset) throws UnsupportedEncodingException {\n        if (!TextUtils.isEmpty(charset)) {\n            this.charset = charset;\n        }\n        this.content = str.getBytes(this.charset);\n    }\n\n    @Override\n    public long getContentLength() {\n        return content.length;\n    }\n\n    @Override\n    public void setContentType(String contentType) {\n        this.contentType = contentType;\n    }\n\n    @Override\n    public String getContentType() {\n        return TextUtils.isEmpty(contentType) ? \"application/json;charset=\" + charset : contentType;\n    }\n\n    @Override\n    public void writeTo(OutputStream out) throws IOException {\n        out.write(content);\n        out.flush();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/body/UrlEncodedParamsBody.java",
    "content": "package org.xutils.http.body;\n\nimport android.net.Uri;\nimport android.text.TextUtils;\n\nimport org.xutils.common.util.KeyValue;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.List;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\npublic class UrlEncodedParamsBody implements RequestBody {\n\n    private byte[] content;\n    private String charset = \"UTF-8\";\n\n    public UrlEncodedParamsBody(List<KeyValue> params, String charset) throws IOException {\n        if (!TextUtils.isEmpty(charset)) {\n            this.charset = charset;\n        }\n        StringBuilder contentSb = new StringBuilder();\n        if (params != null) {\n            for (KeyValue kv : params) {\n                String name = kv.key;\n                String value = kv.getValueStr();\n                if (!TextUtils.isEmpty(name) && value != null) {\n                    if (contentSb.length() > 0) {\n                        contentSb.append(\"&\");\n                    }\n                    contentSb.append(Uri.encode(name, this.charset))\n                            .append(\"=\")\n                            .append(Uri.encode(value, this.charset));\n                }\n            }\n        }\n\n        this.content = contentSb.toString().getBytes(this.charset);\n    }\n\n    @Override\n    public long getContentLength() {\n        return content.length;\n    }\n\n    @Override\n    public void setContentType(String contentType) {\n    }\n\n    @Override\n    public String getContentType() {\n        return \"application/x-www-form-urlencoded;charset=\" + charset;\n    }\n\n    @Override\n    public void writeTo(OutputStream sink) throws IOException {\n        sink.write(this.content);\n        sink.flush();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/cookie/CookieEntity.java",
    "content": "package org.xutils.http.cookie;\n\nimport android.text.TextUtils;\n\nimport org.xutils.db.annotation.Column;\nimport org.xutils.db.annotation.Table;\n\nimport java.net.HttpCookie;\nimport java.net.URI;\n\n/**\n * Created by wyouflf on 15/8/20.\n * 数据库中的cookie实体\n */\n@Table(name = \"cookie\",\n        onCreated = \"CREATE UNIQUE INDEX index_cookie_unique ON cookie(\\\"name\\\",\\\"domain\\\",\\\"path\\\")\")\n/*package*/ final class CookieEntity {\n\n    // ~ 100 year\n    private static final long MAX_EXPIRY = System.currentTimeMillis() + 1000L * 60L * 60L * 24L * 30L * 12L * 100L;\n\n    @Column(name = \"id\", isId = true)\n    private long id;\n\n    @Column(name = \"uri\")\n    private String uri; // cookie add by this uri.\n\n    @Column(name = \"name\")\n    private String name;\n    @Column(name = \"value\")\n    private String value;\n    @Column(name = \"comment\")\n    private String comment;\n    @Column(name = \"commentURL\")\n    private String commentURL;\n    @Column(name = \"discard\")\n    private boolean discard;\n    @Column(name = \"domain\")\n    private String domain;\n    @Column(name = \"expiry\")\n    private long expiry = MAX_EXPIRY;\n    @Column(name = \"path\")\n    private String path;\n    @Column(name = \"portList\")\n    private String portList;\n    @Column(name = \"secure\")\n    private boolean secure;\n    @Column(name = \"version\")\n    private int version = 1;\n\n    public CookieEntity() {\n    }\n\n    public CookieEntity(URI uri, HttpCookie cookie) {\n        this.uri = uri == null ? null : uri.toString();\n        this.name = cookie.getName();\n        this.value = cookie.getValue();\n        this.comment = cookie.getComment();\n        this.commentURL = cookie.getCommentURL();\n        this.discard = cookie.getDiscard();\n        this.domain = cookie.getDomain();\n        long maxAge = cookie.getMaxAge();\n        if (maxAge != -1L && maxAge > 0) {\n            this.expiry = (maxAge * 1000L) + System.currentTimeMillis();\n            if (this.expiry < 0L) { // 计算溢出?\n                this.expiry = MAX_EXPIRY;\n            }\n        } else {\n            this.expiry = -1L;\n        }\n        this.path = cookie.getPath();\n        if (!TextUtils.isEmpty(path) && path.length() > 1 && path.endsWith(\"/\")) {\n            this.path = path.substring(0, path.length() - 1);\n        }\n        this.portList = cookie.getPortlist();\n        this.secure = cookie.getSecure();\n        this.version = cookie.getVersion();\n    }\n\n    public HttpCookie toHttpCookie() {\n        HttpCookie cookie = new HttpCookie(name, value);\n        cookie.setComment(comment);\n        cookie.setCommentURL(commentURL);\n        cookie.setDiscard(discard);\n        cookie.setDomain(domain);\n        if (expiry == -1L) {\n            cookie.setMaxAge(-1L);\n        } else {\n            cookie.setMaxAge((expiry - System.currentTimeMillis()) / 1000L);\n        }\n        cookie.setPath(path);\n        cookie.setPortlist(portList);\n        cookie.setSecure(secure);\n        cookie.setVersion(version);\n        return cookie;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public String getUri() {\n        return uri;\n    }\n\n    public void setUri(String uri) {\n        this.uri = uri;\n    }\n\n    public boolean isExpired() {\n        return expiry != -1L && expiry < System.currentTimeMillis();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/cookie/DbCookieStore.java",
    "content": "package org.xutils.http.cookie;\n\nimport android.text.TextUtils;\n\nimport org.xutils.DbManager;\nimport org.xutils.common.task.PriorityExecutor;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.config.DbConfigs;\nimport org.xutils.db.Selector;\nimport org.xutils.db.sqlite.WhereBuilder;\nimport org.xutils.db.table.DbModel;\nimport org.xutils.x;\n\nimport java.net.CookieStore;\nimport java.net.HttpCookie;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Executor;\n\n/**\n * Created by wyouflf on 15/8/20.\n * 基于数据库的CookieStore实现.\n */\npublic enum DbCookieStore implements CookieStore {\n\n    INSTANCE;\n\n    private final DbManager db;\n    private final Executor trimExecutor = new PriorityExecutor(1, true);\n    private static final int LIMIT_COUNT = 5000; // 限制最多5000条数据\n\n    private long lastTrimTime = 0L;\n    private static final long TRIM_TIME_SPAN = 1000;\n\n    DbCookieStore() {\n        db = x.getDb(DbConfigs.COOKIE.getConfig());\n        try {\n            db.delete(CookieEntity.class, WhereBuilder.b(\"expiry\", \"=\", -1L));\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    /**\n     * Add one cookie into cookie store.\n     */\n    @Override\n    public void add(URI uri, HttpCookie cookie) {\n        if (cookie == null) {\n            return;\n        }\n\n        uri = getEffectiveURI(uri);\n\n        try {\n            db.replace(new CookieEntity(uri, cookie));\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n        trimSize();\n    }\n\n\n    /**\n     * Get all cookies, which:\n     * 1) given uri domain-matches with, or, associated with\n     * 2) given uri when added to the cookie store.\n     * 3) not expired.\n     * See RFC 2965 sec. 3.3.4 for more detail.\n     */\n    @Override\n    public List<HttpCookie> get(URI uri) {\n        // argument can't be null\n        if (uri == null) {\n            throw new NullPointerException(\"uri is null\");\n        }\n\n        uri = getEffectiveURI(uri);\n\n        List<HttpCookie> rt = new ArrayList<HttpCookie>();\n\n        try {\n\n            Selector<CookieEntity> selector = db.selector(CookieEntity.class);\n\n            WhereBuilder where = WhereBuilder.b();\n\n            String host = uri.getHost();\n            if (!TextUtils.isEmpty(host)) {\n                WhereBuilder subWhere = WhereBuilder.b(\"domain\", \"=\", host).or(\"domain\", \"=\", \".\" + host);\n                int firstDot = host.indexOf(\".\");\n                int lastDot = host.lastIndexOf(\".\");\n                if (firstDot > 0 && lastDot > firstDot) {\n                    String domain = host.substring(firstDot, host.length());\n                    if (!TextUtils.isEmpty(domain)) {\n                        subWhere.or(\"domain\", \"=\", domain);\n                    }\n                }\n                where.and(subWhere);\n            }\n\n            String path = uri.getPath();\n            if (!TextUtils.isEmpty(path)) {\n                WhereBuilder subWhere = WhereBuilder.b(\"path\", \"=\", path)\n                        .or(\"path\", \"=\", \"/\").or(\"path\", \"=\", null);\n                int lastSplit = path.lastIndexOf(\"/\");\n                while (lastSplit > 0) {\n                    path = path.substring(0, lastSplit);\n                    subWhere.or(\"path\", \"=\", path);\n                    lastSplit = path.lastIndexOf(\"/\");\n                }\n\n                where.and(subWhere);\n            }\n\n            where.or(\"uri\", \"=\", uri.toString());\n\n            List<CookieEntity> cookieEntityList = selector.where(where).findAll();\n            if (cookieEntityList != null) {\n                for (CookieEntity cookieEntity : cookieEntityList) {\n                    if (!cookieEntity.isExpired()) {\n                        rt.add(cookieEntity.toHttpCookie());\n                    }\n                }\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        return rt;\n    }\n\n    /**\n     * Get all cookies in cookie store, except those have expired\n     */\n    @Override\n    public List<HttpCookie> getCookies() {\n        List<HttpCookie> rt = new ArrayList<HttpCookie>();\n\n        try {\n            List<CookieEntity> cookieEntityList = db.findAll(CookieEntity.class);\n            if (cookieEntityList != null) {\n                for (CookieEntity cookieEntity : cookieEntityList) {\n                    if (!cookieEntity.isExpired()) {\n                        rt.add(cookieEntity.toHttpCookie());\n                    }\n                }\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n\n        return rt;\n    }\n\n    /**\n     * Get all URIs, which are associated with at least one cookie\n     * of this cookie store.\n     */\n    @Override\n    public List<URI> getURIs() {\n        List<URI> uris = new ArrayList<URI>();\n\n        try {\n            List<DbModel> uriList =\n                    db.selector(CookieEntity.class).select(\"uri\").findAll();\n            if (uriList != null) {\n                for (DbModel model : uriList) {\n                    String uri = model.getString(\"uri\");\n                    if (!TextUtils.isEmpty(uri)) {\n                        try {\n                            uris.add(new URI(uri));\n                        } catch (Throwable ex) {\n                            LogUtil.e(ex.getMessage(), ex);\n                            try {\n                                db.delete(CookieEntity.class, WhereBuilder.b(\"uri\", \"=\", uri));\n                            } catch (Throwable ignored) {\n                                LogUtil.e(ignored.getMessage(), ignored);\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n        return uris;\n    }\n\n\n    /**\n     * Remove a cookie from store\n     */\n    @Override\n    public boolean remove(URI uri, HttpCookie cookie) {\n        if (cookie == null) {\n            return true;\n        }\n\n        boolean modified = false;\n        try {\n            WhereBuilder where = WhereBuilder.b(\"name\", \"=\", cookie.getName());\n\n            String domain = cookie.getDomain();\n            if (!TextUtils.isEmpty(domain)) {\n                where.and(\"domain\", \"=\", domain);\n            }\n\n            String path = cookie.getPath();\n            if (!TextUtils.isEmpty(path)) {\n                if (path.length() > 1 && path.endsWith(\"/\")) {\n                    path = path.substring(0, path.length() - 1);\n                }\n                where.and(\"path\", \"=\", path);\n            }\n\n            db.delete(CookieEntity.class, where);\n\n            modified = true;\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n        return modified;\n    }\n\n\n    /**\n     * Remove all cookies in this cookie store.\n     */\n    @Override\n    public boolean removeAll() {\n        try {\n            db.delete(CookieEntity.class);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        return true;\n    }\n\n    private void trimSize() {\n        trimExecutor.execute(new Runnable() {\n            @Override\n            public void run() {\n\n                long current = System.currentTimeMillis();\n                if (current - lastTrimTime < TRIM_TIME_SPAN) {\n                    return;\n                } else {\n                    lastTrimTime = current;\n                }\n\n                // delete expires\n                try {\n                    db.delete(CookieEntity.class, WhereBuilder\n                            .b(\"expiry\", \"<\", System.currentTimeMillis())\n                            .and(\"expiry\", \"!=\", -1L));\n                } catch (Throwable ex) {\n                    LogUtil.e(ex.getMessage(), ex);\n                }\n\n                // trim by limit count\n                try {\n                    int count = (int) db.selector(CookieEntity.class).count();\n                    if (count > LIMIT_COUNT + 10) {\n                        List<CookieEntity> rmList = db.selector(CookieEntity.class)\n                                .where(\"expiry\", \"!=\", -1L).orderBy(\"expiry\", false)\n                                .limit(count - LIMIT_COUNT).findAll();\n                        if (rmList != null) {\n                            db.delete(rmList);\n                        }\n                    }\n                } catch (Throwable ex) {\n                    LogUtil.e(ex.getMessage(), ex);\n                }\n            }\n        });\n    }\n\n    private URI getEffectiveURI(final URI uri) {\n        URI effectiveURI = null;\n        try {\n            effectiveURI = new URI(\"http\",\n                    uri.getHost(),\n                    uri.getPath(),\n                    null,  // query component\n                    null   // fragment component\n            );\n        } catch (Throwable ignored) {\n            effectiveURI = uri;\n        }\n\n        return effectiveURI;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/BooleanLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\n/*package*/ class BooleanLoader extends Loader<Boolean> {\n\n    @Override\n    public Loader<Boolean> newInstance() {\n        return new BooleanLoader();\n    }\n\n    @Override\n    public Boolean load(final InputStream in) throws Throwable {\n        return false;\n    }\n\n    @Override\n    public Boolean load(final UriRequest request) throws Throwable {\n        request.sendRequest();\n        return request.getResponseCode() < 300;\n    }\n\n    @Override\n    public Boolean loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        return null;\n    }\n\n    @Override\n    public void save2Cache(final UriRequest request) {\n\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/ByteArrayLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\n/*package*/ class ByteArrayLoader extends Loader<byte[]> {\n\n    @Override\n    public Loader<byte[]> newInstance() {\n        return new ByteArrayLoader();\n    }\n\n    @Override\n    public byte[] load(final InputStream in) throws Throwable {\n        return IOUtil.readBytes(in);\n    }\n\n    @Override\n    public byte[] load(final UriRequest request) throws Throwable {\n        request.sendRequest();\n        return this.load(request.getInputStream());\n    }\n\n    @Override\n    public byte[] loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        return null;\n    }\n\n    @Override\n    public void save2Cache(final UriRequest request) {\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/FileLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport android.text.TextUtils;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.cache.DiskCacheFile;\nimport org.xutils.cache.LruDiskCache;\nimport org.xutils.common.Callback;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.common.util.ProcessLock;\nimport org.xutils.ex.FileLockedException;\nimport org.xutils.ex.HttpException;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.util.Arrays;\nimport java.util.Date;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n * 下载参数策略:\n * 1. RequestParams#saveFilePath不为空时, 目标文件保存在saveFilePath;\n * 否则由Cache策略分配文件下载路径.\n * 2. 下载时临时目标文件路径为tempSaveFilePath, 下载完后进行a: CacheFile#commit; b:重命名等操作.\n * 断点下载策略:\n * 1. 要下载的目标文件不存在或小于 CHECK_SIZE 时删除目标文件, 重新下载.\n * 2. 若文件存在且大于 CHECK_SIZE, range = fileLen - CHECK_SIZE , 校验check_buffer, 相同: 继续下载;\n * 不相同: 删掉目标文件, 并抛出RuntimeException(HttpRetryHandler会使下载重新开始).\n */\npublic class FileLoader extends Loader<File> {\n\n    private static final int CHECK_SIZE = 512;\n\n    private String tempSaveFilePath;\n    private String saveFilePath;\n    private boolean isAutoResume;\n    private boolean isAutoRename;\n    private long contentLength;\n    private String responseFileName;\n\n    private DiskCacheFile diskCacheFile;\n\n    @Override\n    public Loader<File> newInstance() {\n        return new FileLoader();\n    }\n\n    @Override\n    public void setParams(final RequestParams params) {\n        if (params != null) {\n            this.params = params;\n            isAutoResume = params.isAutoResume();\n            isAutoRename = params.isAutoRename();\n        }\n    }\n\n    @Override\n    public File load(final InputStream in) throws Throwable {\n        File targetFile = null;\n        BufferedInputStream bis = null;\n        BufferedOutputStream bos = null;\n        try {\n            targetFile = new File(tempSaveFilePath);\n            if (targetFile.isDirectory()) {\n                // 防止文件正在写入时, 父文件夹被删除, 继续写入时造成偶现文件节点异常问题.\n                IOUtil.deleteFileOrDir(targetFile);\n            }\n            if (!targetFile.exists()) {\n                File dir = targetFile.getParentFile();\n                if (dir.exists() || dir.mkdirs()) {\n                    targetFile.createNewFile();\n                }\n            }\n\n            // 处理[断点逻辑2](见文件头doc)\n            long targetFileLen = targetFile.length();\n            if (isAutoResume && targetFileLen > 0) {\n                FileInputStream fis = null;\n                try {\n                    long filePos = targetFileLen - CHECK_SIZE;\n                    if (filePos > 0) {\n                        fis = new FileInputStream(targetFile);\n                        byte[] fileCheckBuffer = IOUtil.readBytes(fis, filePos, CHECK_SIZE);\n                        byte[] checkBuffer = IOUtil.readBytes(in, 0, CHECK_SIZE);\n                        if (!Arrays.equals(checkBuffer, fileCheckBuffer)) {\n                            IOUtil.closeQuietly(fis); // 先关闭文件流, 否则文件删除会失败.\n                            IOUtil.deleteFileOrDir(targetFile);\n                            throw new RuntimeException(\"need retry\");\n                        } else {\n                            contentLength -= CHECK_SIZE;\n                        }\n                    } else {\n                        IOUtil.deleteFileOrDir(targetFile);\n                        throw new RuntimeException(\"need retry\");\n                    }\n                } finally {\n                    IOUtil.closeQuietly(fis);\n                }\n            }\n\n            // 开始下载\n            long current = 0;\n            FileOutputStream fileOutputStream = null;\n            if (isAutoResume) {\n                current = targetFileLen;\n                fileOutputStream = new FileOutputStream(targetFile, true);\n            } else {\n                fileOutputStream = new FileOutputStream(targetFile);\n            }\n\n            long total = contentLength + current;\n            bis = new BufferedInputStream(in);\n            bos = new BufferedOutputStream(fileOutputStream);\n\n            if (progressHandler != null && !progressHandler.updateProgress(total, current, true)) {\n                throw new Callback.CancelledException(\"download stopped!\");\n            }\n\n            byte[] tmp = new byte[4096];\n            int len;\n            while ((len = bis.read(tmp)) != -1) {\n\n                // 防止父文件夹被其他进程删除, 继续写入时造成父文件夹变为0字节文件的问题.\n                if (!targetFile.getParentFile().exists()) {\n                    targetFile.getParentFile().mkdirs();\n                    throw new IOException(\"parent be deleted!\");\n                }\n\n                bos.write(tmp, 0, len);\n                current += len;\n                if (progressHandler != null) {\n                    if (!progressHandler.updateProgress(total, current, false)) {\n                        bos.flush();\n                        throw new Callback.CancelledException(\"download stopped!\");\n                    }\n                }\n            }\n            bos.flush();\n            // 处理[下载逻辑2.a](见文件头doc)\n            if (diskCacheFile != null) {\n                targetFile = diskCacheFile.commit();\n            }\n\n            if (progressHandler != null) {\n                progressHandler.updateProgress(total, current, true);\n            }\n        } finally {\n            IOUtil.closeQuietly(bis);\n            IOUtil.closeQuietly(bos);\n        }\n\n        return autoRename(targetFile);\n    }\n\n    @Override\n    public File load(final UriRequest request) throws Throwable {\n        ProcessLock processLock = null;\n        File result = null;\n        try {\n\n            // 处理[下载逻辑1](见文件头doc)\n            saveFilePath = params.getSaveFilePath();\n            diskCacheFile = null;\n            if (TextUtils.isEmpty(saveFilePath)) {\n\n                if (progressHandler != null && !progressHandler.updateProgress(0, 0, false)) {\n                    throw new Callback.CancelledException(\"download stopped!\");\n                }\n\n                // 保存路径为空, 存入磁盘缓存.\n                initDiskCacheFile(request);\n            } else {\n                tempSaveFilePath = saveFilePath + \".tmp\";\n            }\n\n            if (progressHandler != null && !progressHandler.updateProgress(0, 0, false)) {\n                throw new Callback.CancelledException(\"download stopped!\");\n            }\n\n            // 等待, 若不能下载则取消此次下载.\n            processLock = ProcessLock.tryLock(saveFilePath + \"_lock\", true);\n            if (processLock == null || !processLock.isValid()) {\n                throw new FileLockedException(\"download exists: \" + saveFilePath);\n            }\n\n            params = request.getParams();\n            {// 处理[断点逻辑1](见文件头doc)\n                long range = 0;\n                if (isAutoResume) {\n                    File tempFile = new File(tempSaveFilePath);\n                    long fileLen = tempFile.length();\n                    if (fileLen <= CHECK_SIZE) {\n                        IOUtil.deleteFileOrDir(tempFile);\n                        range = 0;\n                    } else {\n                        range = fileLen - CHECK_SIZE;\n                    }\n                }\n                // retry 时需要覆盖RANGE参数\n                params.setHeader(\"RANGE\", \"bytes=\" + range + \"-\");\n            }\n\n            if (progressHandler != null && !progressHandler.updateProgress(0, 0, false)) {\n                throw new Callback.CancelledException(\"download stopped!\");\n            }\n\n            request.sendRequest(); // may be throw an HttpException\n\n            contentLength = request.getContentLength();\n            if (isAutoRename) {\n                responseFileName = getResponseFileName(request);\n            }\n            if (isAutoResume) {\n                isAutoResume = isSupportRange(request);\n            }\n\n            if (progressHandler != null && !progressHandler.updateProgress(0, 0, false)) {\n                throw new Callback.CancelledException(\"download stopped!\");\n            }\n\n            if (diskCacheFile != null) {\n                DiskCacheEntity entity = diskCacheFile.getCacheEntity();\n                entity.setLastAccess(System.currentTimeMillis());\n                entity.setEtag(request.getETag());\n                entity.setExpires(request.getExpiration());\n                entity.setLastModify(new Date(request.getLastModified()));\n            }\n            result = this.load(request.getInputStream());\n        } catch (HttpException httpException) {\n            if (httpException.getCode() == 416) {\n                if (diskCacheFile != null) {\n                    result = diskCacheFile.commit();\n                } else {\n                    result = new File(tempSaveFilePath);\n                }\n                // 从缓存获取文件, 不rename和断点, 直接退出.\n                if (result != null && result.exists()) {\n                    if (isAutoRename) {\n                        responseFileName = getResponseFileName(request);\n                    }\n                    result = autoRename(result);\n                } else {\n                    IOUtil.deleteFileOrDir(result);\n                    throw new IllegalStateException(\"cache file not found\" + request.getCacheKey());\n                }\n            } else {\n                throw httpException;\n            }\n        } finally {\n            IOUtil.closeQuietly(processLock);\n            IOUtil.closeQuietly(diskCacheFile);\n        }\n        return result;\n    }\n\n    // 保存路径为空, 存入磁盘缓存.\n    private void initDiskCacheFile(final UriRequest request) throws Throwable {\n\n        DiskCacheEntity entity = new DiskCacheEntity();\n        entity.setKey(request.getCacheKey());\n        diskCacheFile = LruDiskCache.getDiskCache(params.getCacheDirName()).createDiskCacheFile(entity);\n\n        if (diskCacheFile != null) {\n            saveFilePath = diskCacheFile.getAbsolutePath();\n            // diskCacheFile is a temp path, diskCacheFile.commit() return the dest file.\n            tempSaveFilePath = saveFilePath;\n            isAutoRename = false;\n        } else {\n            throw new IOException(\"create cache file error:\" + request.getCacheKey());\n        }\n    }\n\n    // 处理[下载逻辑2.b](见文件头doc)\n    private File autoRename(File loadedFile) {\n        if (isAutoRename && loadedFile.exists() && !TextUtils.isEmpty(responseFileName)) {\n            File newFile = new File(loadedFile.getParent(), responseFileName);\n            while (newFile.exists()) {\n                newFile = new File(loadedFile.getParent(), System.currentTimeMillis() + responseFileName);\n            }\n            return loadedFile.renameTo(newFile) ? newFile : loadedFile;\n        } else if (!saveFilePath.equals(tempSaveFilePath)) {\n            File newFile = new File(saveFilePath);\n            return loadedFile.renameTo(newFile) ? newFile : loadedFile;\n        } else {\n            return loadedFile;\n        }\n    }\n\n    private static String getResponseFileName(UriRequest request) {\n        if (request == null) return null;\n        String disposition = request.getResponseHeader(\"Content-Disposition\");\n        if (!TextUtils.isEmpty(disposition)) {\n            int startIndex = disposition.indexOf(\"filename=\");\n            if (startIndex > 0) {\n                startIndex += 9; // \"filename=\".length()\n                int endIndex = disposition.indexOf(\";\", startIndex);\n                if (endIndex < 0) {\n                    endIndex = disposition.length();\n                }\n                if (endIndex > startIndex) {\n                    try {\n                        String name = URLDecoder.decode(\n                                disposition.substring(startIndex, endIndex),\n                                request.getParams().getCharset());\n                        if (name.startsWith(\"\\\"\") && name.endsWith(\"\\\"\")) {\n                            name = name.substring(1, name.length() - 1);\n                        }\n                        return name;\n                    } catch (UnsupportedEncodingException ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }\n        }\n        return null;\n    }\n\n    private static boolean isSupportRange(UriRequest request) {\n        if (request == null) return false;\n        String ranges = request.getResponseHeader(\"Accept-Ranges\");\n        if (ranges != null) {\n            return ranges.contains(\"bytes\");\n        }\n        ranges = request.getResponseHeader(\"Content-Range\");\n        return ranges != null && ranges.contains(\"bytes\");\n    }\n\n    @Override\n    public File loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        return LruDiskCache.getDiskCache(params.getCacheDirName()).getDiskCacheFile(cacheEntity.getKey());\n    }\n\n    @Override\n    public void save2Cache(final UriRequest request) {\n        // already saved by diskCacheFile#commit\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/IntegerLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\n\n/**\n * @author: wyouflf\n * @date: 2014/10/17\n */\n/*package*/ class IntegerLoader extends Loader<Integer> {\n    @Override\n    public Loader<Integer> newInstance() {\n        return new IntegerLoader();\n    }\n\n    @Override\n    public Integer load(InputStream in) throws Throwable {\n        return 100;\n    }\n\n    @Override\n    public Integer load(UriRequest request) throws Throwable {\n        request.sendRequest();\n        return request.getResponseCode();\n    }\n\n    @Override\n    public Integer loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        return null;\n    }\n\n    @Override\n    public void save2Cache(UriRequest request) {\n\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/JSONArrayLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport android.text.TextUtils;\n\nimport org.json.JSONArray;\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\n\n/**\n * Author: wyouflf\n * Time: 2014/06/16\n */\n/*package*/ class JSONArrayLoader extends Loader<JSONArray> {\n\n    private String charset = \"UTF-8\";\n    private String resultStr = null;\n\n    @Override\n    public Loader<JSONArray> newInstance() {\n        return new JSONArrayLoader();\n    }\n\n    @Override\n    public void setParams(final RequestParams params) {\n        if (params != null) {\n            String charset = params.getCharset();\n            if (!TextUtils.isEmpty(charset)) {\n                this.charset = charset;\n            }\n        }\n    }\n\n    @Override\n    public JSONArray load(final InputStream in) throws Throwable {\n        resultStr = IOUtil.readStr(in, charset);\n        return new JSONArray(resultStr);\n    }\n\n    @Override\n    public JSONArray load(final UriRequest request) throws Throwable {\n        request.sendRequest();\n        return this.load(request.getInputStream());\n    }\n\n    @Override\n    public JSONArray loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        if (cacheEntity != null) {\n            String text = cacheEntity.getTextContent();\n            if (!TextUtils.isEmpty(text)) {\n                return new JSONArray(text);\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public void save2Cache(UriRequest request) {\n        saveStringCache(request, resultStr);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/JSONObjectLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport android.text.TextUtils;\n\nimport org.json.JSONObject;\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\n\n/**\n * Author: wyouflf\n * Time: 2014/06/16\n */\n/*package*/ class JSONObjectLoader extends Loader<JSONObject> {\n\n    private String charset = \"UTF-8\";\n    private String resultStr = null;\n\n    @Override\n    public Loader<JSONObject> newInstance() {\n        return new JSONObjectLoader();\n    }\n\n    @Override\n    public void setParams(final RequestParams params) {\n        if (params != null) {\n            String charset = params.getCharset();\n            if (!TextUtils.isEmpty(charset)) {\n                this.charset = charset;\n            }\n        }\n    }\n\n    @Override\n    public JSONObject load(final InputStream in) throws Throwable {\n        resultStr = IOUtil.readStr(in, charset);\n        return new JSONObject(resultStr);\n    }\n\n    @Override\n    public JSONObject load(final UriRequest request) throws Throwable {\n        request.sendRequest();\n        return this.load(request.getInputStream());\n    }\n\n    @Override\n    public JSONObject loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        if (cacheEntity != null) {\n            String text = cacheEntity.getTextContent();\n            if (!TextUtils.isEmpty(text)) {\n                return new JSONObject(text);\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public void save2Cache(UriRequest request) {\n        saveStringCache(request, resultStr);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/Loader.java",
    "content": "package org.xutils.http.loader;\n\n\nimport android.text.TextUtils;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.cache.LruDiskCache;\nimport org.xutils.http.ProgressHandler;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\nimport java.util.Date;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/26\n */\npublic abstract class Loader<T> {\n\n    protected RequestParams params;\n    protected ProgressHandler progressHandler;\n\n    public void setParams(final RequestParams params) {\n        this.params = params;\n    }\n\n    public void setProgressHandler(final ProgressHandler callbackHandler) {\n        this.progressHandler = callbackHandler;\n    }\n\n    protected void saveStringCache(UriRequest request, String resultStr) {\n        if (!TextUtils.isEmpty(resultStr)) {\n            DiskCacheEntity entity = new DiskCacheEntity();\n            entity.setKey(request.getCacheKey());\n            entity.setLastAccess(System.currentTimeMillis());\n            entity.setEtag(request.getETag());\n            entity.setExpires(request.getExpiration());\n            entity.setLastModify(new Date(request.getLastModified()));\n            entity.setTextContent(resultStr);\n            LruDiskCache.getDiskCache(request.getParams().getCacheDirName()).put(entity);\n        }\n    }\n\n    public abstract Loader<T> newInstance();\n\n    public abstract T load(final InputStream in) throws Throwable;\n\n    public abstract T load(final UriRequest request) throws Throwable;\n\n    public abstract T loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable;\n\n    public abstract void save2Cache(final UriRequest request);\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/LoaderFactory.java",
    "content": "package org.xutils.http.loader;\n\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\nimport org.xutils.http.RequestParams;\n\nimport java.io.File;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/26\n */\npublic final class LoaderFactory {\n\n    private LoaderFactory() {\n    }\n\n    /**\n     * key: loadType\n     */\n    private static final HashMap<Type, Loader> converterHashMap = new HashMap<Type, Loader>();\n\n    static {\n        converterHashMap.put(JSONObject.class, new JSONObjectLoader());\n        converterHashMap.put(JSONArray.class, new JSONArrayLoader());\n        converterHashMap.put(String.class, new StringLoader());\n        converterHashMap.put(File.class, new FileLoader());\n        converterHashMap.put(byte[].class, new ByteArrayLoader());\n        BooleanLoader booleanLoader = new BooleanLoader();\n        converterHashMap.put(boolean.class, booleanLoader);\n        converterHashMap.put(Boolean.class, booleanLoader);\n        IntegerLoader integerLoader = new IntegerLoader();\n        converterHashMap.put(int.class, integerLoader);\n        converterHashMap.put(Integer.class, integerLoader);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static Loader<?> getLoader(Type type, RequestParams params) {\n        Loader<?> result = converterHashMap.get(type);\n        if (result == null) {\n            result = new ObjectLoader(type);\n        } else {\n            result = result.newInstance();\n        }\n        result.setParams(params);\n        return result;\n    }\n\n    public static <T> void registerLoader(Type type, Loader<T> loader) {\n        converterHashMap.put(type, loader);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/ObjectLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport android.text.TextUtils;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.ParameterizedTypeUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.annotation.HttpResponse;\nimport org.xutils.http.app.InputStreamResponseParser;\nimport org.xutils.http.app.ResponseParser;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.lang.reflect.TypeVariable;\nimport java.util.List;\n\n/**\n * Created by lei.jiao on 2014/6/27.\n * 其他对象的下载转换.\n * 使用类型上的@HttpResponse注解信息进行数据转换.\n */\n/*package*/ class ObjectLoader extends Loader<Object> {\n\n    private String charset = \"UTF-8\";\n    private String resultStr = null;\n\n    private final Type objectType;\n    private final Class<?> objectClass;\n    private final ResponseParser parser;\n\n    public ObjectLoader(Type objectType) {\n        this.objectType = objectType;\n\n        // check loadType & resultType\n        {\n            if (objectType instanceof ParameterizedType) {\n                objectClass = (Class<?>) ((ParameterizedType) objectType).getRawType();\n            } else if (objectType instanceof TypeVariable) {\n                throw new IllegalArgumentException(\n                        \"not support callback type \" + objectType.toString());\n            } else {\n                objectClass = (Class<?>) objectType;\n            }\n        }\n\n        if (List.class.equals(objectClass)) {\n            Type itemType = ParameterizedTypeUtil.getParameterizedType(this.objectType, List.class, 0);\n            Class<?> itemClass = null;\n            if (itemType instanceof ParameterizedType) {\n                itemClass = (Class<?>) ((ParameterizedType) itemType).getRawType();\n            } else if (itemType instanceof TypeVariable) {\n                throw new IllegalArgumentException(\n                        \"not support callback type \" + itemType.toString());\n            } else {\n                itemClass = (Class<?>) itemType;\n            }\n\n            HttpResponse response = itemClass.getAnnotation(HttpResponse.class);\n            if (response != null) {\n                try {\n                    this.parser = response.parser().newInstance();\n                } catch (Throwable ex) {\n                    throw new RuntimeException(\"create parser error\", ex);\n                }\n            } else {\n                throw new IllegalArgumentException(\"not found @HttpResponse from \" + itemType);\n            }\n        } else {\n            HttpResponse response = objectClass.getAnnotation(HttpResponse.class);\n            if (response != null) {\n                try {\n                    this.parser = response.parser().newInstance();\n                } catch (Throwable ex) {\n                    throw new RuntimeException(\"create parser error\", ex);\n                }\n            } else {\n                throw new IllegalArgumentException(\"not found @HttpResponse from \" + this.objectType);\n            }\n        }\n    }\n\n    @Override\n    public Loader<Object> newInstance() {\n        throw new IllegalAccessError(\"use constructor create ObjectLoader.\");\n    }\n\n    @Override\n    public void setParams(final RequestParams params) {\n        if (params != null) {\n            String charset = params.getCharset();\n            if (!TextUtils.isEmpty(charset)) {\n                this.charset = charset;\n            }\n        }\n    }\n\n    @Override\n    public Object load(final InputStream in) throws Throwable {\n        Object result;\n        if (parser instanceof InputStreamResponseParser) {\n            result = ((InputStreamResponseParser) parser).parse(objectType, objectClass, in);\n        } else {\n            resultStr = IOUtil.readStr(in, charset);\n            result = parser.parse(objectType, objectClass, resultStr);\n        }\n        return result;\n    }\n\n    @Override\n    public Object load(final UriRequest request) throws Throwable {\n        try {\n            request.sendRequest();\n        } finally {\n            parser.checkResponse(request);\n        }\n        return this.load(request.getInputStream());\n    }\n\n    @Override\n    public Object loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        if (cacheEntity != null) {\n            String text = cacheEntity.getTextContent();\n            if (!TextUtils.isEmpty(text)) {\n                return parser.parse(objectType, objectClass, text);\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public void save2Cache(UriRequest request) {\n        saveStringCache(request, resultStr);\n    }\n}"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/loader/StringLoader.java",
    "content": "package org.xutils.http.loader;\n\nimport android.text.TextUtils;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.request.UriRequest;\n\nimport java.io.InputStream;\n\n/**\n * Author: wyouflf\n * Time: 2014/05/30\n */\n/*package*/ class StringLoader extends Loader<String> {\n\n    private String charset = \"UTF-8\";\n    private String resultStr = null;\n\n    @Override\n    public Loader<String> newInstance() {\n        return new StringLoader();\n    }\n\n    @Override\n    public void setParams(final RequestParams params) {\n        if (params != null) {\n            String charset = params.getCharset();\n            if (!TextUtils.isEmpty(charset)) {\n                this.charset = charset;\n            }\n        }\n    }\n\n    @Override\n    public String load(final InputStream in) throws Throwable {\n        resultStr = IOUtil.readStr(in, charset);\n        return resultStr;\n    }\n\n    @Override\n    public String load(final UriRequest request) throws Throwable {\n        request.sendRequest();\n        return this.load(request.getInputStream());\n    }\n\n    @Override\n    public String loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {\n        if (cacheEntity != null) {\n            return cacheEntity.getTextContent();\n        }\n\n        return null;\n    }\n\n    @Override\n    public void save2Cache(UriRequest request) {\n        saveStringCache(request, resultStr);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/request/AssetsRequest.java",
    "content": "package org.xutils.http.request;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.cache.LruDiskCache;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Type;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by wyouflf on 15/11/4.\n * Assets资源文件请求\n */\npublic class AssetsRequest extends UriRequest {\n\n    private long contentLength = 0;\n    private InputStream inputStream;\n\n    public AssetsRequest(RequestParams params, Type loadType) throws Throwable {\n        super(params, loadType);\n    }\n\n    @Override\n    public void sendRequest() throws Throwable {\n\n    }\n\n    @Override\n    public boolean isLoading() {\n        return true;\n    }\n\n    @Override\n    public String getCacheKey() {\n        return queryUrl;\n    }\n\n    @Override\n    public Object loadResult() throws Throwable {\n        return this.loader.load(this);\n    }\n\n    @Override\n    public Object loadResultFromCache() throws Throwable {\n        DiskCacheEntity cacheEntity = LruDiskCache.getDiskCache(params.getCacheDirName())\n                .setMaxSize(params.getCacheSize())\n                .get(this.getCacheKey());\n\n        if (cacheEntity != null) {\n            Date lastModifiedDate = cacheEntity.getLastModify();\n            if (lastModifiedDate == null || lastModifiedDate.getTime() < getAssetsLastModified()) {\n                return null;\n            }\n            return loader.loadFromCache(cacheEntity);\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public void clearCacheHeader() {\n\n    }\n\n    @Override\n    public InputStream getInputStream() throws IOException {\n        if (inputStream == null) {\n            if (callingClassLoader != null) {\n                String assetsPath = \"assets/\" + queryUrl.substring(\"assets://\".length());\n                inputStream = callingClassLoader.getResourceAsStream(assetsPath);\n                contentLength = inputStream.available();\n            }\n        }\n        return inputStream;\n    }\n\n    @Override\n    public void close() throws IOException {\n        IOUtil.closeQuietly(inputStream);\n        inputStream = null;\n    }\n\n    @Override\n    public long getContentLength() {\n        try {\n            getInputStream();\n            return contentLength;\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        return 0;\n    }\n\n    @Override\n    public int getResponseCode() throws IOException {\n        return getInputStream() != null ? 200 : 404;\n    }\n\n    @Override\n    public String getResponseMessage() throws IOException {\n        return null;\n    }\n\n    @Override\n    public long getExpiration() {\n        return Long.MAX_VALUE;\n    }\n\n    @Override\n    public long getLastModified() {\n        return getAssetsLastModified();\n    }\n\n    @Override\n    public String getETag() {\n        return null;\n    }\n\n    @Override\n    public String getResponseHeader(String name) {\n        return null;\n    }\n\n    @Override\n    public Map<String, List<String>> getResponseHeaders() {\n        return null;\n    }\n\n    @Override\n    public long getHeaderFieldDate(String name, long defaultValue) {\n        return defaultValue;\n    }\n\n    /**\n     * 如果你的应用基于插件架构, 并且插件有独立的资源管理实现, 可能需要覆盖这里的实现方式,\n     * 并在UriRequestFactory中注册你的实现.\n     */\n    protected long getAssetsLastModified() {\n        return new File(x.app().getApplicationInfo().sourceDir).lastModified();\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/request/HttpRequest.java",
    "content": "package org.xutils.http.request;\n\nimport android.annotation.TargetApi;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.text.TextUtils;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.cache.LruDiskCache;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.KeyValue;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.ex.HttpException;\nimport org.xutils.http.HttpMethod;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.body.ProgressBody;\nimport org.xutils.http.body.RequestBody;\nimport org.xutils.http.cookie.DbCookieStore;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Type;\nimport java.net.CookieManager;\nimport java.net.CookiePolicy;\nimport java.net.HttpURLConnection;\nimport java.net.ProtocolException;\nimport java.net.Proxy;\nimport java.net.URL;\nimport java.net.URLDecoder;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.StringTokenizer;\nimport java.util.TimeZone;\n\nimport javax.net.ssl.HttpsURLConnection;\nimport javax.net.ssl.SSLSocketFactory;\n\n/**\n * Created by wyouflf on 15/7/23.\n * Uri请求发送和数据接收\n */\npublic class HttpRequest extends UriRequest {\n\n    private String cacheKey = null;\n    private boolean isLoading = false;\n    private InputStream inputStream = null;\n    private HttpURLConnection connection = null;\n    private int responseCode = 0;\n\n    // cookie manager\n    private static final CookieManager COOKIE_MANAGER =\n            new CookieManager(DbCookieStore.INSTANCE, CookiePolicy.ACCEPT_ALL);\n\n    /*package*/ HttpRequest(RequestParams params, Type loadType) throws Throwable {\n        super(params, loadType);\n    }\n\n    // build query\n    @Override\n    protected String buildQueryUrl(RequestParams params) {\n        String uri = params.getUri();\n        StringBuilder queryBuilder = new StringBuilder(uri);\n        if (!uri.contains(\"?\")) {\n            queryBuilder.append(\"?\");\n        } else if (!uri.endsWith(\"?\")) {\n            queryBuilder.append(\"&\");\n        }\n        List<KeyValue> queryParams = params.getQueryStringParams();\n        if (queryParams != null) {\n            for (KeyValue kv : queryParams) {\n                String name = kv.key;\n                String value = kv.getValueStr();\n                if (!TextUtils.isEmpty(name) && value != null) {\n                    queryBuilder.append(\n                            Uri.encode(name, params.getCharset()))\n                            .append(\"=\")\n                            .append(Uri.encode(value, params.getCharset()))\n                            .append(\"&\");\n                }\n            }\n        }\n\n        if (queryBuilder.charAt(queryBuilder.length() - 1) == '&') {\n            queryBuilder.deleteCharAt(queryBuilder.length() - 1);\n        }\n\n        if (queryBuilder.charAt(queryBuilder.length() - 1) == '?') {\n            queryBuilder.deleteCharAt(queryBuilder.length() - 1);\n        }\n        return queryBuilder.toString();\n    }\n\n    @Override\n    public String getRequestUri() {\n        String result = queryUrl;\n        if (connection != null) {\n            URL url = connection.getURL();\n            if (url != null) {\n                result = url.toString();\n            }\n        }\n        return result;\n    }\n\n    /**\n     * invoke via Loader\n     *\n     * @throws IOException\n     */\n    @Override\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    public void sendRequest() throws Throwable {\n        isLoading = false;\n        responseCode = 0;\n\n        URL url = new URL(queryUrl);\n        { // init connection\n            Proxy proxy = params.getProxy();\n            if (proxy != null) {\n                connection = (HttpURLConnection) url.openConnection(proxy);\n            } else {\n                connection = (HttpURLConnection) url.openConnection();\n            }\n\n            // try to fix bug: accidental EOFException before API 19\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n                connection.setRequestProperty(\"Connection\", \"close\");\n            }\n\n            connection.setReadTimeout(params.getConnectTimeout());\n            connection.setConnectTimeout(params.getConnectTimeout());\n            connection.setInstanceFollowRedirects(params.getRedirectHandler() == null);\n            if (connection instanceof HttpsURLConnection) {\n                SSLSocketFactory sslSocketFactory = params.getSslSocketFactory();\n                if (sslSocketFactory != null) {\n                    ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);\n                }\n            }\n        }\n\n        if (params.isUseCookie()) {// add cookies\n            try {\n                Map<String, List<String>> singleMap =\n                        COOKIE_MANAGER.get(url.toURI(), new HashMap<String, List<String>>(0));\n                List<String> cookies = singleMap.get(\"Cookie\");\n                if (cookies != null) {\n                    connection.setRequestProperty(\"Cookie\", TextUtils.join(\";\", cookies));\n                }\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n        }\n\n        {// add headers\n            List<RequestParams.Header> headers = params.getHeaders();\n            if (headers != null) {\n                for (RequestParams.Header header : headers) {\n                    String name = header.key;\n                    String value = header.getValueStr();\n                    if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {\n                        if (header.setHeader) {\n                            connection.setRequestProperty(name, value);\n                        } else {\n                            connection.addRequestProperty(name, value);\n                        }\n                    }\n                }\n            }\n        }\n\n        // intercept response\n        if (requestInterceptListener != null) {\n            requestInterceptListener.beforeRequest(this);\n        }\n\n        { // write body\n            HttpMethod method = params.getMethod();\n            try {\n                connection.setRequestMethod(method.toString());\n            } catch (ProtocolException ex) {\n                try { // fix: HttpURLConnection not support PATCH method.\n                    Field methodField = HttpURLConnection.class.getDeclaredField(\"method\");\n                    methodField.setAccessible(true);\n                    methodField.set(connection, method.toString());\n                } catch (Throwable ignored) {\n                    throw ex;\n                }\n            }\n            if (HttpMethod.permitsRequestBody(method)) {\n                RequestBody body = params.getRequestBody();\n                if (body != null) {\n                    if (body instanceof ProgressBody) {\n                        ((ProgressBody) body).setProgressHandler(progressHandler);\n                    }\n                    String contentType = body.getContentType();\n                    if (!TextUtils.isEmpty(contentType)) {\n                        connection.setRequestProperty(\"Content-Type\", contentType);\n                    }\n                    long contentLength = body.getContentLength();\n                    if (contentLength < 0) {\n                        connection.setChunkedStreamingMode(256 * 1024);\n                    } else {\n                        if (contentLength < Integer.MAX_VALUE) {\n                            connection.setFixedLengthStreamingMode((int) contentLength);\n                        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n                            connection.setFixedLengthStreamingMode(contentLength);\n                        } else {\n                            connection.setChunkedStreamingMode(256 * 1024);\n                        }\n                    }\n                    connection.setRequestProperty(\"Content-Length\", String.valueOf(contentLength));\n                    connection.setDoOutput(true);\n                    body.writeTo(connection.getOutputStream());\n                }\n            }\n        }\n\n        if (params.isUseCookie()) { // save cookies\n            try {\n                Map<String, List<String>> headers = connection.getHeaderFields();\n                if (headers != null) {\n                    COOKIE_MANAGER.put(url.toURI(), headers);\n                }\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n        }\n\n        // check response code\n        responseCode = connection.getResponseCode();\n        // intercept response\n        if (requestInterceptListener != null) {\n            requestInterceptListener.afterRequest(this);\n        }\n        if (responseCode == 204 || responseCode == 205) { // empty content\n            throw new HttpException(responseCode, this.getResponseMessage());\n        } else if (responseCode >= 300) {\n            HttpException httpException = new HttpException(responseCode, this.getResponseMessage());\n            try {\n                httpException.setResult(IOUtil.readStr(this.getInputStream(), params.getCharset()));\n            } catch (Throwable ignored) {\n            }\n            LogUtil.e(httpException.toString() + \", url: \" + queryUrl);\n            throw httpException;\n        }\n\n        isLoading = true;\n    }\n\n    @Override\n    public boolean isLoading() {\n        return isLoading;\n    }\n\n    @Override\n    public String getCacheKey() {\n        if (cacheKey == null) {\n\n            cacheKey = params.getCacheKey();\n\n            if (TextUtils.isEmpty(cacheKey)) {\n                cacheKey = params.toString();\n            }\n        }\n        return cacheKey;\n    }\n\n    @Override\n    public Object loadResult() throws Throwable {\n        isLoading = true;\n        return super.loadResult();\n    }\n\n    /**\n     * 尝试从缓存获取结果, 并为请求头加入缓存控制参数.\n     *\n     * @return\n     * @throws Throwable\n     */\n    @Override\n    public Object loadResultFromCache() throws Throwable {\n        isLoading = true;\n        DiskCacheEntity cacheEntity = LruDiskCache.getDiskCache(params.getCacheDirName())\n                .setMaxSize(params.getCacheSize())\n                .get(this.getCacheKey());\n\n        if (cacheEntity != null) {\n            if (HttpMethod.permitsCache(params.getMethod())) {\n                Date lastModified = cacheEntity.getLastModify();\n                if (lastModified.getTime() > 0) {\n                    params.setHeader(\"If-Modified-Since\", toGMTString(lastModified));\n                }\n                String eTag = cacheEntity.getEtag();\n                if (!TextUtils.isEmpty(eTag)) {\n                    params.setHeader(\"If-None-Match\", eTag);\n                }\n            }\n            return loader.loadFromCache(cacheEntity);\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public void clearCacheHeader() {\n        params.setHeader(\"If-Modified-Since\", null);\n        params.setHeader(\"If-None-Match\", null);\n    }\n\n    @Override\n    public InputStream getInputStream() throws IOException {\n        if (connection != null && inputStream == null) {\n            inputStream = connection.getResponseCode() >= 400 ?\n                    connection.getErrorStream() : connection.getInputStream();\n        }\n        return inputStream;\n    }\n\n    @Override\n    public void close() throws IOException {\n        if (inputStream != null) {\n            IOUtil.closeQuietly(inputStream);\n            inputStream = null;\n        }\n        if (connection != null) {\n            connection.disconnect();\n            //connection = null;\n        }\n    }\n\n    @Override\n    public long getContentLength() {\n        long result = 0;\n        if (connection != null) {\n            try {\n                result = connection.getContentLength();\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n            if (result < 1) {\n                try {\n                    result = this.getInputStream().available();\n                } catch (Throwable ignored) {\n                }\n            }\n        } else {\n            try {\n                result = this.getInputStream().available();\n            } catch (Throwable ignored) {\n            }\n        }\n        return result;\n    }\n\n    @Override\n    public int getResponseCode() throws IOException {\n        if (connection != null) {\n            return responseCode;\n        } else {\n            if (this.getInputStream() != null) {\n                return 200;\n            } else {\n                return 404;\n            }\n        }\n    }\n\n    @Override\n    public String getResponseMessage() throws IOException {\n        if (connection != null) {\n            return URLDecoder.decode(connection.getResponseMessage(), params.getCharset());\n        } else {\n            return null;\n        }\n    }\n\n    @Override\n    public long getExpiration() {\n        if (connection == null) return -1L;\n\n        long expiration = -1L;\n\n        // from max-age\n        String cacheControl = connection.getHeaderField(\"Cache-Control\");\n        if (!TextUtils.isEmpty(cacheControl)) {\n            StringTokenizer tok = new StringTokenizer(cacheControl, \",\");\n            while (tok.hasMoreTokens()) {\n                String token = tok.nextToken().trim().toLowerCase();\n                if (token.startsWith(\"max-age\")) {\n                    int eqIdx = token.indexOf('=');\n                    if (eqIdx > 0) {\n                        try {\n                            String value = token.substring(eqIdx + 1).trim();\n                            long seconds = Long.parseLong(value);\n                            if (seconds > 0L) {\n                                expiration = System.currentTimeMillis() + seconds * 1000L;\n                            }\n                        } catch (Throwable ex) {\n                            LogUtil.e(ex.getMessage(), ex);\n                        }\n                    }\n                    break;\n                }\n            }\n        }\n\n        // from expires\n        if (expiration <= 0L) {\n            expiration = connection.getExpiration();\n        }\n\n        if (expiration <= 0L && params.getCacheMaxAge() > 0L) {\n            expiration = System.currentTimeMillis() + params.getCacheMaxAge();\n        }\n\n        if (expiration <= 0L) {\n            expiration = Long.MAX_VALUE;\n        }\n        return expiration;\n    }\n\n    @Override\n    public long getLastModified() {\n        return getHeaderFieldDate(\"Last-Modified\", System.currentTimeMillis());\n    }\n\n    @Override\n    public String getETag() {\n        if (connection == null) return null;\n        return connection.getHeaderField(\"ETag\");\n    }\n\n    @Override\n    public String getResponseHeader(String name) {\n        if (connection == null) return null;\n        return connection.getHeaderField(name);\n    }\n\n    @Override\n    public Map<String, List<String>> getResponseHeaders() {\n        if (connection == null) return null;\n        return connection.getHeaderFields();\n    }\n\n    @Override\n    public long getHeaderFieldDate(String name, long defaultValue) {\n        if (connection == null) return defaultValue;\n        return connection.getHeaderFieldDate(name, defaultValue);\n    }\n\n    private static String toGMTString(Date date) {\n        SimpleDateFormat sdf = new SimpleDateFormat(\n                \"EEE, dd MMM y HH:mm:ss 'GMT'\", Locale.US);\n        TimeZone gmtZone = TimeZone.getTimeZone(\"GMT\");\n        sdf.setTimeZone(gmtZone);\n        GregorianCalendar gc = new GregorianCalendar(gmtZone);\n        gc.setTimeInMillis(date.getTime());\n        return sdf.format(date);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/request/LocalFileRequest.java",
    "content": "package org.xutils.http.request;\n\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.loader.FileLoader;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by wyouflf on 15/11/4.\n * 本地文件请求\n */\npublic class LocalFileRequest extends UriRequest {\n\n    private InputStream inputStream;\n\n    LocalFileRequest(RequestParams params, Type loadType) throws Throwable {\n        super(params, loadType);\n    }\n\n    @Override\n    public void sendRequest() throws Throwable {\n\n    }\n\n    @Override\n    public boolean isLoading() {\n        return true;\n    }\n\n    @Override\n    public String getCacheKey() {\n        return null;\n    }\n\n    @Override\n    public Object loadResult() throws Throwable {\n        if (loader instanceof FileLoader) {\n            return getFile();\n        }\n        return this.loader.load(this);\n    }\n\n    @Override\n    public Object loadResultFromCache() throws Throwable {\n        return null;\n    }\n\n    @Override\n    public void clearCacheHeader() {\n\n    }\n\n    @Override\n    public void save2Cache() {\n\n    }\n\n    private File getFile() {\n        String filePath = null;\n        if (queryUrl.startsWith(\"file:\")) {\n            filePath = queryUrl.substring(\"file:\".length());\n        } else {\n            filePath = queryUrl;\n        }\n        // filePath开始位置多余的\"/\"或被自动去掉\n        return new File(filePath);\n    }\n\n    @Override\n    public InputStream getInputStream() throws IOException {\n        if (inputStream == null) {\n            inputStream = new FileInputStream(getFile());\n        }\n        return inputStream;\n    }\n\n    @Override\n    public void close() throws IOException {\n        IOUtil.closeQuietly(inputStream);\n        inputStream = null;\n    }\n\n    @Override\n    public long getContentLength() {\n        return getFile().length();\n    }\n\n    @Override\n    public int getResponseCode() throws IOException {\n        return getFile().exists() ? 200 : 404;\n    }\n\n    @Override\n    public String getResponseMessage() throws IOException {\n        return null;\n    }\n\n    @Override\n    public long getExpiration() {\n        return -1;\n    }\n\n    @Override\n    public long getLastModified() {\n        return getFile().lastModified();\n    }\n\n    @Override\n    public String getETag() {\n        return null;\n    }\n\n    @Override\n    public String getResponseHeader(String name) {\n        return null;\n    }\n\n    @Override\n    public Map<String, List<String>> getResponseHeaders() {\n        return null;\n    }\n\n    @Override\n    public long getHeaderFieldDate(String name, long defaultValue) {\n        return defaultValue;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/request/UriRequest.java",
    "content": "package org.xutils.http.request;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.ProgressHandler;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.app.RequestInterceptListener;\nimport org.xutils.http.loader.Loader;\nimport org.xutils.http.loader.LoaderFactory;\nimport org.xutils.x;\n\nimport java.io.Closeable;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Created by wyouflf on 15/7/23.\n * Uri请求发送和数据接收\n */\npublic abstract class UriRequest implements Closeable {\n\n    protected final String queryUrl;\n    protected final RequestParams params;\n    protected final Loader<?> loader;\n\n    protected ClassLoader callingClassLoader = null;\n    protected ProgressHandler progressHandler = null;\n    protected RequestInterceptListener requestInterceptListener = null;\n\n    /*package*/ UriRequest(RequestParams params, Type loadType) throws Throwable {\n        this.params = params;\n        this.queryUrl = buildQueryUrl(params);\n        this.loader = LoaderFactory.getLoader(loadType, params);\n    }\n\n    // build query\n    protected String buildQueryUrl(RequestParams params) {\n        return params.getUri();\n    }\n\n    public void setProgressHandler(ProgressHandler progressHandler) {\n        this.progressHandler = progressHandler;\n        this.loader.setProgressHandler(progressHandler);\n    }\n\n    public void setCallingClassLoader(ClassLoader callingClassLoader) {\n        this.callingClassLoader = callingClassLoader;\n    }\n\n    public void setRequestInterceptListener(RequestInterceptListener requestInterceptListener) {\n        this.requestInterceptListener = requestInterceptListener;\n    }\n\n    public RequestParams getParams() {\n        return params;\n    }\n\n    public String getRequestUri() {\n        return queryUrl;\n    }\n\n    /**\n     * invoke via Loader\n     *\n     * @throws IOException\n     */\n    public abstract void sendRequest() throws Throwable;\n\n    public abstract boolean isLoading();\n\n    public abstract String getCacheKey();\n\n    /**\n     * 由loader发起请求, 拿到结果.\n     *\n     * @return\n     * @throws Throwable\n     */\n    public Object loadResult() throws Throwable {\n        return this.loader.load(this);\n    }\n\n    /**\n     * 尝试从缓存获取结果, 并为请求头加入缓存控制参数.\n     *\n     * @return\n     * @throws Throwable\n     */\n    public abstract Object loadResultFromCache() throws Throwable;\n\n    public abstract void clearCacheHeader();\n\n    public void save2Cache() {\n        x.task().run(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    loader.save2Cache(UriRequest.this);\n                } catch (Throwable ex) {\n                    LogUtil.e(ex.getMessage(), ex);\n                }\n            }\n        });\n    }\n\n    public abstract InputStream getInputStream() throws IOException;\n\n    @Override\n    public abstract void close() throws IOException;\n\n    public abstract long getContentLength();\n\n    public abstract int getResponseCode() throws IOException;\n\n    public abstract String getResponseMessage() throws IOException;\n\n    public abstract long getExpiration();\n\n    public abstract long getLastModified();\n\n    public abstract String getETag();\n\n    public abstract String getResponseHeader(String name);\n\n    public abstract Map<String, List<String>> getResponseHeaders();\n\n    public abstract long getHeaderFieldDate(String name, long defaultValue);\n\n    @Override\n    public String toString() {\n        return getRequestUri();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/http/request/UriRequestFactory.java",
    "content": "package org.xutils.http.request;\n\nimport android.text.TextUtils;\n\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.RequestParams;\nimport org.xutils.http.app.RequestTracker;\n\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\n\n/**\n * Created by wyouflf on 15/11/4.\n * Uri请求创建工厂\n */\npublic final class UriRequestFactory {\n\n    private static Class<? extends RequestTracker> defaultTrackerCls;\n\n    private static final HashMap<String, Class<? extends UriRequest>>\n            SCHEME_CLS_MAP = new HashMap<String, Class<? extends UriRequest>>();\n\n    private UriRequestFactory() {\n    }\n\n    public static UriRequest getUriRequest(RequestParams params, Type loadType) throws Throwable {\n\n        // get scheme\n        String scheme = null;\n        String uri = params.getUri();\n        int index = uri.indexOf(\":\");\n        if (index > 0) {\n            scheme = uri.substring(0, index);\n        } else if (uri.startsWith(\"/\")) {\n            scheme = \"file\";\n        }\n\n        // get UriRequest\n        if (!TextUtils.isEmpty(scheme)) {\n            Class<? extends UriRequest> cls = SCHEME_CLS_MAP.get(scheme);\n            if (cls != null) {\n                Constructor<? extends UriRequest> constructor\n                        = cls.getConstructor(RequestParams.class, Class.class);\n                return constructor.newInstance(params, loadType);\n            } else {\n                if (scheme.startsWith(\"http\")) {\n                    return new HttpRequest(params, loadType);\n                } else if (scheme.equals(\"assets\")) {\n                    return new AssetsRequest(params, loadType);\n                } else if (scheme.equals(\"file\")) {\n                    return new LocalFileRequest(params, loadType);\n                } else {\n                    throw new IllegalArgumentException(\"The url not be support: \" + uri);\n                }\n            }\n        } else {\n            throw new IllegalArgumentException(\"The url not be support: \" + uri);\n        }\n    }\n\n    public static void registerDefaultTrackerClass(Class<? extends RequestTracker> trackerCls) {\n        UriRequestFactory.defaultTrackerCls = trackerCls;\n    }\n\n    public static RequestTracker getDefaultTracker() {\n        try {\n            return defaultTrackerCls == null ? null : defaultTrackerCls.newInstance();\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        return null;\n    }\n\n    public static void registerRequestClass(String scheme, Class<? extends UriRequest> uriRequestCls) {\n        SCHEME_CLS_MAP.put(scheme, uriRequestCls);\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/AsyncDrawable.java",
    "content": "package org.xutils.image;\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.PixelFormat;\nimport android.graphics.PorterDuff;\nimport android.graphics.Rect;\nimport android.graphics.Region;\nimport android.graphics.drawable.Drawable;\n\nimport java.lang.ref.WeakReference;\n\n/**\n * Author: wyouflf\n * Date: 13-11-17\n * Time: 上午11:42\n */\npublic final class AsyncDrawable extends Drawable {\n\n    private final WeakReference<ImageLoader> imageLoaderReference;\n\n    private Drawable baseDrawable;\n\n    public AsyncDrawable(ImageLoader imageLoader, Drawable drawable) {\n        if (imageLoader == null) {\n            throw new IllegalArgumentException(\"imageLoader may not be null\");\n        }\n        baseDrawable = drawable;\n        while (baseDrawable instanceof AsyncDrawable) {\n            baseDrawable = ((AsyncDrawable) baseDrawable).baseDrawable;\n        }\n        imageLoaderReference = new WeakReference<ImageLoader>(imageLoader);\n    }\n\n    public ImageLoader getImageLoader() {\n        return imageLoaderReference.get();\n    }\n\n    public void setBaseDrawable(Drawable baseDrawable) {\n        this.baseDrawable = baseDrawable;\n    }\n\n    public Drawable getBaseDrawable() {\n        return baseDrawable;\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        if (baseDrawable != null) {\n            baseDrawable.draw(canvas);\n        }\n    }\n\n    @Override\n    public void setAlpha(int i) {\n        if (baseDrawable != null) {\n            baseDrawable.setAlpha(i);\n        }\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter colorFilter) {\n        if (baseDrawable != null) {\n            baseDrawable.setColorFilter(colorFilter);\n        }\n    }\n\n    @Override\n    public int getOpacity() {\n        return baseDrawable == null ? PixelFormat.TRANSLUCENT : baseDrawable.getOpacity();\n    }\n\n    @Override\n    public void setBounds(int left, int top, int right, int bottom) {\n        if (baseDrawable != null) {\n            baseDrawable.setBounds(left, top, right, bottom);\n        }\n    }\n\n    @Override\n    public void setBounds(Rect bounds) {\n        if (baseDrawable != null) {\n            baseDrawable.setBounds(bounds);\n        }\n    }\n\n    @Override\n    public void setChangingConfigurations(int configs) {\n        if (baseDrawable != null) {\n            baseDrawable.setChangingConfigurations(configs);\n        }\n    }\n\n    @Override\n    public int getChangingConfigurations() {\n        return baseDrawable == null ? 0 : baseDrawable.getChangingConfigurations();\n    }\n\n    @Override\n    public void setDither(boolean dither) {\n        if (baseDrawable != null) {\n            baseDrawable.setDither(dither);\n        }\n    }\n\n    @Override\n    public void setFilterBitmap(boolean filter) {\n        if (baseDrawable != null) {\n            baseDrawable.setFilterBitmap(filter);\n        }\n    }\n\n    @Override\n    public void invalidateSelf() {\n        if (baseDrawable != null) {\n            baseDrawable.invalidateSelf();\n        }\n    }\n\n    @Override\n    public void scheduleSelf(Runnable what, long when) {\n        if (baseDrawable != null) {\n            baseDrawable.scheduleSelf(what, when);\n        }\n    }\n\n    @Override\n    public void unscheduleSelf(Runnable what) {\n        if (baseDrawable != null) {\n            baseDrawable.unscheduleSelf(what);\n        }\n    }\n\n    @Override\n    public void setColorFilter(int color, PorterDuff.Mode mode) {\n        if (baseDrawable != null) {\n            baseDrawable.setColorFilter(color, mode);\n        }\n    }\n\n    @Override\n    public void clearColorFilter() {\n        if (baseDrawable != null) {\n            baseDrawable.clearColorFilter();\n        }\n    }\n\n    @Override\n    public boolean isStateful() {\n        return baseDrawable != null && baseDrawable.isStateful();\n    }\n\n    @Override\n    public boolean setState(int[] stateSet) {\n        return baseDrawable != null && baseDrawable.setState(stateSet);\n    }\n\n    @Override\n    public int[] getState() {\n        return baseDrawable == null ? null : baseDrawable.getState();\n    }\n\n    @Override\n    public Drawable getCurrent() {\n        return baseDrawable == null ? null : baseDrawable.getCurrent();\n    }\n\n    @Override\n    public boolean setVisible(boolean visible, boolean restart) {\n        return baseDrawable != null && baseDrawable.setVisible(visible, restart);\n    }\n\n    @Override\n    public Region getTransparentRegion() {\n        return baseDrawable == null ? null : baseDrawable.getTransparentRegion();\n    }\n\n    @Override\n    public int getIntrinsicWidth() {\n        return baseDrawable == null ? 0 : baseDrawable.getIntrinsicWidth();\n    }\n\n    @Override\n    public int getIntrinsicHeight() {\n        return baseDrawable == null ? 0 : baseDrawable.getIntrinsicHeight();\n    }\n\n    @Override\n    public int getMinimumWidth() {\n        return baseDrawable == null ? 0 : baseDrawable.getMinimumWidth();\n    }\n\n    @Override\n    public int getMinimumHeight() {\n        return baseDrawable == null ? 0 : baseDrawable.getMinimumHeight();\n    }\n\n    @Override\n    public boolean getPadding(Rect padding) {\n        return baseDrawable != null && baseDrawable.getPadding(padding);\n    }\n\n    @Override\n    public Drawable mutate() {\n        return baseDrawable == null ? null : baseDrawable.mutate();\n    }\n\n    @Override\n    public ConstantState getConstantState() {\n        return baseDrawable == null ? null : baseDrawable.getConstantState();\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        super.finalize();\n        ImageLoader imageLoader = this.getImageLoader();\n        if (imageLoader != null) {\n            imageLoader.cancel();\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/GifDrawable.java",
    "content": "package org.xutils.image;\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.Movie;\nimport android.graphics.PixelFormat;\nimport android.graphics.drawable.Animatable;\nimport android.graphics.drawable.Drawable;\nimport android.os.SystemClock;\n\nimport org.xutils.common.util.LogUtil;\n\npublic class GifDrawable extends Drawable implements Runnable, Animatable {\n\n    private int byteCount;\n    private int rate = 300;\n    private volatile boolean running;\n\n    private final Movie movie;\n    private final int duration;\n    private final long begin = SystemClock.uptimeMillis();\n\n    public GifDrawable(Movie movie, int byteCount) {\n        this.movie = movie;\n        this.byteCount = byteCount;\n        this.duration = movie.duration();\n    }\n\n    public int getDuration() {\n        return duration;\n    }\n\n    public Movie getMovie() {\n        return movie;\n    }\n\n    public int getByteCount() {\n        if (byteCount == 0) {\n            byteCount = (movie.width() * movie.height() * 3) * (5/*fake frame count*/);\n        }\n        return byteCount;\n    }\n\n    public int getRate() {\n        return rate;\n    }\n\n    public void setRate(int rate) {\n        this.rate = rate;\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        try {\n            int time = duration > 0 ? (int) (SystemClock.uptimeMillis() - begin) % duration : 0;\n            movie.setTime(time);\n            movie.draw(canvas, 0, 0);\n            start();\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    @Override\n    public void start() {\n        if (!isRunning()) {\n            running = true;\n            run();\n        }\n    }\n\n    @Override\n    public void stop() {\n        if (isRunning()) {\n            this.unscheduleSelf(this);\n        }\n    }\n\n    @Override\n    public boolean isRunning() {\n        return running && duration > 0;\n    }\n\n    @Override\n    public void run() {\n        if (duration > 0) {\n            this.invalidateSelf();\n            this.scheduleSelf(this, SystemClock.uptimeMillis() + rate);\n        }\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n\n    }\n\n    @Override\n    public int getIntrinsicWidth() {\n        return movie.width();\n    }\n\n    @Override\n    public int getIntrinsicHeight() {\n        return movie.height();\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter cf) {\n    }\n\n    @Override\n    public int getOpacity() {\n        return movie.isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/ImageAnimationHelper.java",
    "content": "package org.xutils.image;\n\nimport android.graphics.drawable.Drawable;\nimport android.view.animation.AlphaAnimation;\nimport android.view.animation.Animation;\nimport android.view.animation.DecelerateInterpolator;\nimport android.widget.ImageView;\n\nimport org.xutils.common.util.LogUtil;\n\nimport java.lang.reflect.Method;\n\n/**\n * Created by wyouflf on 15/10/13.\n * ImageView Animation Helper\n */\npublic final class ImageAnimationHelper {\n\n    private final static Method cloneMethod;\n\n    static {\n        Method method = null;\n        try {\n            method = Animation.class.getDeclaredMethod(\"clone\");\n            method.setAccessible(true);\n        } catch (Throwable ex) {\n            method = null;\n            LogUtil.w(ex.getMessage(), ex);\n        }\n        cloneMethod = method;\n    }\n\n    private ImageAnimationHelper() {\n    }\n\n    public static void fadeInDisplay(final ImageView imageView, Drawable drawable) {\n        AlphaAnimation fadeAnimation = new AlphaAnimation(0F, 1F);\n        fadeAnimation.setDuration(300);\n        fadeAnimation.setInterpolator(new DecelerateInterpolator());\n        imageView.setImageDrawable(drawable);\n        imageView.startAnimation(fadeAnimation);\n    }\n\n    public static void animationDisplay(ImageView imageView, Drawable drawable, Animation animation) {\n        imageView.setImageDrawable(drawable);\n        if (cloneMethod != null && animation != null) {\n            try {\n                imageView.startAnimation((Animation) cloneMethod.invoke(animation));\n            } catch (Throwable ex) {\n                imageView.startAnimation(animation);\n            }\n        } else {\n            imageView.startAnimation(animation);\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/ImageDecoder.java",
    "content": "package org.xutils.image;\n\nimport android.backport.webp.WebPFactory;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Canvas;\nimport android.graphics.Matrix;\nimport android.graphics.Movie;\nimport android.graphics.Paint;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.RectF;\nimport android.graphics.drawable.Drawable;\nimport android.media.ExifInterface;\n\nimport org.xutils.cache.DiskCacheEntity;\nimport org.xutils.cache.DiskCacheFile;\nimport org.xutils.cache.LruDiskCache;\nimport org.xutils.common.Callback;\nimport org.xutils.common.task.PriorityExecutor;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.x;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.Arrays;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Created by wyouflf on 15/10/9.\n * ImageDecoder for ImageLoader\n */\npublic final class ImageDecoder {\n\n    private final static int BITMAP_DECODE_MAX_WORKER;\n    private final static AtomicInteger bitmapDecodeWorker = new AtomicInteger(0);\n    private final static Object bitmapDecodeLock = new Object();\n\n    private final static Object gifDecodeLock = new Object();\n    private final static byte[] GIF_HEADER = new byte[]{'G', 'I', 'F'};\n    private final static byte[] WEBP_HEADER = new byte[]{'W', 'E', 'B', 'P'};\n\n    private final static Executor THUMB_CACHE_EXECUTOR = new PriorityExecutor(1, true);\n    private final static LruDiskCache THUMB_CACHE = LruDiskCache.getDiskCache(\"xUtils_img_thumb\");\n\n    static {\n        int cpuCount = Runtime.getRuntime().availableProcessors();\n        BITMAP_DECODE_MAX_WORKER = cpuCount > 4 ? 2 : 1;\n    }\n\n    private ImageDecoder() {\n    }\n\n    /*package*/\n    static void clearCacheFiles() {\n        THUMB_CACHE.clearCacheFiles();\n    }\n\n    /**\n     * decode image file for ImageLoader\n     *\n     * @param file\n     * @param options\n     * @param cancelable\n     * @return\n     * @throws IOException\n     */\n    /*package*/\n    static Drawable decodeFileWithLock(final File file,\n                                       final ImageOptions options,\n                                       final Callback.Cancelable cancelable) throws IOException {\n        if (file == null || !file.exists() || file.length() < 1) return null;\n        if (cancelable != null && cancelable.isCancelled()) {\n            throw new Callback.CancelledException(\"cancelled during decode image\");\n        }\n\n        Drawable result = null;\n        if (!options.isIgnoreGif() && isGif(file)) {\n            Movie movie = null;\n            synchronized (gifDecodeLock) { // decode with lock\n                movie = decodeGif(file, options, cancelable);\n            }\n            if (movie != null) {\n                result = new GifDrawable(movie, (int) file.length());\n            }\n        } else {\n            Bitmap bitmap = null;\n            { // decode with lock\n                try {\n                    while (bitmapDecodeWorker.get() >= BITMAP_DECODE_MAX_WORKER\n                            && (cancelable == null || !cancelable.isCancelled())) {\n                        synchronized (bitmapDecodeLock) {\n                            try {\n                                bitmapDecodeLock.wait();\n                            } catch (InterruptedException iex) {\n                                throw new Callback.CancelledException(\"cancelled during decode image\");\n                            } catch (Throwable ignored) {\n                            }\n                        }\n                    }\n\n                    if (cancelable != null && cancelable.isCancelled()) {\n                        throw new Callback.CancelledException(\"cancelled during decode image\");\n                    }\n\n                    bitmapDecodeWorker.incrementAndGet();\n                    // get from thumb cache\n                    if (options.isCompress()) {\n                        bitmap = getThumbCache(file, options);\n                    }\n                    if (bitmap == null) {\n                        bitmap = decodeBitmap(file, options, cancelable);\n                        // save to thumb cache\n                        if (bitmap != null && options.isCompress()) {\n                            final Bitmap finalBitmap = bitmap;\n                            THUMB_CACHE_EXECUTOR.execute(new Runnable() {\n                                @Override\n                                public void run() {\n                                    saveThumbCache(file, options, finalBitmap);\n                                }\n                            });\n                        }\n                    }\n                } finally {\n                    bitmapDecodeWorker.decrementAndGet();\n                    synchronized (bitmapDecodeLock) {\n                        bitmapDecodeLock.notifyAll();\n                    }\n                }\n            }\n            if (bitmap != null) {\n                result = new ReusableBitmapDrawable(x.app().getResources(), bitmap);\n            }\n        }\n        return result;\n    }\n\n    public static boolean isGif(File file) {\n        FileInputStream in = null;\n        try {\n            in = new FileInputStream(file);\n            byte[] header = IOUtil.readBytes(in, 0, 3);\n            return Arrays.equals(GIF_HEADER, header);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        } finally {\n            IOUtil.closeQuietly(in);\n        }\n\n        return false;\n    }\n\n    public static boolean isWebP(File file) {\n        FileInputStream in = null;\n        try {\n            in = new FileInputStream(file);\n            byte[] header = IOUtil.readBytes(in, 8, 4);\n            return Arrays.equals(WEBP_HEADER, header);\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        } finally {\n            IOUtil.closeQuietly(in);\n        }\n\n        return false;\n    }\n\n    /**\n     * 转化文件为Bitmap, 更好的支持WEBP.\n     *\n     * @param file\n     * @param options\n     * @param cancelable\n     * @return\n     * @throws IOException\n     */\n    public static Bitmap decodeBitmap(File file, ImageOptions options, Callback.Cancelable cancelable) throws IOException {\n        {// check params\n            if (file == null || !file.exists() || file.length() < 1) return null;\n            if (options == null) {\n                options = ImageOptions.DEFAULT;\n            }\n            if (options.getMaxWidth() <= 0 || options.getMaxHeight() <= 0) {\n                options.optimizeMaxSize(null);\n            }\n        }\n\n        Bitmap result = null;\n        try {\n            if (cancelable != null && cancelable.isCancelled()) {\n                throw new Callback.CancelledException(\"cancelled during decode image\");\n            }\n\n            // prepare bitmap options\n            final BitmapFactory.Options bitmapOps = new BitmapFactory.Options();\n            bitmapOps.inJustDecodeBounds = true;\n            bitmapOps.inPurgeable = true;\n            bitmapOps.inInputShareable = true;\n            BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOps);\n            bitmapOps.inJustDecodeBounds = false;\n            bitmapOps.inPreferredConfig = options.getConfig();\n            int rotateAngle = 0;\n            int rawWidth = bitmapOps.outWidth;\n            int rawHeight = bitmapOps.outHeight;\n            int optionWith = options.getWidth();\n            int optionHeight = options.getHeight();\n            if (options.isAutoRotate()) {\n                rotateAngle = getRotateAngle(file.getAbsolutePath());\n                if ((rotateAngle / 90) % 2 == 1) {\n                    rawWidth = bitmapOps.outHeight;\n                    rawHeight = bitmapOps.outWidth;\n                }\n            }\n            if (!options.isCrop() && optionWith > 0 && optionHeight > 0) {\n                if ((rotateAngle / 90) % 2 == 1) {\n                    bitmapOps.outWidth = optionHeight;\n                    bitmapOps.outHeight = optionWith;\n                } else {\n                    bitmapOps.outWidth = optionWith;\n                    bitmapOps.outHeight = optionHeight;\n                }\n            }\n            bitmapOps.inSampleSize = calculateSampleSize(\n                    rawWidth, rawHeight,\n                    options.getMaxWidth(), options.getMaxHeight());\n\n            if (cancelable != null && cancelable.isCancelled()) {\n                throw new Callback.CancelledException(\"cancelled during decode image\");\n            }\n\n            // decode file\n            Bitmap bitmap = null;\n            if (isWebP(file)) {\n                bitmap = WebPFactory.decodeFile(file.getAbsolutePath(), bitmapOps);\n            }\n            if (bitmap == null) {\n                bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOps);\n            }\n            if (bitmap == null) {\n                throw new IOException(\"decode image error\");\n            }\n\n            { // 旋转和缩放处理\n                if (cancelable != null && cancelable.isCancelled()) {\n                    throw new Callback.CancelledException(\"cancelled during decode image\");\n                }\n                if (rotateAngle != 0) {\n                    bitmap = rotate(bitmap, rotateAngle, true);\n                }\n                if (cancelable != null && cancelable.isCancelled()) {\n                    throw new Callback.CancelledException(\"cancelled during decode image\");\n                }\n                if (options.isCrop() && optionWith > 0 && optionHeight > 0) {\n                    bitmap = cut2ScaleSize(bitmap, optionWith, optionHeight, true);\n                }\n            }\n\n            if (bitmap == null) {\n                throw new IOException(\"decode image error\");\n            }\n\n            { // 圆角和方块处理\n                if (cancelable != null && cancelable.isCancelled()) {\n                    throw new Callback.CancelledException(\"cancelled during decode image\");\n                }\n                if (options.isCircular()) {\n                    bitmap = cut2Circular(bitmap, true);\n                } else if (options.getRadius() > 0) {\n                    bitmap = cut2RoundCorner(bitmap, options.getRadius(), options.isSquare(), true);\n                } else if (options.isSquare()) {\n                    bitmap = cut2Square(bitmap, true);\n                }\n            }\n\n            if (bitmap == null) {\n                throw new IOException(\"decode image error\");\n            }\n\n            result = bitmap;\n        } catch (IOException ex) {\n            throw ex;\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n            result = null;\n        }\n\n        return result;\n    }\n\n    /**\n     * 转换文件为Movie, 可用于创建GifDrawable.\n     *\n     * @param file\n     * @param options\n     * @param cancelable\n     * @return\n     * @throws IOException\n     */\n    public static Movie decodeGif(File file, ImageOptions options, Callback.Cancelable cancelable) throws IOException {\n        {// check params\n            if (file == null || !file.exists() || file.length() < 1) return null;\n            /*if (options == null) {\n                options = ImageOptions.DEFAULT; // not use\n            }\n            if (options.getMaxWidth() <= 0 || options.getMaxHeight() <= 0) {\n                options.optimizeMaxSize(null);\n            }*/\n        }\n\n        InputStream in = null;\n        try {\n            if (cancelable != null && cancelable.isCancelled()) {\n                throw new Callback.CancelledException(\"cancelled during decode image\");\n            }\n            int buffSize = 1024 * 16;\n            in = new BufferedInputStream(new FileInputStream(file), buffSize);\n            in.mark(buffSize);\n            Movie movie = Movie.decodeStream(in);\n            if (movie == null) {\n                throw new IOException(\"decode image error\");\n            }\n            return movie;\n        } catch (IOException ex) {\n            throw ex;\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n            return null;\n        } finally {\n            IOUtil.closeQuietly(in);\n        }\n    }\n\n    /**\n     * 计算压缩采样倍数\n     *\n     * @param rawWidth\n     * @param rawHeight\n     * @param maxWidth\n     * @param maxHeight\n     * @return\n     */\n    public static int calculateSampleSize(final int rawWidth, final int rawHeight,\n                                          final int maxWidth, final int maxHeight) {\n        int sampleSize = 1;\n\n        if (rawWidth > maxWidth || rawHeight > maxHeight) {\n            if (rawWidth > rawHeight) {\n                sampleSize = Math.round((float) rawHeight / (float) maxHeight);\n            } else {\n                sampleSize = Math.round((float) rawWidth / (float) maxWidth);\n            }\n\n            if (sampleSize < 1) {\n                sampleSize = 1;\n            }\n\n            final float totalPixels = rawWidth * rawHeight;\n\n            final float maxTotalPixels = maxWidth * maxHeight * 2;\n\n            while (totalPixels / (sampleSize * sampleSize) > maxTotalPixels) {\n                sampleSize++;\n            }\n        }\n        return sampleSize;\n    }\n\n    /**\n     * 裁剪方形图片\n     *\n     * @param source\n     * @param recycleSource 裁剪成功后销毁原图\n     * @return\n     */\n    public static Bitmap cut2Square(Bitmap source, boolean recycleSource) {\n        int width = source.getWidth();\n        int height = source.getHeight();\n        if (width == height) {\n            return source;\n        }\n\n        int squareWith = Math.min(width, height);\n        Bitmap result = Bitmap.createBitmap(source, (width - squareWith) / 2,\n                (height - squareWith) / 2, squareWith, squareWith);\n        if (result != null) {\n            if (recycleSource && result != source) {\n                source.recycle();\n                source = null;\n            }\n        } else {\n            result = source;\n        }\n        return result;\n    }\n\n    /**\n     * 裁剪圆形图片\n     *\n     * @param source\n     * @param recycleSource 裁剪成功后销毁原图\n     * @return\n     */\n    public static Bitmap cut2Circular(Bitmap source, boolean recycleSource) {\n        int width = source.getWidth();\n        int height = source.getHeight();\n        int diameter = Math.min(width, height);\n        Paint paint = new Paint();\n        paint.setAntiAlias(true);\n        Bitmap result = Bitmap.createBitmap(diameter, diameter, Bitmap.Config.ARGB_8888);\n        if (result != null) {\n            Canvas canvas = new Canvas(result);\n            canvas.drawCircle(diameter / 2, diameter / 2, diameter / 2, paint);\n            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));\n            canvas.drawBitmap(source, (diameter - width) / 2, (diameter - height) / 2, paint);\n            if (recycleSource) {\n                source.recycle();\n                source = null;\n            }\n        } else {\n            result = source;\n        }\n        return result;\n    }\n\n    /**\n     * 裁剪圆角\n     *\n     * @param source\n     * @param radius\n     * @param isSquare\n     * @param recycleSource 裁剪成功后销毁原图\n     * @return\n     */\n    public static Bitmap cut2RoundCorner(Bitmap source, int radius, boolean isSquare, boolean recycleSource) {\n        if (radius <= 0) return source;\n\n        int sourceWidth = source.getWidth();\n        int sourceHeight = source.getHeight();\n        int targetWidth = sourceWidth;\n        int targetHeight = sourceHeight;\n        if (isSquare) {\n            targetWidth = targetHeight = Math.min(sourceWidth, sourceHeight);\n        }\n\n        Paint paint = new Paint();\n        paint.setAntiAlias(true);\n        Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);\n        if (result != null) {\n            Canvas canvas = new Canvas(result);\n            RectF rect = new RectF(0, 0, targetWidth, targetHeight);\n            canvas.drawRoundRect(rect, radius, radius, paint);\n            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));\n            canvas.drawBitmap(source,\n                    (targetWidth - sourceWidth) / 2, (targetHeight - sourceHeight) / 2, paint);\n            if (recycleSource) {\n                source.recycle();\n                source = null;\n            }\n        } else {\n            result = source;\n        }\n        return result;\n    }\n\n    /**\n     * 裁剪并缩放至指定大小\n     *\n     * @param source\n     * @param dstWidth\n     * @param dstHeight\n     * @param recycleSource 裁剪成功后销毁原图\n     * @return\n     */\n    public static Bitmap cut2ScaleSize(Bitmap source, int dstWidth, int dstHeight, boolean recycleSource) {\n        final int width = source.getWidth();\n        final int height = source.getHeight();\n        if (width == dstWidth && height == dstHeight) {\n            return source;\n        }\n\n        // scale\n        Matrix m = new Matrix();\n        int l = 0, t = 0, r = width, b = height;\n        {\n            float sx = dstWidth / (float) width;\n            float sy = dstHeight / (float) height;\n\n            if (sx > sy) {\n                sy = sx;\n                l = 0;\n                r = width;\n                t = (int) ((height - dstHeight / sx) / 2);\n                b = (int) ((height + dstHeight / sx) / 2);\n            } else {\n                sx = sy;\n                l = (int) ((width - dstWidth / sx) / 2);\n                r = (int) ((width + dstWidth / sx) / 2);\n                t = 0;\n                b = height;\n            }\n            m.setScale(sx, sy);\n        }\n\n        Bitmap result = Bitmap.createBitmap(source, l, t, r - l, b - t, m, true);\n\n        if (result != null) {\n            if (recycleSource && result != source) {\n                source.recycle();\n                source = null;\n            }\n        } else {\n            result = source;\n        }\n        return result;\n    }\n\n    /**\n     * 旋转图片\n     *\n     * @param source\n     * @param angle\n     * @param recycleSource\n     * @return\n     */\n    public static Bitmap rotate(Bitmap source, int angle, boolean recycleSource) {\n        Bitmap result = null;\n\n        if (angle != 0) {\n\n            Matrix m = new Matrix();\n            m.setRotate(angle);\n            try {\n                result = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), m, true);\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n        }\n\n        if (result != null) {\n            if (recycleSource && result != source) {\n                source.recycle();\n                source = null;\n            }\n        } else {\n            result = source;\n        }\n        return result;\n    }\n\n    /**\n     * 获取图片旋转角度\n     *\n     * @param filePath\n     * @return\n     */\n    public static int getRotateAngle(String filePath) {\n        int angle = 0;\n        try {\n            ExifInterface exif = new ExifInterface(filePath);\n            int orientation = exif.getAttributeInt(\n                    ExifInterface.TAG_ORIENTATION,\n                    ExifInterface.ORIENTATION_UNDEFINED);\n            switch (orientation) {\n                case ExifInterface.ORIENTATION_ROTATE_90:\n                    angle = 90;\n                    break;\n                case ExifInterface.ORIENTATION_ROTATE_180:\n                    angle = 180;\n                    break;\n                case ExifInterface.ORIENTATION_ROTATE_270:\n                    angle = 270;\n                    break;\n                default:\n                    angle = 0;\n                    break;\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n        return angle;\n    }\n\n    /**\n     * 压缩bitmap, 更好的支持webp.\n     *\n     * @param bitmap\n     * @param format\n     * @param quality\n     * @param out\n     * @throws IOException\n     */\n    public static void compress(Bitmap bitmap, Bitmap.CompressFormat format, int quality, OutputStream out) throws IOException {\n        if (format == Bitmap.CompressFormat.WEBP) {\n            byte[] data = WebPFactory.encodeBitmap(bitmap, quality);\n            out.write(data);\n        } else {\n            bitmap.compress(format, quality, out);\n        }\n    }\n\n    /**\n     * 根据文件的修改时间和图片的属性保存缩略图\n     *\n     * @param file\n     * @param options\n     * @param thumbBitmap\n     */\n    private static void saveThumbCache(File file, ImageOptions options, Bitmap thumbBitmap) {\n        if (!WebPFactory.available()) return;\n\n        DiskCacheEntity entity = new DiskCacheEntity();\n        entity.setKey(\n                file.getAbsolutePath() + \"@\" + file.lastModified() + options.toString());\n        DiskCacheFile cacheFile = null;\n        OutputStream out = null;\n        try {\n            cacheFile = THUMB_CACHE.createDiskCacheFile(entity);\n            if (cacheFile != null) {\n                out = new FileOutputStream(cacheFile);\n                byte[] encoded = WebPFactory.encodeBitmap(thumbBitmap, 80);\n                out.write(encoded);\n                out.flush();\n                cacheFile = cacheFile.commit();\n            }\n        } catch (Throwable ex) {\n            IOUtil.deleteFileOrDir(cacheFile);\n            LogUtil.w(ex.getMessage(), ex);\n        } finally {\n            IOUtil.closeQuietly(cacheFile);\n            IOUtil.closeQuietly(out);\n        }\n    }\n\n    /**\n     * 根据文件的修改时间和图片的属性获取缩略图\n     *\n     * @param file\n     * @param options\n     * @return\n     */\n    private static Bitmap getThumbCache(File file, ImageOptions options) {\n        if (!WebPFactory.available()) return null;\n\n        DiskCacheFile cacheFile = null;\n        try {\n            cacheFile = THUMB_CACHE.getDiskCacheFile(\n                    file.getAbsolutePath() + \"@\" + file.lastModified() + options.toString());\n            if (cacheFile != null && cacheFile.exists()) {\n                BitmapFactory.Options bitmapOps = new BitmapFactory.Options();\n                bitmapOps.inJustDecodeBounds = false;\n                bitmapOps.inPurgeable = true;\n                bitmapOps.inInputShareable = true;\n                bitmapOps.inPreferredConfig = Bitmap.Config.ARGB_8888;\n                return WebPFactory.decodeFile(cacheFile.getAbsolutePath(), bitmapOps);\n            }\n        } catch (Throwable ex) {\n            LogUtil.w(ex.getMessage(), ex);\n        } finally {\n            IOUtil.closeQuietly(cacheFile);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/ImageLoader.java",
    "content": "package org.xutils.image;\n\nimport android.annotation.SuppressLint;\nimport android.app.ActivityManager;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Paint;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.text.TextUtils;\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.widget.ImageView;\n\nimport org.xutils.cache.LruCache;\nimport org.xutils.cache.LruDiskCache;\nimport org.xutils.common.Callback;\nimport org.xutils.common.task.Priority;\nimport org.xutils.common.task.PriorityExecutor;\nimport org.xutils.common.util.IOUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.ex.FileLockedException;\nimport org.xutils.http.RequestParams;\nimport org.xutils.x;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * Created by wyouflf on 15/10/9.\n * 图片加载控制\n */\n/*package*/ final class ImageLoader implements\n        Callback.PrepareCallback<File, Drawable>,\n        Callback.CacheCallback<Drawable>,\n        Callback.ProgressCallback<Drawable>,\n        Callback.TypedCallback<Drawable>,\n        Callback.Cancelable {\n\n    private MemCacheKey key;\n    private ImageOptions options;\n    private WeakReference<ImageView> viewRef;\n\n    private final static AtomicLong SEQ_SEEK = new AtomicLong(0);\n    private final long seq = SEQ_SEEK.incrementAndGet();\n\n    private volatile boolean stopped = false;\n    private volatile boolean cancelled = false;\n    private Callback.Cancelable cancelable;\n    private Callback.CommonCallback<Drawable> callback;\n    private Callback.PrepareCallback<File, Drawable> prepareCallback;\n    private Callback.CacheCallback<Drawable> cacheCallback;\n    private Callback.ProgressCallback<Drawable> progressCallback;\n\n    private final static String DISK_CACHE_DIR_NAME = \"xUtils_img\";\n    private final static Executor EXECUTOR = new PriorityExecutor(10, false);\n    private final static int MEM_CACHE_MIN_SIZE = 1024 * 1024 * 4; // 4M\n    private final static LruCache<MemCacheKey, Drawable> MEM_CACHE =\n            new LruCache<MemCacheKey, Drawable>(MEM_CACHE_MIN_SIZE) {\n                private boolean deepClear = false;\n\n                @Override\n                protected int sizeOf(MemCacheKey key, Drawable value) {\n                    if (value instanceof BitmapDrawable) {\n                        Bitmap bitmap = ((BitmapDrawable) value).getBitmap();\n                        return bitmap == null ? 0 : bitmap.getByteCount();\n                    } else if (value instanceof GifDrawable) {\n                        return ((GifDrawable) value).getByteCount();\n                    }\n                    return super.sizeOf(key, value);\n                }\n\n                @Override\n                public void trimToSize(int maxSize) {\n                    if (maxSize < 0) {\n                        deepClear = true;\n                    }\n                    super.trimToSize(maxSize);\n                    deepClear = false;\n                }\n\n                @Override\n                protected void entryRemoved(boolean evicted, MemCacheKey key, Drawable oldValue, Drawable newValue) {\n                    super.entryRemoved(evicted, key, oldValue, newValue);\n                    if (evicted && deepClear && oldValue instanceof ReusableDrawable) {\n                        ((ReusableDrawable) oldValue).setMemCacheKey(null);\n                    }\n                }\n            };\n\n    static {\n        int memClass = ((ActivityManager) x.app()\n                .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();\n\n        // Use 1/8th of the available memory for this memory cache.\n        int cacheSize = 1024 * 1024 * memClass / 8;\n        if (cacheSize < MEM_CACHE_MIN_SIZE) {\n            cacheSize = MEM_CACHE_MIN_SIZE;\n        }\n        MEM_CACHE.resize(cacheSize);\n    }\n\n    private ImageLoader() {\n    }\n\n    /*package*/\n    static void clearMemCache() {\n        MEM_CACHE.evictAll();\n    }\n\n    /*package*/\n    static void clearCacheFiles() {\n        LruDiskCache.getDiskCache(DISK_CACHE_DIR_NAME).clearCacheFiles();\n    }\n\n    private final static HashMap<String, FakeImageView> FAKE_IMG_MAP = new HashMap<String, FakeImageView>();\n\n    /**\n     * load from Network or DiskCache, invoke in any thread.\n     *\n     * @param url\n     * @param options\n     * @param callback\n     */\n    /*package*/\n    static Cancelable doLoadDrawable(final String url,\n                                     final ImageOptions options,\n                                     final Callback.CommonCallback<Drawable> callback) {\n        if (TextUtils.isEmpty(url)) {\n            postArgsException(null, options, \"url is null\", callback);\n            return null;\n        }\n\n        FakeImageView fakeImageView = null;\n        synchronized (FAKE_IMG_MAP) {\n            fakeImageView = FAKE_IMG_MAP.get(url);\n            if (fakeImageView == null) {\n                fakeImageView = new FakeImageView();\n            }\n        }\n        return doBind(fakeImageView, url, options, callback);\n    }\n\n    /**\n     * load from Network or DiskCache, invoke in any thread.\n     *\n     * @param url\n     * @param options\n     * @param callback\n     */\n    /*package*/\n    static Cancelable doLoadFile(final String url,\n                                 final ImageOptions options,\n                                 final Callback.CacheCallback<File> callback) {\n        if (TextUtils.isEmpty(url)) {\n            postArgsException(null, options, \"url is null\", callback);\n            return null;\n        }\n\n        RequestParams params = createRequestParams(url, options);\n        return x.http().get(params, callback);\n    }\n\n    /**\n     * load from Network or DiskCache, invoke in ui thread.\n     *\n     * @param view\n     * @param url\n     * @param options\n     * @param callback\n     */\n    /*package*/\n    static Cancelable doBind(final ImageView view,\n                             final String url,\n                             final ImageOptions options,\n                             final Callback.CommonCallback<Drawable> callback) {\n\n        // check params\n        ImageOptions localOptions = options;\n        {\n            if (view == null) {\n                postArgsException(null, localOptions, \"view is null\", callback);\n                return null;\n            }\n\n            if (TextUtils.isEmpty(url)) {\n                postArgsException(view, localOptions, \"url is null\", callback);\n                return null;\n            }\n\n            if (localOptions == null) {\n                localOptions = ImageOptions.DEFAULT;\n            }\n            localOptions.optimizeMaxSize(view);\n        }\n\n        // stop the old loader\n        MemCacheKey key = new MemCacheKey(url, localOptions);\n        Drawable oldDrawable = view.getDrawable();\n        if (oldDrawable instanceof AsyncDrawable) {\n            ImageLoader loader = ((AsyncDrawable) oldDrawable).getImageLoader();\n            if (loader != null && !loader.stopped) {\n                if (key.equals(loader.key)) {\n                    // repetitive url and options binding to the same View.\n                    // not need callback to ui.\n                    return null;\n                } else {\n                    loader.cancel();\n                }\n            }\n        } else if (oldDrawable instanceof ReusableDrawable) {\n            MemCacheKey oldKey = ((ReusableDrawable) oldDrawable).getMemCacheKey();\n            if (oldKey != null && oldKey.equals(key)) {\n                MEM_CACHE.put(key, oldDrawable);\n            }\n        }\n\n        // load from Memory Cache\n        Drawable memDrawable = null;\n        if (localOptions.isUseMemCache()) {\n            memDrawable = MEM_CACHE.get(key);\n            if (memDrawable instanceof BitmapDrawable) {\n                Bitmap bitmap = ((BitmapDrawable) memDrawable).getBitmap();\n                if (bitmap == null || bitmap.isRecycled()) {\n                    memDrawable = null;\n                }\n            }\n        }\n        if (memDrawable != null) { // has mem cache\n            boolean trustMemCache = false;\n            try {\n                if (callback instanceof ProgressCallback) {\n                    ((ProgressCallback) callback).onWaiting();\n                }\n                // hit mem cache\n                view.setScaleType(localOptions.getImageScaleType());\n                view.setImageDrawable(memDrawable);\n                trustMemCache = true;\n                if (callback instanceof CacheCallback) {\n                    trustMemCache = ((CacheCallback<Drawable>) callback).onCache(memDrawable);\n                    if (!trustMemCache) {\n                        // not trust the cache\n                        // load from Network or DiskCache\n                        return new ImageLoader().doLoad(view, url, localOptions, callback);\n                    }\n                } else if (callback != null) {\n                    callback.onSuccess(memDrawable);\n                }\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n                // try load from Network or DiskCache\n                trustMemCache = false;\n                return new ImageLoader().doLoad(view, url, localOptions, callback);\n            } finally {\n                if (trustMemCache && callback != null) {\n                    try {\n                        callback.onFinished();\n                    } catch (Throwable ignored) {\n                        LogUtil.e(ignored.getMessage(), ignored);\n                    }\n                }\n            }\n        } else {\n            // load from Network or DiskCache\n            return new ImageLoader().doLoad(view, url, localOptions, callback);\n        }\n        return null;\n    }\n\n    /**\n     * load from Network or DiskCache\n     *\n     * @param view\n     * @param url\n     * @param options\n     * @param callback\n     */\n    private Cancelable doLoad(ImageView view,\n                              String url,\n                              ImageOptions options,\n                              Callback.CommonCallback<Drawable> callback) {\n\n        this.viewRef = new WeakReference<ImageView>(view);\n        this.options = options;\n        this.key = new MemCacheKey(url, options);\n        this.callback = callback;\n        if (callback instanceof Callback.ProgressCallback) {\n            this.progressCallback = (Callback.ProgressCallback<Drawable>) callback;\n        }\n        if (callback instanceof Callback.PrepareCallback) {\n            this.prepareCallback = (Callback.PrepareCallback<File, Drawable>) callback;\n        }\n        if (callback instanceof Callback.CacheCallback) {\n            this.cacheCallback = (Callback.CacheCallback<Drawable>) callback;\n        }\n\n        // set loadingDrawable\n        Drawable loadingDrawable = null;\n        if (options.isForceLoadingDrawable()) {\n            loadingDrawable = options.getLoadingDrawable(view);\n            view.setScaleType(options.getPlaceholderScaleType());\n            view.setImageDrawable(new AsyncDrawable(this, loadingDrawable));\n        } else {\n            loadingDrawable = view.getDrawable();\n            view.setImageDrawable(new AsyncDrawable(this, loadingDrawable));\n        }\n\n        // request\n        RequestParams params = createRequestParams(url, options);\n        if (view instanceof FakeImageView) {\n            synchronized (FAKE_IMG_MAP) {\n                FAKE_IMG_MAP.put(url, (FakeImageView) view);\n            }\n        }\n        return cancelable = x.http().get(params, this);\n    }\n\n    @Override\n    public void cancel() {\n        stopped = true;\n        cancelled = true;\n        if (cancelable != null) {\n            cancelable.cancel();\n        }\n    }\n\n    @Override\n    public boolean isCancelled() {\n        return cancelled || !validView4Callback(false);\n    }\n\n    @Override\n    public void onWaiting() {\n        if (progressCallback != null) {\n            progressCallback.onWaiting();\n        }\n    }\n\n    @Override\n    public void onStarted() {\n        if (validView4Callback(true) && progressCallback != null) {\n            progressCallback.onStarted();\n        }\n    }\n\n    @Override\n    public void onLoading(long total, long current, boolean isDownloading) {\n        if (validView4Callback(true) && progressCallback != null) {\n            progressCallback.onLoading(total, current, isDownloading);\n        }\n    }\n\n    private static final Type loadType = File.class;\n\n    @Override\n    public Type getLoadType() {\n        return loadType;\n    }\n\n    @Override\n    public Drawable prepare(File rawData) {\n        if (!validView4Callback(true)) return null;\n\n        try {\n            Drawable result = null;\n            if (prepareCallback != null) {\n                result = prepareCallback.prepare(rawData);\n            }\n            if (result == null) {\n                result = ImageDecoder.decodeFileWithLock(rawData, options, this);\n            }\n            if (result != null) {\n                if (result instanceof ReusableDrawable) {\n                    ((ReusableDrawable) result).setMemCacheKey(key);\n                    MEM_CACHE.put(key, result);\n                }\n            }\n            return result;\n        } catch (IOException ex) {\n            IOUtil.deleteFileOrDir(rawData);\n            LogUtil.w(ex.getMessage(), ex);\n        }\n        return null;\n    }\n\n    private boolean hasCache = false;\n\n    @Override\n    public boolean onCache(Drawable result) {\n        if (!validView4Callback(true)) return false;\n\n        if (result != null) {\n            hasCache = true;\n            setSuccessDrawable4Callback(result);\n            if (cacheCallback != null) {\n                return cacheCallback.onCache(result);\n            } else if (callback != null) {\n                callback.onSuccess(result);\n                return true;\n            }\n            return true;\n        }\n\n        return false;\n    }\n\n    @Override\n    public void onSuccess(Drawable result) {\n        if (!validView4Callback(!hasCache)) return;\n\n        if (result != null) {\n            setSuccessDrawable4Callback(result);\n            if (callback != null) {\n                callback.onSuccess(result);\n            }\n        }\n    }\n\n    @Override\n    public void onError(Throwable ex, boolean isOnCallback) {\n        stopped = true;\n        if (!validView4Callback(false)) return;\n\n        if (ex instanceof FileLockedException) {\n            LogUtil.d(\"ImageFileLocked: \" + key.url);\n            x.task().postDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    doBind(viewRef.get(), key.url, options, callback);\n                }\n            }, 10);\n            return;\n        }\n\n        LogUtil.e(key.url, ex);\n        setErrorDrawable4Callback();\n        if (callback != null) {\n            callback.onError(ex, isOnCallback);\n        }\n    }\n\n    @Override\n    public void onCancelled(CancelledException cex) {\n        stopped = true;\n        if (!validView4Callback(false)) return;\n\n        if (callback != null) {\n            callback.onCancelled(cex);\n        }\n    }\n\n    @Override\n    public void onFinished() {\n        stopped = true;\n        ImageView view = viewRef.get();\n        if (view instanceof FakeImageView) {\n            synchronized (FAKE_IMG_MAP) {\n                FAKE_IMG_MAP.remove(key.url);\n            }\n        }\n\n        if (!validView4Callback(false)) return;\n\n        if (callback != null) {\n            callback.onFinished();\n        }\n    }\n\n    private static RequestParams createRequestParams(String url, ImageOptions options) {\n        RequestParams params = new RequestParams(url);\n        params.setCacheDirName(DISK_CACHE_DIR_NAME);\n        params.setConnectTimeout(1000 * 8);\n        params.setPriority(Priority.BG_LOW);\n        params.setExecutor(EXECUTOR);\n        params.setCancelFast(true);\n        params.setUseCookie(false);\n        if (options != null) {\n            ImageOptions.ParamsBuilder paramsBuilder = options.getParamsBuilder();\n            if (paramsBuilder != null) {\n                params = paramsBuilder.buildParams(params, options);\n            }\n        }\n        return params;\n    }\n\n    private boolean validView4Callback(boolean forceValidAsyncDrawable) {\n        final ImageView view = viewRef.get();\n        if (view != null) {\n            Drawable otherDrawable = view.getDrawable();\n            if (otherDrawable instanceof AsyncDrawable) {\n                ImageLoader otherLoader = ((AsyncDrawable) otherDrawable).getImageLoader();\n                if (otherLoader != null) {\n                    if (otherLoader == this) {\n                        if (view.getVisibility() != View.VISIBLE) {\n                            otherLoader.cancel();\n                            return false;\n                        } else {\n                            return true;\n                        }\n                    } else {\n                        if (this.seq > otherLoader.seq) {\n                            otherLoader.cancel();\n                            return true;\n                        } else {\n                            this.cancel();\n                            return false;\n                        }\n                    }\n                }\n            } else if (forceValidAsyncDrawable) {\n                this.cancel();\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    private void setSuccessDrawable4Callback(final Drawable drawable) {\n        final ImageView view = viewRef.get();\n        if (view != null) {\n            view.setScaleType(options.getImageScaleType());\n            if (drawable instanceof GifDrawable) {\n                if (view.getScaleType() == ImageView.ScaleType.CENTER) {\n                    view.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n                }\n                view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);\n            }\n            if (options.getAnimation() != null) {\n                ImageAnimationHelper.animationDisplay(view, drawable, options.getAnimation());\n            } else if (options.isFadeIn()) {\n                ImageAnimationHelper.fadeInDisplay(view, drawable);\n            } else {\n                view.setImageDrawable(drawable);\n            }\n        }\n    }\n\n    private void setErrorDrawable4Callback() {\n        final ImageView view = viewRef.get();\n        if (view != null) {\n            Drawable drawable = options.getFailureDrawable(view);\n            view.setScaleType(options.getPlaceholderScaleType());\n            view.setImageDrawable(drawable);\n        }\n    }\n\n    private static void postArgsException(\n            final ImageView view, final ImageOptions options,\n            final String exMsg, final Callback.CommonCallback<?> callback) {\n        x.task().autoPost(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    if (callback instanceof ProgressCallback) {\n                        ((ProgressCallback) callback).onWaiting();\n                    }\n                    if (view != null && options != null) {\n                        view.setScaleType(options.getPlaceholderScaleType());\n                        view.setImageDrawable(options.getFailureDrawable(view));\n                    }\n                    if (callback != null) {\n                        callback.onError(new IllegalArgumentException(exMsg), false);\n                    }\n                } catch (Throwable ex) {\n                    if (callback != null) {\n                        try {\n                            callback.onError(ex, true);\n                        } catch (Throwable ignored) {\n                            LogUtil.e(ignored.getMessage(), ignored);\n                        }\n                    }\n                } finally {\n                    if (callback != null) {\n                        try {\n                            callback.onFinished();\n                        } catch (Throwable ignored) {\n                            LogUtil.e(ignored.getMessage(), ignored);\n                        }\n                    }\n                }\n            }\n        });\n    }\n\n    @SuppressLint(\"ViewConstructor\")\n    private final static class FakeImageView extends ImageView {\n\n        private Drawable drawable;\n\n        public FakeImageView() {\n            super(x.app());\n        }\n\n        @Override\n        public void setImageDrawable(Drawable drawable) {\n            this.drawable = drawable;\n        }\n\n        @Override\n        public Drawable getDrawable() {\n            return drawable;\n        }\n\n        @Override\n        public void setLayerType(int layerType, Paint paint) {\n        }\n\n        @Override\n        public void setScaleType(ScaleType scaleType) {\n        }\n\n        @Override\n        public void startAnimation(Animation animation) {\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/ImageManagerImpl.java",
    "content": "package org.xutils.image;\n\nimport android.graphics.drawable.Drawable;\nimport android.widget.ImageView;\n\nimport org.xutils.ImageManager;\nimport org.xutils.common.Callback;\nimport org.xutils.x;\n\nimport java.io.File;\n\n/**\n * Created by wyouflf on 15/10/9.\n */\npublic final class ImageManagerImpl implements ImageManager {\n\n    private static final Object lock = new Object();\n    private static volatile ImageManagerImpl instance;\n\n    private ImageManagerImpl() {\n    }\n\n    public static void registerInstance() {\n        if (instance == null) {\n            synchronized (lock) {\n                if (instance == null) {\n                    instance = new ImageManagerImpl();\n                }\n            }\n        }\n        x.Ext.setImageManager(instance);\n    }\n\n\n    @Override\n    public void bind(final ImageView view, final String url) {\n        x.task().autoPost(new Runnable() {\n            @Override\n            public void run() {\n                ImageLoader.doBind(view, url, null, null);\n            }\n        });\n    }\n\n    @Override\n    public void bind(final ImageView view, final String url, final ImageOptions options) {\n        x.task().autoPost(new Runnable() {\n            @Override\n            public void run() {\n                ImageLoader.doBind(view, url, options, null);\n            }\n        });\n    }\n\n    @Override\n    public void bind(final ImageView view, final String url, final Callback.CommonCallback<Drawable> callback) {\n        x.task().autoPost(new Runnable() {\n            @Override\n            public void run() {\n                ImageLoader.doBind(view, url, null, callback);\n            }\n        });\n    }\n\n    @Override\n    public void bind(final ImageView view, final String url, final ImageOptions options, final Callback.CommonCallback<Drawable> callback) {\n        x.task().autoPost(new Runnable() {\n            @Override\n            public void run() {\n                ImageLoader.doBind(view, url, options, callback);\n            }\n        });\n    }\n\n    @Override\n    public Callback.Cancelable loadDrawable(String url, ImageOptions options, Callback.CommonCallback<Drawable> callback) {\n        return ImageLoader.doLoadDrawable(url, options, callback);\n    }\n\n    @Override\n    public Callback.Cancelable loadFile(String url, ImageOptions options, Callback.CacheCallback<File> callback) {\n        return ImageLoader.doLoadFile(url, options, callback);\n    }\n\n    @Override\n    public void clearMemCache() {\n        ImageLoader.clearMemCache();\n    }\n\n    @Override\n    public void clearCacheFiles() {\n        ImageLoader.clearCacheFiles();\n        ImageDecoder.clearCacheFiles();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/ImageOptions.java",
    "content": "package org.xutils.image;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.view.ViewGroup;\nimport android.view.animation.Animation;\nimport android.widget.ImageView;\n\nimport org.xutils.common.util.DensityUtil;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.http.RequestParams;\n\nimport java.lang.reflect.Field;\n\n/**\n * Created by wyouflf on 15/8/21.\n * 图片加载参数\n */\npublic class ImageOptions {\n\n    public final static ImageOptions DEFAULT = new ImageOptions();\n\n    // region ###################### decode options (equals & hashcode prop) ################\n    private int maxWidth = 0;\n    private int maxHeight = 0;\n    private int width = 0; // 小于0时不采样压缩. 等于0时自动识别ImageView的宽高和maxWidth.\n    private int height = 0; // 小于0时不采样压缩. 等于0时自动识别ImageView的宽高和maxHeight.\n    private boolean crop = false; // crop to (width, height)\n\n    private int radius = 0;\n    private boolean square = false;\n    private boolean circular = false;\n    private boolean autoRotate = false;\n    private boolean compress = true;\n    private Bitmap.Config config = Bitmap.Config.RGB_565;\n\n    // gif option\n    private boolean ignoreGif = true;\n    // end region ########################################## decode options #################\n\n    // region ############# display options\n    private int loadingDrawableId = 0;\n    private int failureDrawableId = 0;\n    private Drawable loadingDrawable = null;\n    private Drawable failureDrawable = null;\n    private boolean forceLoadingDrawable = true;\n\n    private ImageView.ScaleType placeholderScaleType = ImageView.ScaleType.CENTER_INSIDE;\n    private ImageView.ScaleType imageScaleType = ImageView.ScaleType.CENTER_CROP;\n\n    private boolean fadeIn = false;\n    private Animation animation = null;\n    // end region ############ display options\n\n    // extends\n    private boolean useMemCache = true;\n    private ParamsBuilder paramsBuilder;\n\n    protected ImageOptions() {\n    }\n\n    /*package*/\n    final void optimizeMaxSize(ImageView view) {\n        if (width > 0 && height > 0) {\n            maxWidth = width;\n            maxHeight = height;\n            return;\n        }\n\n        int screenWidth = DensityUtil.getScreenWidth();\n        int screenHeight = DensityUtil.getScreenHeight();\n\n        if (width < 0) {\n            maxWidth = screenWidth * 3 / 2; //Integer.MAX_VALUE;\n            compress = false;\n        }\n        if (height < 0) {\n            maxHeight = screenHeight * 3 / 2; //Integer.MAX_VALUE;\n            compress = false;\n        }\n\n        if (view == null && maxWidth <= 0 && maxHeight <= 0) {\n            maxWidth = screenWidth;\n            maxHeight = screenHeight;\n        } else {\n            int tempWidth = maxWidth;\n            int tempHeight = maxHeight;\n\n            if (view != null) {\n                final ViewGroup.LayoutParams params = view.getLayoutParams();\n                if (params != null) {\n\n                    if (tempWidth <= 0) {\n                        if (params.width > 0) {\n                            tempWidth = params.width;\n                            if (this.width <= 0) {\n                                this.width = tempWidth;\n                            }\n                        } else if (params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {\n                            tempWidth = view.getWidth();\n                        }\n                    }\n\n                    if (tempHeight <= 0) {\n                        if (params.height > 0) {\n                            tempHeight = params.height;\n                            if (this.height <= 0) {\n                                this.height = tempHeight;\n                            }\n                        } else if (params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {\n                            tempHeight = view.getHeight();\n                        }\n                    }\n                }\n\n                if (tempWidth <= 0) tempWidth = getImageViewFieldValue(view, \"mMaxWidth\");\n                if (tempHeight <= 0) tempHeight = getImageViewFieldValue(view, \"mMaxHeight\");\n            }\n\n            if (tempWidth <= 0) tempWidth = screenWidth;\n            if (tempHeight <= 0) tempHeight = screenHeight;\n\n            maxWidth = tempWidth;\n            maxHeight = tempHeight;\n        }\n    }\n\n    public int getMaxWidth() {\n        return maxWidth;\n    }\n\n    public int getMaxHeight() {\n        return maxHeight;\n    }\n\n    public int getWidth() {\n        return width;\n    }\n\n    public int getHeight() {\n        return height;\n    }\n\n    public boolean isCrop() {\n        return crop;\n    }\n\n    public int getRadius() {\n        return radius;\n    }\n\n    public boolean isSquare() {\n        return square;\n    }\n\n    public boolean isCircular() {\n        return circular;\n    }\n\n    public boolean isIgnoreGif() {\n        return ignoreGif;\n    }\n\n    public boolean isAutoRotate() {\n        return autoRotate;\n    }\n\n    public boolean isCompress() {\n        return compress;\n    }\n\n    public Bitmap.Config getConfig() {\n        return config;\n    }\n\n    public Drawable getLoadingDrawable(ImageView view) {\n        if (loadingDrawable == null && loadingDrawableId > 0 && view != null) {\n            try {\n                loadingDrawable = view.getResources().getDrawable(loadingDrawableId);\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n        }\n        return loadingDrawable;\n    }\n\n    public Drawable getFailureDrawable(ImageView view) {\n        if (failureDrawable == null && failureDrawableId > 0 && view != null) {\n            try {\n                failureDrawable = view.getResources().getDrawable(failureDrawableId);\n            } catch (Throwable ex) {\n                LogUtil.e(ex.getMessage(), ex);\n            }\n        }\n        return failureDrawable;\n    }\n\n    public boolean isFadeIn() {\n        return fadeIn;\n    }\n\n    public Animation getAnimation() {\n        return animation;\n    }\n\n    public ImageView.ScaleType getPlaceholderScaleType() {\n        return placeholderScaleType;\n    }\n\n    public ImageView.ScaleType getImageScaleType() {\n        return imageScaleType;\n    }\n\n    public boolean isForceLoadingDrawable() {\n        return forceLoadingDrawable;\n    }\n\n    public boolean isUseMemCache() {\n        return useMemCache;\n    }\n\n    public ParamsBuilder getParamsBuilder() {\n        return paramsBuilder;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        ImageOptions options = (ImageOptions) o;\n\n        if (maxWidth != options.maxWidth) return false;\n        if (maxHeight != options.maxHeight) return false;\n        if (width != options.width) return false;\n        if (height != options.height) return false;\n        if (crop != options.crop) return false;\n        if (radius != options.radius) return false;\n        if (square != options.square) return false;\n        if (circular != options.circular) return false;\n        if (autoRotate != options.autoRotate) return false;\n        if (compress != options.compress) return false;\n        return config == options.config;\n\n    }\n\n    @Override\n    public int hashCode() {\n        int result = maxWidth;\n        result = 31 * result + maxHeight;\n        result = 31 * result + width;\n        result = 31 * result + height;\n        result = 31 * result + (crop ? 1 : 0);\n        result = 31 * result + radius;\n        result = 31 * result + (square ? 1 : 0);\n        result = 31 * result + (circular ? 1 : 0);\n        result = 31 * result + (autoRotate ? 1 : 0);\n        result = 31 * result + (compress ? 1 : 0);\n        result = 31 * result + (config != null ? config.hashCode() : 0);\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder sb = new StringBuilder(\"_\");\n        sb.append(maxWidth).append(\"_\");\n        sb.append(maxHeight).append(\"_\");\n        sb.append(width).append(\"_\");\n        sb.append(height).append(\"_\");\n        sb.append(radius).append(\"_\");\n        sb.append(config).append(\"_\");\n        sb.append(crop ? 1 : 0).append(square ? 1 : 0).append(circular ? 1 : 0);\n        sb.append(autoRotate ? 1 : 0).append(compress ? 1 : 0);\n        return sb.toString();\n    }\n\n    private static int getImageViewFieldValue(ImageView view, String fieldName) {\n        int value = 0;\n        try {\n            Field field = ImageView.class.getDeclaredField(fieldName);\n            field.setAccessible(true);\n            int fieldValue = (Integer) field.get(view);\n            if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {\n                value = fieldValue;\n            }\n        } catch (Throwable ignored) {\n        }\n        return value;\n    }\n\n    public interface ParamsBuilder {\n        RequestParams buildParams(RequestParams params, ImageOptions options);\n    }\n\n    public static class Builder {\n\n        protected ImageOptions options;\n\n        public Builder() {\n            newImageOptions();\n        }\n\n        protected void newImageOptions() {\n            options = new ImageOptions();\n        }\n\n        public ImageOptions build() {\n            return options;\n        }\n\n        /**\n         * 小于0时不采样压缩. 等于0时自动识别ImageView的宽高和(maxWidth, maxHeight).\n         *\n         * @param width\n         * @param height\n         * @return\n         */\n        public Builder setSize(int width, int height) {\n            options.width = width;\n            options.height = height;\n            return this;\n        }\n\n        public Builder setCrop(boolean crop) {\n            options.crop = crop;\n            return this;\n        }\n\n        public Builder setRadius(int radius) {\n            options.radius = radius;\n            return this;\n        }\n\n        public Builder setSquare(boolean square) {\n            options.square = square;\n            return this;\n        }\n\n        public Builder setCircular(boolean circular) {\n            options.circular = circular;\n            return this;\n        }\n\n        public Builder setAutoRotate(boolean autoRotate) {\n            options.autoRotate = autoRotate;\n            return this;\n        }\n\n        public Builder setConfig(Bitmap.Config config) {\n            options.config = config;\n            return this;\n        }\n\n        public Builder setIgnoreGif(boolean ignoreGif) {\n            options.ignoreGif = ignoreGif;\n            return this;\n        }\n\n        public Builder setLoadingDrawableId(int loadingDrawableId) {\n            options.loadingDrawableId = loadingDrawableId;\n            return this;\n        }\n\n        public Builder setLoadingDrawable(Drawable loadingDrawable) {\n            options.loadingDrawable = loadingDrawable;\n            return this;\n        }\n\n        public Builder setFailureDrawableId(int failureDrawableId) {\n            options.failureDrawableId = failureDrawableId;\n            return this;\n        }\n\n        public Builder setFailureDrawable(Drawable failureDrawable) {\n            options.failureDrawable = failureDrawable;\n            return this;\n        }\n\n        public Builder setFadeIn(boolean fadeIn) {\n            options.fadeIn = fadeIn;\n            return this;\n        }\n\n        public Builder setAnimation(Animation animation) {\n            options.animation = animation;\n            return this;\n        }\n\n        public Builder setPlaceholderScaleType(ImageView.ScaleType placeholderScaleType) {\n            options.placeholderScaleType = placeholderScaleType;\n            return this;\n        }\n\n        public Builder setImageScaleType(ImageView.ScaleType imageScaleType) {\n            options.imageScaleType = imageScaleType;\n            return this;\n        }\n\n        public Builder setForceLoadingDrawable(boolean forceLoadingDrawable) {\n            options.forceLoadingDrawable = forceLoadingDrawable;\n            return this;\n        }\n\n        public Builder setUseMemCache(boolean useMemCache) {\n            options.useMemCache = useMemCache;\n            return this;\n        }\n\n        public Builder setParamsBuilder(ParamsBuilder paramsBuilder) {\n            options.paramsBuilder = paramsBuilder;\n            return this;\n        }\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/MemCacheKey.java",
    "content": "package org.xutils.image;\n\n/**\n * Created by wyouflf on 15/10/20.\n */\n/*package*/ final class MemCacheKey {\n    public final String url;\n    public final ImageOptions options;\n\n    public MemCacheKey(String url, ImageOptions options) {\n        this.url = url;\n        this.options = options;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        MemCacheKey that = (MemCacheKey) o;\n\n        if (!url.equals(that.url)) return false;\n        return options.equals(that.options);\n\n    }\n\n    @Override\n    public int hashCode() {\n        int result = url.hashCode();\n        result = 31 * result + options.hashCode();\n        return result;\n    }\n\n    @Override\n    public String toString() {\n        return url + options.toString();\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/ReusableBitmapDrawable.java",
    "content": "package org.xutils.image;\n\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\n\n/*package*/ final class ReusableBitmapDrawable extends BitmapDrawable implements ReusableDrawable {\n\n    private MemCacheKey key;\n\n    public ReusableBitmapDrawable(Resources res, Bitmap bitmap) {\n        super(res, bitmap);\n    }\n\n    @Override\n    public MemCacheKey getMemCacheKey() {\n        return key;\n    }\n\n    @Override\n    public void setMemCacheKey(MemCacheKey key) {\n        this.key = key;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/image/ReusableDrawable.java",
    "content": "package org.xutils.image;\n\n/**\n * Created by wyouflf on 15/10/20.\n * 使已被LruCache移除, 但还在被ImageView使用的Drawable可以再次被回收使用.\n */\n/*package*/ interface ReusableDrawable {\n\n    MemCacheKey getMemCacheKey();\n\n    void setMemCacheKey(MemCacheKey key);\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/view/EventListenerManager.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.view;\n\nimport android.text.TextUtils;\nimport android.view.View;\n\nimport org.xutils.common.util.DoubleKeyValueMap;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.view.annotation.Event;\n\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\n\n/*package*/ final class EventListenerManager {\n\n    private final static long QUICK_EVENT_TIME_SPAN = 300;\n    private final static HashSet<String> AVOID_QUICK_EVENT_SET = new HashSet<String>(2);\n\n    static {\n        AVOID_QUICK_EVENT_SET.add(\"onClick\");\n        AVOID_QUICK_EVENT_SET.add(\"onItemClick\");\n    }\n\n    private EventListenerManager() {\n    }\n\n    /**\n     * k1: viewInjectInfo\n     * k2: interface Type\n     * value: listener\n     */\n    private final static DoubleKeyValueMap<ViewInfo, Class<?>, Object>\n            listenerCache = new DoubleKeyValueMap<ViewInfo, Class<?>, Object>();\n\n\n    public static void addEventMethod(\n            //根据页面或view holder生成的ViewFinder\n            ViewFinder finder,\n            //根据当前注解ID生成的ViewInfo\n            ViewInfo info,\n            //注解对象\n            Event event,\n            //页面或view holder对象\n            Object handler,\n            //当前注解方法\n            Method method) {\n        try {\n            View view = finder.findViewByInfo(info);\n\n            if (view != null) {\n                // 注解中定义的接口，比如Event注解默认的接口为View.OnClickListener\n                Class<?> listenerType = event.type();\n                // 默认为空，注解接口对应的Set方法，比如setOnClickListener方法\n                String listenerSetter = event.setter();\n                if (TextUtils.isEmpty(listenerSetter)) {\n                    listenerSetter = \"set\" + listenerType.getSimpleName();\n                }\n\n\n                String methodName = event.method();\n\n                boolean addNewMethod = false;\n                /*\n                    根据View的ID和当前的接口类型获取已经缓存的接口实例对象，\n                    比如根据View.id和View.OnClickListener.class两个键获取这个View的OnClickListener对象\n                 */\n                Object listener = listenerCache.get(info, listenerType);\n                DynamicHandler dynamicHandler = null;\n                /*\n                    如果接口实例对象不为空\n                    获取接口对象对应的动态代理对象\n                    如果动态代理对象的handler和当前handler相同\n                    则为动态代理对象添加代理方法\n                 */\n                if (listener != null) {\n                    dynamicHandler = (DynamicHandler) Proxy.getInvocationHandler(listener);\n                    addNewMethod = handler.equals(dynamicHandler.getHandler());\n                    if (addNewMethod) {\n                        dynamicHandler.addMethod(methodName, method);\n                    }\n                }\n\n                // 如果还没有注册此代理\n                if (!addNewMethod) {\n\n                    dynamicHandler = new DynamicHandler(handler);\n\n                    dynamicHandler.addMethod(methodName, method);\n\n                    // 生成的代理对象实例，比如View.OnClickListener的实例对象\n                    listener = Proxy.newProxyInstance(\n                            listenerType.getClassLoader(),\n                            new Class<?>[]{listenerType},\n                            dynamicHandler);\n\n                    listenerCache.put(info, listenerType, listener);\n                }\n\n                Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);\n                setEventListenerMethod.invoke(view, listener);\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n    }\n\n    public static class DynamicHandler implements InvocationHandler {\n        // 存放代理对象，比如Fragment或view holder\n        private WeakReference<Object> handlerRef;\n        // 存放代理方法\n        private final HashMap<String, Method> methodMap = new HashMap<String, Method>(1);\n\n        private static long lastClickTime = 0;\n\n        public DynamicHandler(Object handler) {\n            this.handlerRef = new WeakReference<Object>(handler);\n        }\n\n        public void addMethod(String name, Method method) {\n            methodMap.put(name, method);\n        }\n\n        public Object getHandler() {\n            return handlerRef.get();\n        }\n\n        @Override\n        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n            Object handler = handlerRef.get();\n            if (handler != null) {\n\n                String eventMethod = method.getName();\n                if (\"toString\".equals(eventMethod)) {\n                    return DynamicHandler.class.getSimpleName();\n                }\n\n                method = methodMap.get(eventMethod);\n                if (method == null && methodMap.size() == 1) {\n                    for (Map.Entry<String, Method> entry : methodMap.entrySet()) {\n                        if (TextUtils.isEmpty(entry.getKey())) {\n                            method = entry.getValue();\n                        }\n                        break;\n                    }\n                }\n\n                if (method != null) {\n\n                    if (AVOID_QUICK_EVENT_SET.contains(eventMethod)) {\n                        long timeSpan = System.currentTimeMillis() - lastClickTime;\n                        if (timeSpan < QUICK_EVENT_TIME_SPAN) {\n                            LogUtil.d(\"onClick cancelled: \" + timeSpan);\n                            return null;\n                        }\n                        lastClickTime = System.currentTimeMillis();\n                    }\n\n                    try {\n                        return method.invoke(handler, args);\n                    } catch (Throwable ex) {\n                        throw new RuntimeException(\"invoke method error:\" +\n                                handler.getClass().getName() + \"#\" + method.getName(), ex);\n                    }\n                } else {\n                    LogUtil.w(\"method not impl: \" + eventMethod + \"(\" + handler.getClass().getSimpleName() + \")\");\n                }\n            }\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/view/ViewFinder.java",
    "content": "package org.xutils.view;\n\nimport android.app.Activity;\nimport android.view.View;\n\n/**\n * Author: wyouflf\n * Date: 13-9-9\n * Time: 下午12:29\n */\n/*package*/ final class ViewFinder {\n\n    private View view;\n    private Activity activity;\n\n    public ViewFinder(View view) {\n        this.view = view;\n    }\n\n    public ViewFinder(Activity activity) {\n        this.activity = activity;\n    }\n\n    public View findViewById(int id) {\n        if (view != null) return view.findViewById(id);\n        if (activity != null) return activity.findViewById(id);\n        return null;\n    }\n\n    public View findViewByInfo(ViewInfo info) {\n        return findViewById(info.value, info.parentId);\n    }\n\n    public View findViewById(int id, int pid) {\n        View pView = null;\n        if (pid > 0) {\n            pView = this.findViewById(pid);\n        }\n\n        View view = null;\n        if (pView != null) {\n            view = pView.findViewById(id);\n        } else {\n            view = this.findViewById(id);\n        }\n        return view;\n    }\n\n    /*public Context getContext() {\n        if (view != null) return view.getContext();\n        if (activity != null) return activity;\n        return null;\n    }*/\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/view/ViewInfo.java",
    "content": "package org.xutils.view;\n\n/**\n * Author: wyouflf\n * Date: 13-12-5\n * Time: 下午11:25\n */\n/*package*/ final class ViewInfo {\n    public int value;\n    public int parentId;\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        ViewInfo viewInfo = (ViewInfo) o;\n\n        if (value != viewInfo.value) return false;\n        return parentId == viewInfo.parentId;\n\n    }\n\n    @Override\n    public int hashCode() {\n        int result = value;\n        result = 31 * result + parentId;\n        return result;\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/view/ViewInjectorImpl.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.view;\n\nimport android.app.Activity;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport org.xutils.ViewInjector;\nimport org.xutils.common.util.LogUtil;\nimport org.xutils.view.annotation.ContentView;\nimport org.xutils.view.annotation.Event;\nimport org.xutils.view.annotation.ViewInject;\nimport org.xutils.x;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.util.HashSet;\n\npublic final class ViewInjectorImpl implements ViewInjector {\n\n    private static final HashSet<Class<?>> IGNORED = new HashSet<Class<?>>();\n\n    static {\n        IGNORED.add(Object.class);\n        IGNORED.add(Activity.class);\n        IGNORED.add(android.app.Fragment.class);\n        try {\n            IGNORED.add(Class.forName(\"android.support.v4.app.Fragment\"));\n            IGNORED.add(Class.forName(\"android.support.v4.app.FragmentActivity\"));\n        } catch (Throwable ignored) {\n        }\n    }\n\n    private static final Object lock = new Object();\n    private static volatile ViewInjectorImpl instance;\n\n    private ViewInjectorImpl() {\n    }\n\n    public static void registerInstance() {\n        if (instance == null) {\n            synchronized (lock) {\n                if (instance == null) {\n                    instance = new ViewInjectorImpl();\n                }\n            }\n        }\n        x.Ext.setViewInjector(instance);\n    }\n\n    @Override\n    public void inject(View view) {\n        injectObject(view, view.getClass(), new ViewFinder(view));\n    }\n\n    @Override\n    public void inject(Activity activity) {\n        //获取Activity的ContentView的注解\n        Class<?> handlerType = activity.getClass();\n        try {\n            ContentView contentView = findContentView(handlerType);\n            if (contentView != null) {\n                int viewId = contentView.value();\n                if (viewId > 0) {\n                    Method setContentViewMethod = handlerType.getMethod(\"setContentView\", int.class);\n                    setContentViewMethod.invoke(activity, viewId);\n                }\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n        injectObject(activity, handlerType, new ViewFinder(activity));\n    }\n\n    @Override\n    public void inject(Object handler, View view) {\n        injectObject(handler, handler.getClass(), new ViewFinder(view));\n    }\n\n    @Override\n    public View inject(Object fragment, LayoutInflater inflater, ViewGroup container) {\n        // inject ContentView\n        View view = null;\n        Class<?> handlerType = fragment.getClass();\n        try {\n            ContentView contentView = findContentView(handlerType);\n            if (contentView != null) {\n                int viewId = contentView.value();\n                if (viewId > 0) {\n                    view = inflater.inflate(viewId, container, false);\n                }\n            }\n        } catch (Throwable ex) {\n            LogUtil.e(ex.getMessage(), ex);\n        }\n\n        // inject res & event\n        injectObject(fragment, handlerType, new ViewFinder(view));\n\n        return view;\n    }\n\n    /**\n     * 从父类获取注解View\n     */\n    private static ContentView findContentView(Class<?> thisCls) {\n        if (thisCls == null || IGNORED.contains(thisCls)) {\n            return null;\n        }\n        ContentView contentView = thisCls.getAnnotation(ContentView.class);\n        if (contentView == null) {\n            return findContentView(thisCls.getSuperclass());\n        }\n        return contentView;\n    }\n\n    @SuppressWarnings(\"ConstantConditions\")\n    private static void injectObject(Object handler, Class<?> handlerType, ViewFinder finder) {\n\n        if (handlerType == null || IGNORED.contains(handlerType)) {\n            return;\n        }\n\n        // 从父类到子类递归\n        injectObject(handler, handlerType.getSuperclass(), finder);\n\n        // inject view\n        Field[] fields = handlerType.getDeclaredFields();\n        if (fields != null && fields.length > 0) {\n            for (Field field : fields) {\n\n                Class<?> fieldType = field.getType();\n                if (\n                /* 不注入静态字段 */     Modifier.isStatic(field.getModifiers()) ||\n                /* 不注入final字段 */    Modifier.isFinal(field.getModifiers()) ||\n                /* 不注入基本类型字段 */  fieldType.isPrimitive() ||\n                /* 不注入数组类型字段 */  fieldType.isArray()) {\n                    continue;\n                }\n\n                ViewInject viewInject = field.getAnnotation(ViewInject.class);\n                if (viewInject != null) {\n                    try {\n                        View view = finder.findViewById(viewInject.value(), viewInject.parentId());\n                        if (view != null) {\n                            field.setAccessible(true);\n                            field.set(handler, view);\n                        } else {\n                            throw new RuntimeException(\"Invalid @ViewInject for \"\n                                    + handlerType.getSimpleName() + \".\" + field.getName());\n                        }\n                    } catch (Throwable ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }\n        } // end inject view\n\n        // inject event\n        Method[] methods = handlerType.getDeclaredMethods();\n        if (methods != null && methods.length > 0) {\n            for (Method method : methods) {\n\n                if (Modifier.isStatic(method.getModifiers())\n                        || !Modifier.isPrivate(method.getModifiers())) {\n                    continue;\n                }\n\n                //检查当前方法是否是event注解的方法\n                Event event = method.getAnnotation(Event.class);\n                if (event != null) {\n                    try {\n                        // id参数\n                        int[] values = event.value();\n                        int[] parentIds = event.parentId();\n                        int parentIdsLen = parentIds == null ? 0 : parentIds.length;\n                        //循环所有id，生成ViewInfo并添加代理反射\n                        for (int i = 0; i < values.length; i++) {\n                            int value = values[i];\n                            if (value > 0) {\n                                ViewInfo info = new ViewInfo();\n                                info.value = value;\n                                info.parentId = parentIdsLen > i ? parentIds[i] : 0;\n                                method.setAccessible(true);\n                                EventListenerManager.addEventMethod(finder, info, event, handler, method);\n                            }\n                        }\n                    } catch (Throwable ex) {\n                        LogUtil.e(ex.getMessage(), ex);\n                    }\n                }\n            }\n        } // end inject event\n\n    }\n\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/view/annotation/ContentView.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.view.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ContentView {\n    int value();\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/view/annotation/Event.java",
    "content": "package org.xutils.view.annotation;\n\nimport android.view.View;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 事件注解.\n * 被注解的方法必须具备以下形式:\n * 1. private 修饰\n * 2. 返回值类型没有要求\n * 3. 参数签名和type的接口要求的参数签名一致.\n * Author: wyouflf\n * Date: 13-9-9\n * Time: 下午12:43\n */\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Event {\n\n    /**\n     * 控件的id集合, id小于1时不执行ui事件绑定.\n     *\n     * @return\n     */\n    int[] value();\n\n    /**\n     * 控件的parent控件的id集合, 组合为(value[i], parentId[i] or 0).\n     *\n     * @return\n     */\n    int[] parentId() default 0;\n\n    /**\n     * 事件的listener, 默认为点击事件.\n     *\n     * @return\n     */\n    Class<?> type() default View.OnClickListener.class;\n\n    /**\n     * 事件的setter方法名, 默认为set+type#simpleName.\n     *\n     * @return\n     */\n    String setter() default \"\";\n\n    /**\n     * 如果type的接口类型提供多个方法, 需要使用此参数指定方法名.\n     *\n     * @return\n     */\n    String method() default \"\";\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/view/annotation/ViewInject.java",
    "content": "/*\n * Copyright (c) 2013. wyouflf (wyouflf@gmail.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage org.xutils.view.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target(ElementType.FIELD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface ViewInject {\n\n    int value();\n\n    /* parent view id */\n    int parentId() default 0;\n}\n"
  },
  {
    "path": "xutils/src/main/java/org/xutils/x.java",
    "content": "package org.xutils;\n\nimport android.app.Application;\nimport android.content.Context;\n\nimport org.xutils.common.TaskController;\nimport org.xutils.common.task.TaskControllerImpl;\nimport org.xutils.db.DbManagerImpl;\nimport org.xutils.http.HttpManagerImpl;\nimport org.xutils.image.ImageManagerImpl;\nimport org.xutils.view.ViewInjectorImpl;\n\nimport java.lang.reflect.Method;\n\n\n/**\n * Created by wyouflf on 15/6/10.\n * 任务控制中心, http, image, db, view注入等接口的入口.\n * 需要在在application的onCreate中初始化: x.Ext.init(this);\n */\npublic final class x {\n\n    private x() {\n    }\n\n    public static boolean isDebug() {\n        return Ext.debug;\n    }\n\n    public static Application app() {\n        if (Ext.app == null) {\n            try {\n                // 在IDE进行布局预览时使用\n                Class<?> renderActionClass = Class.forName(\"com.android.layoutlib.bridge.impl.RenderAction\");\n                Method method = renderActionClass.getDeclaredMethod(\"getCurrentContext\");\n                Context context = (Context) method.invoke(null);\n                Ext.app = new MockApplication(context);\n            } catch (Throwable ignored) {\n                throw new RuntimeException(\"please invoke x.Ext.init(app) on Application#onCreate()\"\n                        + \" and register your Application in manifest.\");\n            }\n        }\n        return Ext.app;\n    }\n\n    public static TaskController task() {\n        return Ext.taskController;\n    }\n\n    public static HttpManager http() {\n        if (Ext.httpManager == null) {\n            HttpManagerImpl.registerInstance();\n        }\n        return Ext.httpManager;\n    }\n\n    public static ImageManager image() {\n        if (Ext.imageManager == null) {\n            ImageManagerImpl.registerInstance();\n        }\n        return Ext.imageManager;\n    }\n\n    public static ViewInjector view() {\n        if (Ext.viewInjector == null) {\n            ViewInjectorImpl.registerInstance();\n        }\n        return Ext.viewInjector;\n    }\n\n    public static DbManager getDb(DbManager.DaoConfig daoConfig) {\n        return DbManagerImpl.getInstance(daoConfig);\n    }\n\n    public static class Ext {\n        private static boolean debug;\n        private static Application app;\n        private static TaskController taskController;\n        private static HttpManager httpManager;\n        private static ImageManager imageManager;\n        private static ViewInjector viewInjector;\n\n        private Ext() {\n        }\n\n        public static void init(Application app) {\n            TaskControllerImpl.registerInstance();\n            if (Ext.app == null) {\n                Ext.app = app;\n            }\n        }\n\n        public static void setDebug(boolean debug) {\n            Ext.debug = debug;\n        }\n\n        public static void setTaskController(TaskController taskController) {\n            if (Ext.taskController == null) {\n                Ext.taskController = taskController;\n            }\n        }\n\n        public static void setHttpManager(HttpManager httpManager) {\n            Ext.httpManager = httpManager;\n        }\n\n        public static void setImageManager(ImageManager imageManager) {\n            Ext.imageManager = imageManager;\n        }\n\n        public static void setViewInjector(ViewInjector viewInjector) {\n            Ext.viewInjector = viewInjector;\n        }\n    }\n\n    private static class MockApplication extends Application {\n        public MockApplication(Context baseContext) {\n            this.attachBaseContext(baseContext);\n        }\n    }\n}\n"
  },
  {
    "path": "xutils/src/main/java_compat/android/backport/webp/WebPFactory.java",
    "content": "package android.backport.webp;\n\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\n\nimport java.io.ByteArrayOutputStream;\n\n/**\n * Factory to encode and decode WebP images into Android Bitmap\n *\n * @author Alexey Pelykh\n */\n@SuppressWarnings(\"JniMissingFunction\")\npublic final class WebPFactory {\n\n    private static boolean loadSoLibError = false;\n\n    static {\n        // 部分4.x的设备仍然不能很好的兼容webp, 需要借助libwebp.\n        try {\n            System.loadLibrary(\"webpbackport\");\n        } catch (Throwable ex) {\n            loadSoLibError = true;\n        }\n    }\n\n    private WebPFactory() {\n    }\n\n    public static boolean available() {\n        return !loadSoLibError;\n    }\n\n    /**\n     * Decodes byte array to bitmap\n     *\n     * @param data    Byte array with WebP bitmap data\n     * @param options Options to control decoding. Accepts null\n     * @return Decoded bitmap\n     */\n    public static Bitmap decodeByteArray(byte[] data, BitmapFactory.Options options) {\n        if (available()) {\n            return nativeDecodeByteArray(data, options);\n        } else {\n            return BitmapFactory.decodeByteArray(data, 0, data.length, options);\n        }\n    }\n\n    /**\n     * Decodes file to bitmap\n     *\n     * @param path    WebP file path\n     * @param options Options to control decoding. Accepts null\n     * @return Decoded bitmap\n     */\n    public static Bitmap decodeFile(String path, BitmapFactory.Options options) {\n        if (available()) {\n            return nativeDecodeFile(path, options);\n        } else {\n            return BitmapFactory.decodeFile(path, options);\n        }\n    }\n\n    /**\n     * Encodes bitmap into byte array\n     *\n     * @param bitmap  Bitmap\n     * @param quality Quality, should be between 0 and 100\n     * @return Encoded byte array\n     */\n    public static byte[] encodeBitmap(Bitmap bitmap, int quality) {\n        if (available()) {\n            return nativeEncodeBitmap(bitmap, quality);\n        } else {\n            ByteArrayOutputStream out = new ByteArrayOutputStream();\n            bitmap.compress(Bitmap.CompressFormat.WEBP, quality, out);\n            return out.toByteArray();\n        }\n    }\n\n    /**\n     * Decodes byte array to bitmap\n     *\n     * @param data    Byte array with WebP bitmap data\n     * @param options Options to control decoding. Accepts null\n     * @return Decoded bitmap\n     */\n    private static native Bitmap nativeDecodeByteArray(byte[] data, BitmapFactory.Options options);\n\n    /**\n     * Decodes file to bitmap\n     *\n     * @param path    WebP file path\n     * @param options Options to control decoding. Accepts null\n     * @return Decoded bitmap\n     */\n    private static native Bitmap nativeDecodeFile(String path, BitmapFactory.Options options);\n\n    /**\n     * Encodes bitmap into byte array\n     *\n     * @param bitmap  Bitmap\n     * @param quality Quality, should be between 0 and 100\n     * @return Encoded byte array\n     */\n    private static native byte[] nativeEncodeBitmap(Bitmap bitmap, int quality);\n}\n"
  }
]