Repository: zhaozepeng/Android_framework
Branch: master
Commit: 216cd58506ae
Files: 178
Total size: 1.0 MB
Directory structure:
gitextract_h9zek9ps/
├── .gitignore
├── .idea/
│ ├── compiler.xml
│ ├── copyright/
│ │ └── profiles_settings.xml
│ ├── gradle.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ └── vcs.xml
├── LICENSE
├── README.md
├── README.md~
├── build.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── libcore/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── android/
│ │ └── libcore/
│ │ ├── Toast/
│ │ │ └── T.java
│ │ ├── activity/
│ │ │ ├── ActivityManager.java
│ │ │ ├── RootActivity.java
│ │ │ └── RootFragment.java
│ │ ├── application/
│ │ │ └── RootApplication.java
│ │ ├── cachemanager/
│ │ │ └── CacheManager.java
│ │ ├── database/
│ │ │ ├── BaseDB.java
│ │ │ ├── BaseDBHelper.java
│ │ │ └── IBaseDBTable.java
│ │ ├── dialog/
│ │ │ └── BaseDialog.java
│ │ ├── download/
│ │ │ ├── DownloadDB.java
│ │ │ ├── DownloadDBHelper.java
│ │ │ └── FileDownloadManager.java
│ │ ├── guide/
│ │ │ └── GuideManager.java
│ │ ├── log/
│ │ │ └── L.java
│ │ ├── net/
│ │ │ ├── NetError.java
│ │ │ ├── imageloader/
│ │ │ │ ├── ImageLoader.java
│ │ │ │ └── VolleyLruCache.java
│ │ │ └── netapi/
│ │ │ └── BaseNetApi.java
│ │ ├── utils/
│ │ │ ├── CommonUtils.java
│ │ │ ├── FileUtils.java
│ │ │ └── ImageUtils.java
│ │ └── volley/
│ │ ├── BaseVolleyApi.java
│ │ └── VolleyLruCache.java
│ └── res/
│ └── values/
│ └── strings.xml
├── libcore-ui/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── android/
│ │ └── libcore_ui/
│ │ ├── activity/
│ │ │ ├── BaseActivity.java
│ │ │ ├── BaseActivityWithPopWindow.java
│ │ │ ├── BaseFragment.java
│ │ │ └── widget/
│ │ │ └── BottomBarGroupLinearLayout.java
│ │ ├── application/
│ │ │ └── BaseApplication.java
│ │ ├── dialog/
│ │ │ ├── AppDialog.java
│ │ │ ├── DialogCreator.java
│ │ │ └── LoadingDialog.java
│ │ ├── net/
│ │ │ ├── NetApi.java
│ │ │ └── request/
│ │ │ └── XMLRequest.java
│ │ ├── permanentdbcache/
│ │ │ ├── PermanentCacheDB.java
│ │ │ └── PermanentCacheDBHelper.java
│ │ ├── volley/
│ │ │ ├── VolleyApi.java
│ │ │ └── request/
│ │ │ └── XMLRequest.java
│ │ ├── web/
│ │ │ ├── WebFragment.java
│ │ │ └── webactivity/
│ │ │ ├── WebActivity.java
│ │ │ └── WebActivityFragment.java
│ │ └── widget/
│ │ ├── FlowLayout.java
│ │ └── SimpleGridLayout.java
│ └── res/
│ ├── drawable/
│ │ ├── base_dialog_shape.xml
│ │ ├── bg_edittext.xml
│ │ ├── bg_edittext_focus.xml
│ │ ├── bg_edittext_not_focus.xml
│ │ ├── bg_loading_dialog.xml
│ │ ├── bg_progress_bar.xml
│ │ ├── bottom_button_all_selector.xml
│ │ ├── bottom_button_bottom_selector.xml
│ │ ├── bottom_button_middle_selector.xml
│ │ ├── bottom_button_shape.xml
│ │ ├── bottom_button_top_selector.xml
│ │ ├── dialog_button_bottom_selector.xml
│ │ ├── dialog_button_bottomleft_selector.xml
│ │ ├── dialog_button_bottomright_selector.xml
│ │ └── dialog_button_middle_selector.xml
│ ├── layout/
│ │ ├── activity_base_layout.xml
│ │ ├── activity_base_layout_with_popwindow.xml
│ │ ├── activity_top_bar_layout.xml
│ │ ├── activity_top_toolbar_layout.xml
│ │ ├── activity_web_layout.xml
│ │ ├── bottom_group_layout.xml
│ │ ├── bottom_item_layout.xml
│ │ ├── bottom_popwindow_layout.xml
│ │ ├── dialog_base_layout.xml
│ │ ├── dialog_item_button_layout.xml
│ │ ├── dialog_js_prompt_message_layout.xml
│ │ ├── loading_dialog_layout.xml
│ │ └── menu_refresh_layout.xml
│ ├── menu/
│ │ └── menu_webactivity_refresh.xml
│ ├── values/
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ ├── values-v19/
│ │ └── styles.xml
│ ├── values-v21/
│ │ └── styles.xml
│ └── values-v23/
│ └── styles.xml
├── settings.gradle
└── testsample/
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src/
└── main/
├── AndroidManifest.xml
├── assets/
│ ├── 1.html
│ ├── baidu.html
│ └── baidu_files/
│ ├── activity_start_52498d2c.js
│ ├── all_async_search_57459356.js
│ ├── jquery-1.10.2_d88366fd.js
│ ├── min_nav_9dda26ef.js
│ ├── min_super_6a1867d5.js
│ ├── min_tips_27647c7d.js
│ ├── nav_min_22274039.css
│ ├── sbase_6ae84319.js
│ ├── super_min_de974358.css
│ └── xmancard_c006cb6f.js
├── java/
│ └── com/
│ └── android/
│ └── sample/
│ ├── HomeTestActivity.java
│ ├── test_activity/
│ │ ├── ActivityA.java
│ │ ├── ActivityB.java
│ │ └── ActivityTestHomePage.java
│ ├── test_cache/
│ │ └── CacheActivity.java
│ ├── test_db/
│ │ ├── DBActivity.java
│ │ └── db/
│ │ ├── StudentDB.java
│ │ └── StudentHelper.java
│ ├── test_dialog/
│ │ └── DialogActivity.java
│ ├── test_download/
│ │ └── DownloadActivity.java
│ ├── test_guide/
│ │ └── GuideActivity.java
│ ├── test_imageloader/
│ │ ├── ImageActivity.java
│ │ └── PicUrl.java
│ ├── test_netapi/
│ │ └── NetActivity.java
│ ├── test_utils/
│ │ ├── CommonActivity.java
│ │ ├── FileActivity.java
│ │ ├── ImageActivity.java
│ │ └── UtilsActivity.java
│ ├── test_volley/
│ │ └── VolleyActivity.java
│ ├── test_webview/
│ │ ├── TestWebFragment.java
│ │ └── WebViewActivity.java
│ └── test_widget/
│ ├── FlowLayoutActivity.java
│ ├── GridLayoutActivity.java
│ └── WidgetActivity.java
└── res/
├── drawable/
│ ├── button_forthpage_selector.xml
│ ├── button_homepage_selector.xml
│ ├── button_secondpage_selector.xml
│ └── button_thirdpage_selector.xml
├── layout/
│ ├── activity_home_test.xml
│ ├── activity_test_activity_a.xml
│ ├── activity_test_activity_b.xml
│ ├── activity_test_activity_homepage.xml
│ ├── activity_test_cache.xml
│ ├── activity_test_common.xml
│ ├── activity_test_db.xml
│ ├── activity_test_dialog.xml
│ ├── activity_test_download.xml
│ ├── activity_test_file.xml
│ ├── activity_test_flow.xml
│ ├── activity_test_grid.xml
│ ├── activity_test_guide.xml
│ ├── activity_test_image.xml
│ ├── activity_test_imageloader.xml
│ ├── activity_test_net.xml
│ ├── activity_test_utils.xml
│ ├── activity_test_webview.xml
│ ├── activity_test_widget.xml
│ ├── guide_test_1.xml
│ ├── guide_test_2.xml
│ ├── guide_test_3.xml
│ └── menu_activity_home_test_search.xml
├── menu/
│ └── menu_activity_home_test.xml
└── values/
├── colors.xml
├── strings.xml
├── strings_activity.xml
└── styles.xml
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Built application files
*.apk
*.ap_
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Intellij
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/libraries
# Keystore files
*.jks
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
================================================
FILE: .idea/compiler.xml
================================================
================================================
FILE: .idea/copyright/profiles_settings.xml
================================================
================================================
FILE: .idea/gradle.xml
================================================
================================================
FILE: .idea/misc.xml
================================================
================================================
FILE: .idea/modules.xml
================================================
================================================
FILE: .idea/runConfigurations.xml
================================================
================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: LICENSE
================================================
Copyright (c) 2015, zhao_zepeng
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# android_framework #
an android framework in order for rapid development
##1.开发环境##
环境为*android studio* + *jdk1.7*

##2.框架结构##
该框架分为三个部分:
libcore 层
libcore-ui 层
testsample 层
第三层为测试代码,重点在第一和第二层...
##3.框架详细概述##
###第一层libcore###
该层为基础核心代码层,该层的代码特点是封装了应用所应该使用的基础功能,好处是封装的功能仅仅提供简单的接口,使得应用只需要更改libcore的封装实现,而不用修改底层的代码,从而轻松实现功能的变更,功能列表如下所示
Application封装,使用了weakRefrence指向当前Activity的context,方便使用,还增加了应用crash,应用关闭等处理
最基础的Activity和Fragment类,配合Application类和ActivityManager类进行Activity的集中管理
ActivityManager类,使用栈来管理所有的activity
BaseNetApi类,实现了网络请求,网络访问的底层使用的是volley框架。现在提供两种方案:第一种就是可更换的网络框架,为了可更换,就要对volley进行大程度的封装,所有子module不可直接使用volley相关类,以后网络框架更改,只需要修改这几个相关类即可,由于volley功能的复杂性,只能封装最基本的网络访问功能,tag和cache等功能由于不通用性,暂时不封装;
BaseVolleyApi类,第二种就是纯粹的volley框架,不会更换网络访问框架,只需对其进行最基本的封装,所有功能类都可在子module中使用,所以会增大项目对volley框架的耦合性
ImageLoader类,用来进行图片的加载,封装的是volley的imageloader功能,支持lrucache和sd卡二级存储功能
log类,用来打印log,打印的日志信息非常完整
Toast类,该类用来弹出toast,支持弹出toast的位置
GuideManager类,用来显示指引蒙版,支持全屏展示和只在内容区域展示
BaseDialog类,定义了一个应用dialog所应该具备的基础行为
数据库相关类,将数据库类进行了非常便捷的封装,创建数据库应该继承自BaseDB类,为了该数据库的访问应该再创建一个helper类继承自BaseDBHelper,封装该数据库的所有操作,另外还需要对表名和列名进行了枚举的封装,这样使用该数据库直接使用该枚举类获取表名和表的相关列名。对数据库的版本升级也做了相应快捷的处理
CacheManager类,这个类使用SharedPreference来存储基本对象,有临时和永久两种,临时存储将会在每次应用退出之后自动清空,永久存储则永久存储
FileDownloadManager类,用来下载相关文件,为多线程断点续传式下载,支持开始,停止和删除操作
Utils类:
CommonUtils,用来集中管理一些杂项函数,比如dp2px等
FileUtils,用来管理文件的相关操作
ImageUtils,用来处理图片的相关操作
###第二层libcore-ui层###
该层为基础核心扩展层,扩展libcore的层的代码,并且定义应用的基本样式,够统一样式,方便管理
扩展实现的Activity和Fragment类,BaseActivity类中定义了整个应用的基本简单样式(现在提供两种样式,顶部透明样式和底部透明样式),顶部bar的样式(顶部bar有自定义bar和系统控件toolbar)等,BaseActivityWithPopWindow类继承BaseActivity类,实现了底部的弹出框;Fragment类定义了fragment和activity之间的通信方式和topbar的交互,
NetApi类,NetApi继承自BaseNetApi类,在原来的基础上扩展相关的功能,支持自定义继承自volley request的请求,额外的处理就只是在NetApi类中添加相应的函数
VolleyApi类,用来对BaseVolleyApi类进行功能扩展
PermanentCacheDB类,用来存储一些和应用生命周期相关的变量,写入数据库,永久保存
WebFragment类,该fragment用来显示网页,可以单独作为一个fragment嵌入一个页面的任何地方
WebActivity类,该activity用来展示网页,传入url即可显示网页,有进度条和刷新操作
AppDialog类,该类继承自父类BaseDialog类,定义了基本的样式,实现了基础的行为
DialogCreator类,该类用来生成基本样式的dialog
LoadingDialog类,用来定义一个应用最基本的加载框
基本实用的控件:
SimpleGridLayout类,自定义网格布局,自动换行,自定义attr
FlowLayout类,自定义流式布局,支持方向选择,自动换行,自定义attr
###第三层应用层###
该层为模拟应用层,用来测试下层的代码。一个应用在使用了libcore层和libcore-ui层之后,可能还需要在封装一层或几层module,但是保证最基础的两层是应用所通用的module即可
================================================
FILE: README.md~
================================================
# android_framework #
an android framework in order for rapid development
##1.开发环境##
环境为*android studio* + *jdk1.7*

##2.框架结构##
该框架分为三个部分:
libcore 层
libcore-ui 层
testsample 层
第三层为测试代码,重点在第一和第二层...
##3.框架详细概述##
###第一层libcore###
该层为基础核心代码层,该层的代码特点是封装了应用所应该使用的基础功能,好处是封装的功能仅仅提供简单的接口,使得应用只需要更改libcore的封装实现,而不用修改底层的代码,从而轻松实现功能的变更,功能列表如下所示
Application封装,使用了weakRefrence指向当前Activity的context,方便使用,还增加了应用crash,应用关闭等处理
最基础的Activity和Fragment类,配合Application类和ActivityManager类进行Activity的集中管理
ActivityManager类,使用栈来管理所有的activity
BaseNetApi类,实现了网络请求,网络访问的底层使用的是volley框架。现在提供两种方案:第一种就是可更换的网络框架,为了可更换,就要对volley进行大程度的封装,所有子module不可直接使用volley相关类,以后网络框架更改,只需要修改这几个相关类即可,由于volley功能的复杂性,只能封装最基本的网络访问功能,tag和cache等功能由于不通用性,暂时不封装;
BaseVolleyApi类,第二种就是纯粹的volley框架,不会更换网络访问框架,只需对其进行最基本的封装,所有功能类都可在子module中使用,所以会增大项目对volley框架的耦合性
ImageLoader类,用来进行图片的加载,封装的是volley的imageloader功能,支持lrucache和sd卡二级存储功能
log类,用来打印log,打印的日志信息非常完整
Toast类,该类用来弹出toast,支持弹出toast的位置
GuideManager类,用来显示指引蒙版,支持全屏展示和只在内容区域展示
BaseDialog类,定义了一个应用dialog所应该具备的基础行为
数据库相关类,将数据库类进行了非常便捷的封装,创建数据库应该继承自BaseDB类,为了该数据库的访问应该再创建一个helper类继承自BaseDBHelper,封装该数据库的所有操作,另外还需要对表名和列名进行了枚举的封装,这样使用该数据库直接使用该枚举类获取表名和表的相关列名。对数据库的版本升级也做了相应快捷的处理
CacheManager类,这个类使用SharedPreference来存储基本对象,有临时和永久两种,临时存储将会在每次应用退出之后自动清空,永久存储则永久存储
FileDownloadManager类,用来下载相关文件,为多线程断点续传式下载,支持开始,停止和删除操作
Utils类:
CommonUtils,用来集中管理一些杂项函数,比如dp2px等
FileUtils,用来管理文件的相关操作
ImageUtils,用来处理图片的相关操作
###第二层libcore-ui层###
该层为基础核心扩展层,扩展libcore的层的代码,并且定义应用的基本样式,够统一样式,方便管理
扩展实现的Activity和Fragment类,BaseActivity类中定义了整个应用的基本简单样式(现在提供两种样式,顶部透明样式和底部透明样式),顶部bar的样式(顶部bar有自定义bar和系统控件toolbar)等,BaseActivityWithPopWindow类继承BaseActivity类,实现了底部的弹出框;Fragment类定义了fragment和activity之间的通信方式和topbar的交互,
NetApi类,NetApi继承自BaseNetApi类,在原来的基础上扩展相关的功能,支持自定义继承自volley request的请求,额外的处理就只是在NetApi类中添加相应的函数
VolleyApi类,用来对BaseVolleyApi类进行功能扩展
PermanentCacheDB类,用来存储一些和应用生命周期相关的变量,写入数据库,永久保存
WebFragment类,该fragment用来显示网页,可以单独作为一个fragment嵌入一个页面的任何地方
WebActivity类,该activity用来展示网页,传入url即可显示网页,有进度条和刷新操作
AppDialog类,该类继承自父类BaseDialog类,定义了基本的样式,实现了基础的行为
DialogCreator类,该类用来生成基本样式的dialog
LoadingDialog类,用来定义一个应用最基本的加载框
基本实用的控件:
AutomaticNewlineLinearLayout类是自动换行的linearLayout,自定义了attr
###第三层应用层###
该层为模拟应用层,用来测试下层的代码。一个应用在使用了libcore层和libcore-ui层之后,可能还需要在封装一层或几层module,但是保证最基础的两层是应用所通用的module即可
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
mavenCentral()
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Mar 03 10:38:35 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: libcore/.gitignore
================================================
/build
================================================
FILE: libcore/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion '25.0.0'
defaultConfig {
minSdkVersion 11
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.mcxiaoke.volley:library:1.0.19'
}
================================================
FILE: libcore/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/zzp/adt-bundle-linux-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: libcore/src/main/AndroidManifest.xml
================================================
================================================
FILE: libcore/src/main/java/com/android/libcore/Toast/T.java
================================================
package com.android.libcore.Toast;
import android.view.Gravity;
import android.view.View;
import android.widget.Toast;
import com.android.libcore.application.RootApplication;
/**
* Description: Toast类
* 如果需要设置gravity,请使用{@link #getInstance()}.{@link #setGravity(int)}方法进行设置
* 如果需要额外设置{@link #xOffset}或者{@link #yOffset}请调用对应{@link #setxOffset(int)}和
* {@link #setyOffset(int)}即可,用法和gravity一致
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-09
*/
public class T {
private volatile static T instance;
private int mGravity = -1;
private int xOffset = 0;
private int yOffset = 0;
private Toast mTemp;
public static T getInstance() {
if (instance == null){
synchronized (T.class){
if (instance == null){
instance = new T();
}
}
}
return instance;
}
private T(){
}
/**
* 设置该toast的显示位置,只对该toast有效
*/
public T setGravity(int mGravity) {
this.mGravity = mGravity;
return getInstance();
}
/**
* 请在{@link #setGravity(int)}调用之后调用,只对该toast有效
*/
public T setxOffset(int xOffset) {
this.xOffset = xOffset;
return getInstance();
}
/**
* 请在{@link #setGravity(int)}调用之后调用,只对该toast有效
*/
public T setyOffset(int yOffset) {
this.yOffset = yOffset;
return getInstance();
}
public void showShort(String message){
showShort(message, null);
}
/**
* 需要在toast中显示的v
*/
public void showShort(String message, View v){
//防止一堆toast的显示堆积
if (mTemp != null)
mTemp.cancel();
mTemp = Toast.makeText(RootApplication.getInstance(), message, Toast.LENGTH_SHORT);
if (mGravity != -1)
mTemp.setGravity(mGravity, xOffset, yOffset);
if (v != null){
mTemp.setView(v);
}
mTemp.show();
reset();
}
public void showLong(String message){
showLong(message, null);
}
/**
* 需要在toast中显示的v
*/
public void showLong(String message, View v){
//防止一堆toast的显示堆积
if (mTemp != null)
mTemp.cancel();
mTemp = Toast.makeText(RootApplication.getInstance(), message, Toast.LENGTH_LONG);
if (mGravity != -1)
mTemp.setGravity(mGravity, xOffset, yOffset);
if (v != null){
mTemp.setView(v);
}
mTemp.show();
reset();
}
private void reset(){
mGravity = -1;
xOffset = 0;
yOffset = 0;
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/activity/ActivityManager.java
================================================
package com.android.libcore.activity;
import android.app.Activity;
import android.content.Context;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import java.util.Iterator;
import java.util.Stack;
/**
* Description: 该类用栈来管理所有该应用的activity,进栈退栈等
*
* {@linkplain #finishActivity()} 关闭栈中第一个activity
* {@linkplain #finishAllActivity(Class)} 关闭栈中所有该类名的activity
* {@linkplain #finishAfterActivity(Class)} 关闭栈中该类名的activity之上的activity
* {@linkplain #finishLastActivity(Class)} 关闭栈中第一个与该类名匹配的activity
* {@linkplain #finishAllActivityAndClose()} 关闭应用退出
*
*
* 注意所有的函数都要进行判空的操作,因为可能因为内存不足导致activity被回收而导致空指针的错误
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-07
*/
public final class ActivityManager {
private static volatile ActivityManager instance = null;
private Stack mStack = null;
private ActivityManager(){
mStack = new Stack<>();
}
public static ActivityManager getInstance(){
if (instance == null){
synchronized (ActivityManager.class){
if (instance == null)
instance = new ActivityManager();
}
}
return instance;
}
/**
* 获取栈的信息
*/
public String getStackInfo() {
StringBuilder sb = new StringBuilder();
for (Activity temp : mStack){
if (temp != null)
sb.append(temp.toString()).append("\n");
}
return sb.toString();
}
/**
* 将activity加入到栈中
* @param activity 需要加入到栈中的activity
*/
public void addActivity(Activity activity){
mStack.push(activity);
}
/**
* 删除栈中activity
*/
public void removeActivity(Activity activity){
mStack.remove(activity);
}
/**
* @return 栈顶的activity
*/
public Activity getActivity(){
if (!mStack.isEmpty())
return mStack.peek();
L.i("Activity 栈为空!!!");
return null;
}
/**
* 关闭并删除掉最上面一个的activity
*/
public void finishActivity(){
if (!mStack.isEmpty()) {
Activity temp = mStack.pop();
if (temp != null)
temp.finish();
return;
}
L.e("Activity 栈为空!!!");
}
/***
* 关闭并删除指定 activity
*/
public void finishActivity(Activity activity){
if (mStack.isEmpty()) {
L.e("Activity 栈为空!!!");
return ;
}
try {
mStack.remove(activity);
}catch (Exception e){
L.e("删除错误", e, ActivityManager.class);
}finally {
if (activity != null)
activity.finish();
}
}
/**
* 删除并关闭栈中该class对应的所有的该activity
*/
public void finishAllActivity(Class> clazz){
Iterator iterator = mStack.iterator();
while (iterator.hasNext()){
Activity activity = iterator.next();
if (activity!=null && activity.getClass().equals(clazz)) {
//注意应该通过iterator操作stack,要不然回报ConcurrentModificationException
iterator.remove();
activity.finish();
}
}
}
/**
* 删除并关闭栈中该class对应的第一个该activity,从栈顶开始
*/
public void finishLastActivity(Class> clazz){
Activity activity = null;
Iterator iterator = mStack.iterator();
while (iterator.hasNext()){
Activity temp = iterator.next();
if (temp!=null && temp.getClass().equals(clazz))
activity = temp;
}
if (activity != null)
finishActivity(activity);
}
/**
* 删除栈上该activity之上的所有activity
*/
public void finishAfterActivity(Activity activity){
if (activity!=null && mStack.search(activity) == -1){
L.e("在栈中找不到该activity", ActivityManager.class);
return;
}
while (mStack.peek() != null){
Activity temp = mStack.pop();
if (temp!=null && temp.equals(activity)){
mStack.push(temp);
break;
}
if (temp!=null)
temp.finish();
}
}
/**
* 删除栈上该class之上的所有activity
*/
public void finishAfterActivity(Class> clazz){
boolean flag = true;
Activity activity = null;
Iterator iterator = mStack.iterator();
while (iterator.hasNext()){
activity = iterator.next();
if (activity!=null && activity.getClass().equals(clazz)) {
flag = false;
break;
}
}
if (flag) {
L.e("在栈中找不到该class", ActivityManager.class);
return;
}
finishAfterActivity(activity);
}
/**
* 弹出关闭所有activity并关闭应用所有进程
*/
public void finishAllActivityAndClose(){
while (mStack.size() > 0){
Activity temp = mStack.pop();
if (temp != null)
temp.finish();
}
//调用finish()之后不会立马调用onDestroy()
RootApplication.checkApplicationDestroy();
try {
android.app.ActivityManager activityManager = (android.app.ActivityManager)
RootApplication.getInstance().getSystemService(Context.ACTIVITY_SERVICE);
activityManager.killBackgroundProcesses(RootApplication.getInstance().getPackageName());
}catch (SecurityException e){
L.e("请添加permission", e);
}
System.exit(0);
}
/**
* 弹出关闭所有activity并保留应用后台进程
*/
public void finishAllActivityWithoutClose(){
while (mStack.size() > 0){
Activity temp = mStack.pop();
if (temp != null)
temp.finish();
}
//调用finish()之后不会立马调用onDestroy()
RootApplication.checkApplicationDestroy();
System.exit(0);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/activity/RootActivity.java
================================================
package com.android.libcore.activity;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import com.android.libcore.application.RootApplication;
/**
* Description: 所有基础{@linkplain Activity}的基类,所有的Activity应该
* 继承自该基类,以便进行context的管理、页面的管理等
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-07
*/
public abstract class RootActivity extends AppCompatActivity{
/** 用来在页面之间进行广播的传递 */
private BroadcastReceiver mReceiver = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RootApplication.setInstanceRef(this);
ActivityManager.getInstance().addActivity(this);
}
@Override
protected void onResume() {
super.onResume();
//也要在onresume函数里面进行设置,保证弱引用一直引用当前的可见页面
RootApplication.setInstanceRef(this);
}
/**
* 用来在注册广播之后进行广播的接收处理
*/
protected void onReceive(Context context, Intent intent){}
/**
* 用来注册广播
* @param action 需要注册广播的action
*/
public void registerReceiver(String action){
if (mReceiver == null) {
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
RootActivity.this.onReceive(context, intent);
}
};
}
IntentFilter filter = new IntentFilter(action);
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
}
/**
* 发送应用内部广播
*/
protected final void sendLocalBroadcast(String action){
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(action));
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReceiver != null)
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
ActivityManager.getInstance().removeActivity(this);
//每次在activity销毁的时候调用该函数来检测应用是否被销毁
RootApplication.checkApplicationDestroy();
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/activity/RootFragment.java
================================================
package com.android.libcore.activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.View;
/**
* Description: 所有基础{@linkplain Fragment}的基类
* 从今天开始抛弃Fragment吧
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-08
*/
public abstract class RootFragment extends Fragment{
protected BroadcastReceiver mReceiver;
protected View mViewContainer;
private Boolean mIsNeedUnRegister = false;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
this.onReceive(context, intent);
}
};
}
protected void onReceive(Context context, Intent intent){}
protected void registerReceiver(String action){
IntentFilter filter = new IntentFilter(action);
getActivity().registerReceiver(mReceiver, filter);
mIsNeedUnRegister = true;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mIsNeedUnRegister)
getActivity().unregisterReceiver(mReceiver);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/application/RootApplication.java
================================================
package com.android.libcore.application;
import android.app.AlarmManager;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.android.libcore.cachemanager.CacheManager;
import com.android.libcore.activity.ActivityManager;
import com.android.libcore.utils.FileUtils;
import java.lang.ref.WeakReference;
import java.util.HashMap;
/**
* Description: {@linkplain Application}基类,不要忘记在manifest文件中设置application的
* android:name,应用的application应该继承自该基类
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-07
*/
public class RootApplication extends Application{
/** 是否是调试模式,统一使用该全局的debug变量 */
public static final boolean DEBUG = true;
/** 用来保存当前该Application的context */
private static Context instance;
/** 用来保存最新打开页面的context */
private volatile static WeakReference instanceRef = null;
/** 用来存放一些在软件启动生命周期之内需要存放的变量和数据,但存放的数据量不宜过大,
* 如果需要存放过大的数据,请在使用完之后,立马清除,还有一点需要注意的是该maps可
* 能会因为应用在后台,手机内存不足而被回收,回收之后该maps会被清空!!*/
public static HashMap appMaps;
@Override
public void onCreate() {
super.onCreate();
instance = this;
appMaps = new HashMap<>();
//设置默认崩溃处理,如需使用,不注释即可
// Thread.setDefaultUncaughtExceptionHandler(new MyExceptionHandler());
}
/**
* 该函数用来返回一个context,一般情况下为当前activity的context,如果为空,
* 就会调用{@linkplain ActivityManager#getActivity()}方法去获取栈顶context,
* 但是如果activity没有调用 {@link #setInstanceRef(Context)}方法去设置context,
* 就会使用整个Application的context,相当于{@link #getApplicationContext()},
* 不推荐使用该方法,特别是耗时任务,因为会导致页面销毁时,任务无法回收,导致内存泄露和
* 其他异常
*
* @return context上下文,如果返回Null检测manifest文件是否设置了application的name
*/
public static Context getInstance(){
if (instanceRef == null || instanceRef.get() == null){
synchronized (RootApplication.class) {
if (instanceRef == null || instanceRef.get() == null) {
Context context = ActivityManager.getInstance().getActivity();
if (context != null)
instanceRef = new WeakReference<>(context);
else {
instanceRef = new WeakReference<>(instance);
}
}
}
}
return instanceRef.get();
}
/**
* 将{@link #instanceRef}设置为最新页面的context
* @param context 最新页面的context
*/
public static void setInstanceRef(Context context){
instanceRef = new WeakReference<>(context);
}
/**
* 检测应用是否退出,并且在应用退出的时候做相关的处理
*/
public static void checkApplicationDestroy(){
//应用被关闭,删除需要删除的相关目录和文件
if (ActivityManager.getInstance().getActivity() == null){
CacheManager.removeTemporary();
FileUtils.clearExternalStorageTemp();
}
}
/**
* 处理崩溃异常,并且在崩溃异常之后重启
* Android对待所有传递给Context.startActivity()的 隐式intent好像它们至少包含
* "android.intent.category.DEFAULT"(对应CATEGORY_DEFAULT常量)。
* 因此,活动想要接收隐式intent必须要在intent过滤器中包含"android.intent.category.DEFAULT"
*/
private class MyExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
//启动首页
Intent intent = new Intent();
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.LAUNCHER");
intent.addCategory("com.android.framework.MAINPAGE");
PendingIntent restartIntent = PendingIntent.getActivity(getInstance(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//退出程序
AlarmManager mgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent);
ActivityManager.getInstance().finishAllActivityWithoutClose();
}
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/cachemanager/CacheManager.java
================================================
package com.android.libcore.cachemanager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Description: 使用SharedPreferences来保存对象,SharedPreference可以使用两种存储方式存储:
*
*
* 一种是存储进临时SharedPreference中,{@link #setTemporary(String, Object)},读取使用
* {@link #getTemporary(String, Class, Object)} 来读取固定类型或者{@link #getTemporarySet(String, Class)}
* 函数来读取特定类型的集合,这种类型的存储会在应用退出之后全部删除
*
*
* 一种是存储进永久的SharedPreference中,{@link #setPermanent(String, Object)},读取使用
* {@link #getPermanent(String, Class, Object)}来读取固定类型或者{@link #getPermanentSet(String, Class)}
* 方法来读取特定类型集合,存储之后永久保存
*
*
*
* 记住如果要保存一个不是基本类型的对象,那么该对象的类一定要继承自{@link ParseObject}虚类,
* 并且该类要有空参构造函数,并且如果该实体类作为一个内部类,则需要定义为static,因为非静态内部类实例化
* 一定要靠外部类的对象,会导致{@link InstantiationException}
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-23
*/
public class CacheManager {
/** 临时SharedPreference,当退出应用之后会主动删除 */
public static String TEMPORARY = "temporary";
/** 永久SharedPreference,退出应用不会主动删除 */
public static String PERMANENT = "permanent";
private static void setValue(SharedPreferences sp, String key, Object value){
SharedPreferences.Editor editor = sp.edit();
if (value instanceof Boolean){
editor.putBoolean(key, (Boolean) value);
}else if (value instanceof Float){
editor.putFloat(key, (Float) value);
}else if (value instanceof Integer){
editor.putInt(key, (Integer) value);
}else if (value instanceof Long){
editor.putLong(key, (Long) value);
}else if (value instanceof String){
editor.putString(key, (String) value);
}else if (value instanceof Set>){
if ((((Set>)value).toArray())[0] instanceof String){
if (Build.VERSION.SDK_INT < 11){
L.e(CacheManager.class.getSimpleName() + " 版本不支持String set");
return;
}
editor.putStringSet(key, (Set) value);
}else{
//如果以不是string集合的方式存储进SharedPreference则将其以"|"分割线的模式分割成
//一个String子串存储,取时注意要以"||"分割成String集合进行对象的重组
String putValue = "";
Object[] objects = ((Set>)value).toArray();
for (Object object : objects){
putValue += object.toString()+"||";
}
editor.putString(key, putValue);
}
}else{
editor.putString(key, value.toString());
}
editor.commit();
}
private static T getValue(SharedPreferences sp, String key, Class clazz, T defaultValue){
Object returnValue = null;
if (clazz == Boolean.class){
returnValue = sp.getBoolean(key, (Boolean) defaultValue);
}else if (clazz == Float.class){
returnValue = sp.getFloat(key, (Float) defaultValue);
}else if (clazz == Integer.class){
returnValue = sp.getInt(key, (Integer) defaultValue);
}else if (clazz == Long.class){
returnValue = sp.getLong(key, (Long) defaultValue);
}else if (clazz == String.class){
returnValue = sp.getString(key, (String) defaultValue);
}else{
if (ParseObject.class.isAssignableFrom(clazz)){
String value = sp.getString(key, null);
if (value != null){
try {
T temp = clazz.newInstance();
((ParseObject)temp).stringParseObject(value);
return temp;
} catch (Exception e){
return defaultValue;
}
}
}
return defaultValue;
}
if (returnValue == null)
return defaultValue;
return (T) returnValue;
}
private static Set getValueSet(SharedPreferences sp, String key, Class clazz){
if (clazz == String.class){
if (Build.VERSION.SDK_INT < 11){
L.e(CacheManager.class.getSimpleName() + " 版本不支持String set");
return null;
}
return (Set) sp.getStringSet(key, null);
}else{
if (ParseObject.class.isAssignableFrom(clazz)){
String value = sp.getString(key, null);
if (value != null) {
//要用转义符
String[] values = value.split("\\|\\|");
LinkedHashSet lists = new LinkedHashSet<>();
for (String string : values){
try {
T temp = null;
temp = clazz.newInstance();
((ParseObject)temp).stringParseObject(string);
lists.add(temp);
} catch (Exception e){
e.printStackTrace();
}
}
return lists;
}
}
return null;
}
}
/**
* 设置临时变量
*/
public static void setTemporary(String key, Object value){
SharedPreferences sp = RootApplication.getInstance().getSharedPreferences(TEMPORARY, Context.MODE_PRIVATE);
setValue(sp, key, value);
}
/**
* 获取临时变量
* @param key 键
* @param clazz 获取临时变量的类型
* @param defaultValue 当取出失败时返回的默认值
*/
public static T getTemporary(String key, Class clazz, T defaultValue){
SharedPreferences sp = RootApplication.getInstance().getSharedPreferences(TEMPORARY, Context.MODE_PRIVATE);
return getValue(sp, key, clazz, defaultValue);
}
/**
* 返回指定对象的数据集合
*/
public static Set getTemporarySet(String key, Class clazz){
SharedPreferences sp = RootApplication.getInstance().getSharedPreferences(TEMPORARY, Context.MODE_PRIVATE);
return getValueSet(sp, key, clazz);
}
public static void setPermanent(String key, Object value){
SharedPreferences sp = RootApplication.getInstance().getSharedPreferences(PERMANENT, Context.MODE_PRIVATE);
setValue(sp, key, value);
}
public static T getPermanent(String key, Class clazz, T defaultValue){
SharedPreferences sp = RootApplication.getInstance().getSharedPreferences(PERMANENT, Context.MODE_PRIVATE);
return getValue(sp, key, clazz, defaultValue);
}
public static Set getPermanentSet(String key, Class clazz){
SharedPreferences sp = RootApplication.getInstance().getSharedPreferences(PERMANENT, Context.MODE_PRIVATE);
return getValueSet(sp, key, clazz);
}
/** 清空临时SharedPreference */
public static void removeTemporary(){
L.i("application close remove temporary");
SharedPreferences sp = RootApplication.getInstance().getSharedPreferences(TEMPORARY, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.clear();
editor.commit();
}
/**
* 如果需要存储一个非String集合,那么集合里面的对象类必须继承该虚类,完成String到Object的转换
*/
public static abstract class ParseObject{
public abstract void stringParseObject(String value);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/database/BaseDB.java
================================================
package com.android.libcore.database;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Description: 基本数据库类,所有的数据库类都应该继承自该类
* 应该注意的地方是:如果新版本修改了数据库,请在增加version版本号,并且在
* {@link #onDBUpgrade(SQLiteDatabase, int, int)}函数里增加相应的版本升级工作
*
* 每次数据库的操作请加上事务,开始操作时请调用{@link #beginTransaction()},如果操作成功调用
* {@link #setTransactionSuccessful()},操作完成调用{@link #endTransaction()},还有一定
* 要记住最后调用{@link #close()}方法关闭数据库,使用try-catch-finally结构操作
*
* 注意:表的创建请在名字后面加上版本,如版本为1的cache表名为cache_1
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-20
*/
public abstract class BaseDB {
/** 数据库锁 */
private final Byte[] lock = new Byte[0];
/** 打开数据库超时时间 */
private final int TIME_OUT = 30*1000;
/** 当前操作的表 */
private String mTable;
private DataBaseHelper mHelper;
protected SQLiteDatabase mDb;
public BaseDB(IBaseDBTable table, boolean writable){
this.mTable = table.getTableName()+"_"+getDBVersion();
mHelper = new DataBaseHelper();
if (writable)
mDb = mHelper.getWritableDatabase();
else
mDb = mHelper.getReadableDatabase();
}
/**
* 获取数据库的名字
*/
protected abstract String getDBName();
/**
* 获取数据库的版本,注意该版本只增不减
*/
protected abstract int getDBVersion();
/**
* 数据库的创建
*/
protected abstract void onDBCreate(SQLiteDatabase db);
/**
* 如果应用程序版本号大于本地版本库,会产生升级动作,该函数只需要处理数据库表的变更即可,不需要进行名字的更改,
* 注意该函数使用的是逐步升级版本的操作,比如本地数据库版本为1,升级到3版本,会迭代先升级到2版本,再从2版本升
* 级到3版本,升级时的版本号仍然为旧版本号 ,所以该函数要详细写清楚每个版本之间的差异,
* 并且处理每个版本之间的升级任务,该升级操作会在数据库的第一个操作检测到数据库版本不一致时进行升级
*/
protected abstract void onDBUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
/**
* 开始事务
*/
public void beginTransaction(){
if (mDb != null && !mDb.inTransaction()){
try {
mDb.beginTransaction();
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 事务成功
*/
public void setTransactionSuccessful(){
if (mDb != null && mDb.inTransaction()){
try {
mDb.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 结束事务
*/
public void endTransaction(){
if (mDb != null && mDb.inTransaction()){
try {
mDb.endTransaction();
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
* 关闭数据库
*/
public void close(){
if (mDb != null && mDb.isOpen())
mDb.close();
}
/**
* 获取表中所有的条目
*/
public int getCount(){
return getCount(null, null);
}
/**
* 获取表中符合该条件的表的条目数
*/
public int getCount(String selection, String[] selectionArgs){
int count = 0;
Cursor cursor = null;
try {
cursor = mDb.query(mTable, new String[]{"count(*)"}, selection, selectionArgs, null, null, null);
if (cursor.moveToNext()){
count = cursor.getInt(0);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (cursor != null){
try {
cursor.close();
}catch (Exception e1){
e1.printStackTrace();
}
}
}
return count;
}
/**
* 增
*/
public long insert(HashMap map, boolean replace){
getCount();
long count = 0;
synchronized (lock){
try {
if (!replace)
count = mDb.insert(mTable, null, parseHashMapToContentValues(map));
else
count = mDb.replace(mTable, null, parseHashMapToContentValues(map));
}catch (Exception e){
e.printStackTrace();
count = -1;
}
}
return count;
}
/**
* 删
*/
public long delete(String selection, String[] selectionArgs){
long count = 0;
synchronized (lock){
try {
count = mDb.delete(mTable, selection, selectionArgs);
}catch (Exception e){
e.printStackTrace();
count = -1;
}
}
return count;
}
/**
* 改
* 使用范例:如果需要将带"shangh"开头的城市的人数字段都变成0,函数就可以这么调用:
* update(hashmap({"people", "0"}), "city like ?", "new String[]{"shangh"}")
*/
public long update(HashMap maps, String whereClause, String[] whereArgs){
long count = 0;
synchronized (lock) {
try {
count = mDb.update(mTable, parseHashMapToContentValues(maps), whereClause, whereArgs);
}catch (Exception e){
e.printStackTrace();
count = -1;
}
}
return count;
}
/**
* 查
*/
public ArrayList> query(){
return query(null, null);
}
/**
* 查
*/
public ArrayList> query(String selection, String[] selectionArgs){
return query(selection, selectionArgs, null, null, null, null);
}
/**
* 查
* 使用范例:query("city like ? or city=?", new String[]{"shangh", "beijing"}, "district", "sum(people)>10", "GDP", "1000"),
* 作用为查找城市中像"shangh"和等于beijing的城市,并且按照district排序,统计所有区总人数大于10万,并且区之间按照GDP排序,显示前1000条信息,该函数的
* 使用与正常的sql语句一样
*/
public ArrayList> query(String selection, String[] selectionArgs,
String groupBy, String having, String orderBy, String limit){
ArrayList> result = new ArrayList<>();
synchronized (lock){
try {
Cursor cursor = mDb.query(mTable, null, selection, selectionArgs, groupBy, having, orderBy, limit);
int length = cursor.getColumnCount();
while (cursor.moveToNext()){
HashMap value = new HashMap<>();
for (int i=0; i map){
ContentValues values = new ContentValues();
for (Map.Entry entry : map.entrySet()) {
values.put(entry.getKey(), entry.getValue());
}
return values;
}
/**
* 数据库封装类
*/
private class DataBaseHelper extends SQLiteOpenHelper{
public DataBaseHelper() {
super(RootApplication.getInstance(), getDBName(), null, getDBVersion());
}
@Override
public SQLiteDatabase getReadableDatabase() {
SQLiteDatabase db = null;
synchronized (lock){
boolean retry = false;
long time = System.currentTimeMillis();
do {
retry = false;
try {
db = super.getReadableDatabase();
}catch (Exception e){
L.e(e);
retry = true;
//休眠一个随机时间防止线程并发
try {
Thread.sleep((long) (3*1000+Math.random()*1000));
}catch (InterruptedException e1){
L.e(e1);
}
}
}while (retry && ((System.currentTimeMillis()-time) getTables(SQLiteDatabase db){
ArrayList tables = new ArrayList<>();
Cursor cursor = db.rawQuery("select * from sqlite_master where type = 'table'", null);
while (cursor.moveToNext()){
String[] cols = cursor.getColumnNames();
for (int i = 0; i < cols.length; i++) {
if (cols[i].equals("tbl_name")){
String name = cursor.getString(i);
//排除sqlite_master表
if (name.length() < 7 || (!name.substring(0, 7).equals("sqlite_"))) {
tables.add(name);
}
}
}
}
return tables;
}
/**
* 删除所有异常新版本库,保证当前数据的表都为旧版本
* @param version 需要删除的版本
*/
private void removeNewTables(SQLiteDatabase db, int version){
ArrayList tables = getTables(db);
String suffix = "_"+version;
for (String table : tables){
if (table.substring(table.length()-suffix.length()).equals(suffix)){
db.rawQuery("drop table if exists " + table, null);
}
}
}
/**
* 将旧版本的表名后面的版本号修改成新版本号
*/
private void renameOldTables(SQLiteDatabase db, int oldVersion, int newVersion){
String oldSuffix = "_"+oldVersion;
String newSuffix = "_"+newVersion;
ArrayList tables = getTables(db);
for (String table : tables){
if (table.substring(table.length()-oldSuffix.length()).equals(oldSuffix)){
db.execSQL("alter table " + table + " rename to " + table.replace(oldSuffix, newSuffix));
}
}
}
/**
* 清除所有的旧表
*/
private void clearOldTables(SQLiteDatabase db, int newVersion){
ArrayList tables = getTables(db);
String suffix = "_"+newVersion;
for (String table : tables){
//如果数据库中存在不是新版本的表,删除
if (!table.substring(table.length()-suffix.length()).equals(suffix)){
db.rawQuery("drop table if exists " + table, null);
}
}
}
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/database/BaseDBHelper.java
================================================
package com.android.libcore.database;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Description: 所有数据库操作的操作封装类,在此可以封装单次的插入操作,也可以封装大量数据的一次插入,
* 其他类只能通过helper的子类进行数据库的操作,不能直接操作数据库,每一个数据库对应一个数据库helper,
* 这样就能够保证数据库访问的统一性,以便以后的数据库修改,继承自该helper类之后也可增加方法去操作
* {@link #mDb}
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-21
*/
public abstract class BaseDBHelper {
protected BaseDB mDb;
protected IBaseDBTable mTable;
/** 初始化插入数据库 */
protected abstract void initInsertDB();
/** 初始化删除数据库 */
protected abstract void initDeleteDB();
/** 初始化更改数据库 */
protected abstract void initUpdateDB();
/** 初始化查询数据库 */
protected abstract void initQueryDB();
/**
* 单次插入
* @param map 单次插入的一行数据
*/
protected long insert(HashMap map, boolean replace){
initInsertDB();
if (mDb == null)
return 0;
long count = -1;
try {
mDb.beginTransaction();
count = mDb.insert(map, replace);
mDb.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
mDb.endTransaction();
mDb.close();
}
return count;
}
/**
* 大量数据的插入
* @param maps 需要插入的数据集
* @return 成功插入的数量
*/
protected long insertAll(ArrayList> maps, boolean replace){
initInsertDB();
if (mDb == null)
return 0;
long count = 0;
try {
mDb.beginTransaction();
for (HashMap map : maps) {
count ++;
mDb.insert(map, replace);
}
mDb.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
mDb.endTransaction();
mDb.close();
}
return count;
}
/**
* 删除
*/
protected long delete(String selection, String[] selectionArgs){
initDeleteDB();
if (mDb == null)
return 0;
long count = -1;
try {
mDb.beginTransaction();
count = mDb.delete(selection, selectionArgs);
mDb.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
mDb.endTransaction();
mDb.close();
}
return count;
}
/**
* 修改
*/
protected long update(HashMap maps, String whereClause, String[] whereArgs){
initUpdateDB();
if (mDb == null)
return 0;
long count = -1;
try {
mDb.beginTransaction();
count = mDb.update(maps, whereClause, whereArgs);
mDb.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
mDb.endTransaction();
mDb.close();
}
return count;
}
protected ArrayList> query(String selection, String[] selectionArgs,
String groupBy, String having, String orderBy, String limit){
initQueryDB();
if (mDb == null)
return null;
ArrayList> result = null;
try {
mDb.beginTransaction();
result = mDb.query(selection, selectionArgs, groupBy, having, orderBy, limit);
mDb.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
mDb.endTransaction();
mDb.close();
}
return result;
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/database/IBaseDBTable.java
================================================
package com.android.libcore.database;
import java.util.ArrayList;
/**
* Description: 所有数据库DB类应该声明一个枚举类,用来封装该数据库中的所有表名,
* 和表的相关列名,这样将表名和列名封装起来,外部使用封装之后的枚举进行操作
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-22
*/
public interface IBaseDBTable {
/**
* 用来返回表名
*/
String getTableName();
/**
* 用来返回该表中所有的列名
*/
ArrayList getTableColumns();
}
================================================
FILE: libcore/src/main/java/com/android/libcore/dialog/BaseDialog.java
================================================
package com.android.libcore.dialog;
import android.app.Dialog;
import android.content.Context;
import android.view.View;
import java.util.ArrayList;
/**
* Description: 最基础的dialog类,用来统一dialog的功能,该dialog可以添加多个按钮,按钮的顺序
* 和添加按钮顺序一致,所以请按照需要显示的顺序要添加按钮
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-16
*/
public abstract class BaseDialog extends Dialog{
/** 用来标识确定按钮的回调id */
public static final int POSITIVE_LISTENER = 0;
/** 用来标识否定按钮的回调id */
public static final int NEGATIVE_LISTENER = 1;
/** 用来标识中间按钮的回调id */
public static final int NEUTRAL_LISTENER = 2;
/** 用来标示是否有title */
protected boolean mHasTitle = false;
/** 其他按钮的id */
protected ArrayList mIds = new ArrayList<>();
protected ButtonClickListener mListener;
public BaseDialog(Context context, int theme) {
super(context, theme);
mIds.add(POSITIVE_LISTENER);
mIds.add(NEGATIVE_LISTENER);
mIds.add(NEUTRAL_LISTENER);
}
/**
* 检测按钮id是否合法
* @return 合法返回true
*/
protected boolean checkIllegalId(int id){
return !mIds.contains(id);
}
/**
* 设置dialog的title
* @param title 标题的文字
*/
public abstract BaseDialog setTitle(String title);
/**
* 设置dialog的title view
* @param title view
*/
public abstract BaseDialog setTitle(View title);
/**
* 设置内容区域的文字
* @param message 内容的文字
*/
public abstract BaseDialog setMessage(String message);
/**
* 内容区域的View
* @param message view
*/
public abstract BaseDialog setMessage(View message);
/**
* 设置确定按钮的文字
* @param positive 文字
*/
public abstract BaseDialog setPositiveButton(String positive);
/**
* 设置确定按钮的view
* @param positive view
*/
public abstract BaseDialog setPositiveButton(View positive);
/**
* 设置取消按钮的文字
* @param negative 文字
*/
public abstract BaseDialog setNegativeButton(String negative);
/**
* 设置取消按钮的view
* @param negative view
*/
public abstract BaseDialog setNegativeButton(View negative);
/**
* 设置中性按钮的文字
* @param neutral 文字
*/
public abstract BaseDialog setNeutralButton(String neutral);
/**
* 设置中性按钮的view
* @param neutral view
* @return
*/
public abstract BaseDialog setNeutralButton(View neutral);
/**
* 增加一个按钮
* @param other 按钮的文字
* @param other_listener 按钮的点击回调id
*/
public abstract BaseDialog addOtherButton(String other, int other_listener);
/**
* 增加一个按钮
* @param other 按钮的view
* @param other_listener 按钮的点击回调id
*/
public abstract BaseDialog addOtherButton(View other, int other_listener);
/**
* 设置dialog的按钮点击回调
*/
public BaseDialog setOnButtonClickListener(ButtonClickListener listener){
this.mListener = listener;
return this;
}
/**
* 设置dialog显示的位置
* @param gravity Gravity类的变量
*/
public abstract BaseDialog setGravity(int gravity);
/**
* 设置dialog显示的具体位置,相对于原位置而言,比如:x为负数代表向左,正数代表向右
* @param x x坐标,0代表使用原位置
* @param y y坐标,0代表使用原位置
*/
public abstract BaseDialog setPosition(int x, int y);
/**
* 设置宽度,不使用默认
* @param width 宽度
*/
public abstract BaseDialog setWidth(int width);
/**
* 设置高度,不使用默认
* @param height 高度
*/
public abstract BaseDialog setHeight(int height);
/**
* 设置透明度
* @param alpha 0~1
*/
public abstract BaseDialog setAlpha(float alpha);
public interface ButtonClickListener{
void onButtonClick(int button_id);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/download/DownloadDB.java
================================================
package com.android.libcore.download;
import android.database.sqlite.SQLiteDatabase;
import com.android.libcore.database.BaseDB;
import com.android.libcore.database.IBaseDBTable;
import com.android.libcore.log.L;
import java.util.ArrayList;
/**
* Description: 保存下载信息的数据库
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-08-05
*/
public class DownloadDB extends BaseDB{
public DownloadDB(IBaseDBTable table, boolean writable) {
super(table, writable);
}
@Override
protected String getDBName() {
return "download.db";
}
@Override
protected int getDBVersion() {
return 1;
}
@Override
protected void onDBCreate(SQLiteDatabase db) {
try {
db.beginTransaction();
String sql;
sql = "create table if not exists "+TABLES.DOWNLOAD.getTableName()+"_" + getDBVersion() + " (";
sql += TABLES.DOWNLOAD.getTableColumns().get(0)+" integer not null, ";
sql += TABLES.DOWNLOAD.getTableColumns().get(1)+" varchar(4000) not null default '', ";
sql += TABLES.DOWNLOAD.getTableColumns().get(2)+" varchar(50) not null default '0', ";
sql += TABLES.DOWNLOAD.getTableColumns().get(3)+" varchar(50) not null default '0', ";
sql += TABLES.DOWNLOAD.getTableColumns().get(4)+" varchar(50) not null default '0'";
sql += ")";
db.execSQL(sql);
db.setTransactionSuccessful();
} catch (Exception e) {
L.e(getClass().getSimpleName()+" sql语句错误", e);
} finally {
db.endTransaction();
}
}
@Override
protected void onDBUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
/**
* 将该数据库中所有的表使用枚举封装
*/
public enum TABLES implements IBaseDBTable {
DOWNLOAD("download"){
@Override
public ArrayList getTableColumns() {
ArrayList columns = new ArrayList<>();
columns.add("id");//id
columns.add("url");//下载url
columns.add("start_pos");//该线程开始位置
columns.add("end_pos");//该线程结束位置
columns.add("complete_size");//线程当前完成的数
return columns;
}
};
private String table_name;
TABLES(String table_name){
this.table_name = table_name;
}
@Override
public String getTableName() {
return table_name;
}
@Override
public ArrayList getTableColumns() {
return null;
}
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/download/DownloadDBHelper.java
================================================
package com.android.libcore.download;
import com.android.libcore.database.BaseDBHelper;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Description: 下载数据库帮助类
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-08-05
*/
public class DownloadDBHelper extends BaseDBHelper{
/**
* 一次性插入大量数据
*/
public void insertInfos(String url, ArrayList infos){
ArrayList columns = DownloadDB.TABLES.DOWNLOAD.getTableColumns();
ArrayList> lists = new ArrayList<>();
for (FileDownloadManager.DownloadInfo info : infos){
HashMap map = new HashMap<>();
map.put(columns.get(0), info.id+"");
map.put(columns.get(1), url);
map.put(columns.get(2), info.startPos+"");
map.put(columns.get(3), info.endPos+"");
map.put(columns.get(4), info.completeSize+"");
lists.add(map);
}
insertAll(lists, true);
}
/**
* 更新该线程下载的完成度
*/
public void updateInfos(String url, ArrayList infos){
ArrayList columns = DownloadDB.TABLES.DOWNLOAD.getTableColumns();
HashMap maps = new HashMap<>();
initUpdateDB();
if (mDb == null)
return;
try {
mDb.beginTransaction();
for (FileDownloadManager.DownloadInfo info : infos){
maps.clear();
maps.put(columns.get(4), info.completeSize+"");
String whereClause = columns.get(0)+"=? and "+columns.get(1)+"=?";
String[] whereArgs = new String[]{info.id+"", url};
mDb.update(maps, whereClause, whereArgs);
}
mDb.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
mDb.endTransaction();
mDb.close();
}
}
/**
* 获取该文件下载进度信息
*/
public ArrayList> getInfo(String url){
ArrayList columns = DownloadDB.TABLES.DOWNLOAD.getTableColumns();
String selection = columns.get(1)+"=?";
String[] selectionArgs = new String[]{url};
return query(selection, selectionArgs, null, null, null, null);
}
/**
* 删除该url的相关下载信息
*/
public void deleteInfos(String url){
ArrayList columns = DownloadDB.TABLES.DOWNLOAD.getTableColumns();
String selection = columns.get(1)+"=?";
String[] selectionArgs = new String[]{url};
delete(selection, selectionArgs);
}
@Override
protected void initInsertDB() {
mDb = new DownloadDB(DownloadDB.TABLES.DOWNLOAD, true);
}
@Override
protected void initDeleteDB() {
mDb = new DownloadDB(DownloadDB.TABLES.DOWNLOAD, true);
}
@Override
protected void initUpdateDB() {
mDb = new DownloadDB(DownloadDB.TABLES.DOWNLOAD, true);
}
@Override
protected void initQueryDB() {
mDb = new DownloadDB(DownloadDB.TABLES.DOWNLOAD, false);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/download/FileDownloadManager.java
================================================
package com.android.libcore.download;
import android.os.Handler;
import android.os.Message;
import com.android.libcore.Toast.T;
import com.android.libcore.log.L;
import com.android.libcore.utils.CommonUtils;
import com.android.libcore.utils.FileUtils;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
/**
* Description: 单个文件下载,支持断点续传,相同url的被认为为同一个文件
* {@link #start()}开启下载
* {@link #stop()}停止下载
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-08-05
*/
public class FileDownloadManager {
/** 开启线程 */
private static final int STATE_START_THREADS = -1;
/** 更新状态 */
private static final int STATE_UPDATE_PROGRESS = 0;
/** 下载状态,正在获取文件大小 */
public static final int STATE_GETSIZE = 1;
/** 下载状态,开始下载 */
public static final int STATE_STARTING = 2;
/** 下载状态,正在停止 */
public static final int STATE_STOPING = 3;
/** 下载状态,停止成功 */
public static final int STATE_STOPED = 4;
/** 下载状态,下载完成 */
public static final int STATE_FINISH = 5;
/** 下载状态,正在删除 */
public static final int STATE_DELETING = 6;
/** 下载状态,删除成功 */
public static final int STATE_DELETE = 7;
/** 下载状态,网络错误 */
public static final int STATE_NET_ERROR = 8;
/** 下载状态,服务器错误 */
public static final int STATE_SERVER_ERROR = 9;
/** 当前文件的下载状态,默认为停止成功,即为下载完成,且随时可以开始下载 */
private int currentState = STATE_STOPED;
/** 下载一个文件所开启的线程数量 */
private final int THREAD_NUM = 4;
/** 下载一个文件的所有线程信息 */
private ArrayList infos;
/** 开始下载线程 */
private Thread startDownloadThread;
/** 结束下载线程 */
private Thread stopDownloadThread;
/** 删除下载线程 */
private Thread deleteDownloadThread;
/** 下载一个文件的线程队列 */
private ArrayList threads;
/** 更新下载信息,更新界面线程 */
private UpdateThread updateThread;
/** 数据库操作对象 */
private DownloadDBHelper helper;
/** 该文件下载名 */
private String fileName;
/** 该文件下载的url */
private String url;
/** 该文件下载路径,默认为SD卡file目录 */
private String path = FileUtils.getExternalStorageFilePath();
/** 该文件下载的文件大小 */
private long fileSize = 0;
/** 该文件下载的完成度 */
private long completeSize = 0;
/** 通知更新进度handler */
private ProgressChangeHandler progressChangeHandler;
/** 文件下载进度更新 */
private IDownloadProgressChangedListener listener;
/** 文件的下载状态,true表示正在下载 */
private boolean downloadState = false;
/** 文件是否下载完成 */
private boolean isDownloadFinish = false;
/** 下载线程是否结束标识 */
private CountDownLatch countDownLatch;
/**
* @param url 文件下载url
* @param fileName 文件名,默认将会下载到sd卡file目录下
*/
public FileDownloadManager(String url, String fileName){
this(url, fileName, null);
}
/**
* @param url 文件下载url
* @param fileName 文件名
* @param path 文件下载路径,不需要带文件名
*/
public FileDownloadManager(String url, String fileName, String path){
this.url = url;
if (path != null)
this.path = path;
if (!this.path.substring(this.path.length()-1).equals("/")){
this.path += "/";
}
//保存该文件原路径
this.fileName = this.path + fileName;
//将文件名字先进行md5,最后等文件下载完成之后再更改文件名字
String md5 = CommonUtils.md5(fileName);
this.path += md5;
helper = new DownloadDBHelper();
infos = new ArrayList<>();
threads = new ArrayList<>();
progressChangeHandler = new ProgressChangeHandler(this);
checkFileFinish();
}
/**
* 检测该文件是否已经下载完成
*/
private boolean checkFileFinish(){
if (isDownloadFinish || isFileDownloadFinish(helper.getInfo(url))){
isDownloadFinish = true;
progressChangeHandler.sendEmptyMessage(STATE_FINISH);
return true;
}
return false;
}
/**
* 开启下载
*/
public void start(){
if (!CommonUtils.isNetworkAvailable()){
progressChangeHandler.sendEmptyMessage(STATE_NET_ERROR);
T.getInstance().showShort("网络错误");
return;
}
if (checkFileFinish()){
T.getInstance().showShort("文件已下载完成");
return;
}
if (downloadState){
T.getInstance().showShort("已经启动下载");
return;
}
if (currentState == STATE_STOPING){
T.getInstance().showShort("文件还未停止成功");
return;
}
if (currentState == STATE_DELETING){
T.getInstance().showShort("文件正在删除");
return;
}
if (currentState == STATE_DELETE){
T.getInstance().showShort("文件已删除");
return;
}
downloadState = true;
//开启下载任务
startDownload(helper.getInfo(url));
}
/**
* 停止下载
*/
public void stop(){
downloadState = false;
//停止更新界面线程,一定要保证最多只有一个更新线程在执行
if (updateThread != null && updateThread.isAlive())
updateThread.canRun = false;
if (checkFileFinish()){
T.getInstance().showShort("文件已下载完成");
return;
}
if (currentState == STATE_STOPING){
T.getInstance().showShort("正在停止,稍后...");
return;
}
if (currentState == STATE_STOPED){
T.getInstance().showShort("已停止");
return;
}
if (currentState == STATE_DELETING){
T.getInstance().showShort("文件正在删除");
return;
}
if (currentState == STATE_DELETE){
T.getInstance().showShort("文件已删除");
return;
}
stopDownload();
}
/**
* 删除该文件下载的相关所有信息,成功之后,该对象将自动置为null
*/
public void delete(){
//停止下载线程
downloadState = false;
//停止更新界面线程,一定要保证最多只有一个更新线程在执行
if (updateThread != null && updateThread.isAlive())
updateThread.canRun = false;
if (currentState == STATE_DELETING){
T.getInstance().showShort("正在删除,稍后...");
return;
}
if (currentState == STATE_DELETE){
T.getInstance().showShort("已删除");
return;
}
if (currentState == STATE_STOPING){
T.getInstance().showShort("正在停止,稍后...");
return;
}
deleteDownload();
}
private void startDownload(final ArrayList> maps){
startDownloadThread = new Thread(new Runnable() {
@Override
public void run() {
if (stopDownloadThread!=null && stopDownloadThread.isAlive()){
L.e("正在停止,稍后...");
return;
}
if (deleteDownloadThread!=null && deleteDownloadThread.isAlive()){
L.e("文件正在删除");
return;
}
//如果没有下载信息,则需要创建
if (infos==null || infos.size()==0) {
if (maps == null || maps.size() == 0) {
createDownloadInfos();
} else {
revertDownloadInfos(maps);
}
}
//更新文件状态为正在下载
progressChangeHandler.sendEmptyMessage(STATE_STARTING);
//上次的线程完成之后才能开启新的下载线程开始下载
threads.clear();
for (DownloadInfo info : infos){
DownloadThread thread = new DownloadThread(info);
threads.add(thread);
}
L.i("准备开启下载线程");
//初始化多线程标识
countDownLatch = new CountDownLatch(THREAD_NUM);
progressChangeHandler.sendEmptyMessage(STATE_START_THREADS);
}
});
startDownloadThread.start();
}
private void stopDownload(){
stopDownloadThread = new Thread(new Runnable() {
@Override
public void run() {
if (deleteDownloadThread!=null && deleteDownloadThread.isAlive()){
L.e("文件正在删除");
return;
}
stopStartThread(STATE_STOPING);
stopDownloadThread(STATE_STOPING);
//确保开始线程和下载线程都已经执行完成之后才能将状态修改为停止成功
progressChangeHandler.sendEmptyMessage(STATE_STOPED);
}
});
stopDownloadThread.start();
}
private void deleteDownload(){
deleteDownloadThread = new Thread(new Runnable() {
@Override
public void run() {
if (stopDownloadThread!=null && stopDownloadThread.isAlive()){
L.e("正在停止,稍后...");
return;
}
stopStartThread(STATE_DELETING);
stopDownloadThread(STATE_DELETING);
//确保开始线程,停止线程和下载线程都已经执行完成之后才能开始删除下载的相关信息
helper.deleteInfos(url);
File file = new File(path);
if (file.exists()){
file.delete();
}else{
L.w("FileDownloadManager deleteDownload file not exist");
}
progressChangeHandler.sendEmptyMessage(STATE_DELETE);
}
});
deleteDownloadThread.start();
}
/**
* 停止开始线程
*/
private void stopStartThread(int sendState){
boolean state;
do {
//如果开始线程还未停止,比如用户摁完开始下载之后快速摁停止下载或删除,
// 这时需要更新状态,直到开始线程完成
state = (startDownloadThread!=null&&startDownloadThread.isAlive());
L.i("开始线程还未结束");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
progressChangeHandler.sendEmptyMessage(sendState);
}while (state);
}
/**
* 停止下载线程
*/
private void stopDownloadThread(int sendState){
boolean state;
//检测下载线程,确保下载线程要全部执行完成
state = threads.size()>0;
while(state){
state = countDownLatch.getCount()>0;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//还有线程在执行,所以状态还需要相应变更
progressChangeHandler.sendEmptyMessage(sendState);
}
}
/**
* 第一次下载文件,无下载记录,重新创建
*/
private void createDownloadInfos(){
try {
//更新状态为正在获取文件大小
progressChangeHandler.sendEmptyMessage(STATE_GETSIZE);
URL url = new URL(FileDownloadManager.this.url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
//添加这句话,要不然disconnect()会花费很多时间
conn.setRequestMethod("HEAD");
conn.setAllowUserInteraction(true);
conn.connect();
if (conn.getResponseCode()==200) {
fileSize = conn.getContentLength();
File file = FileUtils.checkAndCreateFile(path);
// 本地访问文件
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
accessFile.setLength(fileSize);
accessFile.close();
}
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
L.e("获取文件长度发生错误", e);
return;
}
//开始计算每个线程下载的字节范围
long startPos = 0;
//最后一个线程所下载的字节数一定要小于等于前面的线程保证文件完整性
long perSize = (long) Math.ceil((fileSize*1.0) / (THREAD_NUM*1.0));
for (int i=0; i= fileSize)
startPos = fileSize-1;
info.endPos = startPos;
info.completeSize = 0;
//下一个任务的开始位置要+1
startPos ++;
infos.add(info);
}
helper.insertInfos(url, infos);
}
/**
* 恢复以前的下载信息
*/
private void revertDownloadInfos(ArrayList> maps){
ArrayList columns = DownloadDB.TABLES.DOWNLOAD.getTableColumns();
for (HashMap map : maps){
DownloadInfo info = new DownloadInfo();
info.id = Integer.parseInt(map.get(columns.get(0)));
info.startPos = Long.parseLong(map.get(columns.get(2)));
info.endPos = Long.parseLong(map.get(columns.get(3)));
info.completeSize = Long.parseLong(map.get(columns.get(4)));
completeSize += info.completeSize;
fileSize = fileSize>info.endPos ? fileSize:info.endPos;
infos.add(info);
}
}
/**
* 获取该文件的总下载字节数
*/
private long getCompleteSize(){
completeSize = 0;
for (DownloadThread thread : threads)
completeSize += thread.getCompleteSize();
return completeSize;
}
/**
* 检测该文件是否下载完成
*/
private boolean isFileDownloadFinish(ArrayList> maps){
boolean result = true;
if (maps==null || maps.size() == 0){
return false;
}
ArrayList columns = DownloadDB.TABLES.DOWNLOAD.getTableColumns();
for (HashMap map : maps){
//如果完成字节数不足end-start,代表该线程未完成,所以需要继续下载
if (Long.parseLong(map.get(columns.get(4))) <
(Long.parseLong(map.get(columns.get(3)))- Long.parseLong(map.get(columns.get(2))))){
result = false;
break;
}
}
return result;
}
public void setListener(IDownloadProgressChangedListener listener){
this.listener = listener;
}
/**
* 下载进度更新接口
*/
public interface IDownloadProgressChangedListener{
void onProgressChanged(long completeSize, long totalSize);
void onStateChanged(int state);
}
/**
* 开启更新界面线程和开启下载线程
*/
private void startThreads(){
//准备开启线程
updateThread = new UpdateThread();
updateThread.start();
for (Thread thread : threads)
thread.start();
}
/**
* 下载完成之后的处理工作
*/
private void finishDownload(){
downloadState = false;
isDownloadFinish = true;
progressChangeHandler.sendEmptyMessage(STATE_FINISH);
if (updateThread!=null && updateThread.isAlive())
updateThread.canRun = false;
File file = new File(path);
File newFile = new File(fileName);
if (file.exists()){
file.renameTo(newFile);
}
helper.deleteInfos(url);
}
private static class ProgressChangeHandler extends Handler{
private WeakReference activityWeakReference;
public ProgressChangeHandler(FileDownloadManager manager){
activityWeakReference = new WeakReference<>(manager);
}
@Override
public void handleMessage(Message msg) {
if (msg.what == STATE_START_THREADS){
L.i("开启线程");
activityWeakReference.get().currentState = STATE_START_THREADS;
activityWeakReference.get().startThreads();
}
//下载进度更新
else if (msg.what == STATE_UPDATE_PROGRESS) {
if (activityWeakReference.get().getCompleteSize() >= activityWeakReference.get().fileSize) {
activityWeakReference.get().finishDownload();
T.getInstance().showShort("下载完成");
}
if (activityWeakReference.get().listener != null)
activityWeakReference.get().listener.onProgressChanged
(activityWeakReference.get().getCompleteSize(), activityWeakReference.get().fileSize);
}
//下载状态更新
else {
L.i("state"+msg.what);
if (activityWeakReference.get().currentState != msg.what){
//如果在下载过程中网络发生错误,或者服务器发生错误,就不再更新后面的停止状态
if ((activityWeakReference.get().currentState == STATE_NET_ERROR
|| activityWeakReference.get().currentState == STATE_SERVER_ERROR)
&& (msg.what==STATE_STOPING || msg.what==STATE_STOPED)){
return;
}
activityWeakReference.get().currentState = msg.what;
if (activityWeakReference.get().listener != null){
activityWeakReference.get().listener.onStateChanged(activityWeakReference.get().currentState);
}
}
}
}
}
/**
* 单独一个线程下载的信息
*/
public class DownloadInfo {
public int id;
public long startPos;
public long endPos;
public volatile long completeSize;
}
/**
* 下载线程
*/
private class DownloadThread extends Thread{
private DownloadInfo info;
HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream is = null;
public DownloadThread(DownloadInfo info){
this.info = info;
}
public long getCompleteSize(){
return info.completeSize;
}
public void initConnection() throws Exception{
URL url = new URL(FileDownloadManager.this.url);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(2000);
connection.setReadTimeout(5000);
connection.setRequestMethod("GET");
connection.setAllowUserInteraction(true);
// 设置范围,格式为Range:bytes x-y;
connection.setRequestProperty("Range", "bytes=" + (info.startPos + info.completeSize) + "-" + info.endPos);
}
@Override
public void run() {
try {
initConnection();
randomAccessFile = new RandomAccessFile(path, "rwd");
// 将要下载的字节写到上次写的末尾
randomAccessFile.seek(info.startPos + info.completeSize);
//偶尔发现,下载的资源有时候403 FileNotFoundException,这种情况重新连接一次,不成功就Log出错误日志
try {
is = connection.getInputStream();
}catch (Exception e){
//重新连接
initConnection();
try {
is = connection.getInputStream();
}catch (Exception ee){
L.e("无法连接"+info.startPos+"~"+info.endPos+"处资源", ee);
return;
}
}
byte[] buffer = new byte[1024 * 8];
int length;
while (((length = is.read(buffer)) != -1) && downloadState) {
randomAccessFile.write(buffer, 0, length);
info.completeSize += length;
}
L.i("结束下载线程");
} catch (Exception e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
try {
//android 4.x disconnect或者close会耗费很长的时间,解决了很长时间,暂未找到方法,有的联系我
is.close();
randomAccessFile.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 更新数据库和界面线程
*/
private class UpdateThread extends Thread{
public boolean canRun = true;
private long lastCompleteSize = 0;
/** 重试时间3s */
private final int RETRYCOUNT = 3;
private int retry;
@Override
public void run() {
retry = RETRYCOUNT;
try {
while (canRun) {
// 更新数据库中的下载信息
helper.updateInfos(url, infos);
L.i("lastCompleteSize " + lastCompleteSize);
L.i("更新界面 " + getCompleteSize() + " fileSize" + fileSize);
//由于网络不稳定,或者是服务器不稳定导致的无法返回数据
if (lastCompleteSize == getCompleteSize()){
retry--;
if (retry <= 0){
//停止下载线程,并提示用户重试
FileDownloadManager.this.stop();
//有网络的情况下为服务器错误
if (CommonUtils.isNetworkAvailable()){
L.e("服务器错误,请重试");
progressChangeHandler.sendEmptyMessage(STATE_SERVER_ERROR);
}else{
L.e("网络错误,请连接网络后重试");
progressChangeHandler.sendEmptyMessage(STATE_NET_ERROR);
}
}
}else{
retry = RETRYCOUNT;
}
lastCompleteSize = getCompleteSize();
//更新界面
progressChangeHandler.sendEmptyMessage(STATE_UPDATE_PROGRESS);
//每隔1秒操作数据库和更新界面,防止频繁的更新
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/guide/GuideManager.java
================================================
package com.android.libcore.guide;
import android.app.Activity;
import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import java.util.ArrayList;
/**
* Description: 蒙版管理器,用来管理页面的蒙版
* 使用{@link #initMask(Integer...)}传入资源layout的id,调用{@link #showMaskFullScreen()}
* 或者{@link #showMaskInContent()}显示蒙版,使用完蒙版之后需要调用{@link #clearMask()}清空蒙版,要不然会影响下次的使用
* 注意传入的layout文件中请在需要点击消失的地方加入一个id为click_to_disappear 的View即可
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-15
*/
public class GuideManager {
private static volatile GuideManager instance = null;
private ArrayList mViews;
private int mCurrentShowId;
private View.OnClickListener mListener;
private boolean mIsShowFullScreen = true;
private GuideManager() {
}
public static GuideManager getInstance() {
if (instance == null) {
synchronized (GuideManager.class) {
if (instance == null) {
instance = new GuideManager();
}
}
}
return instance;
}
/**
* 初始化监听器
*/
private void initListener() {
mListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
showNextMask();
}
};
}
/**
* 此函数用来传入蒙版的id,蒙版的显示顺序将会和参数的顺序一致
*
* @param layoutIds 需要展示蒙版的资源id,按照显示顺序传递即可
*/
public void initMask(Integer... layoutIds) {
if (layoutIds.length <= 0)
throw new IllegalArgumentException("参数不能为空");
LayoutInflater inflater = LayoutInflater.from(RootApplication.getInstance());
try {
//该处需要去根据名字找到该view的id
int btnId = RootApplication.getInstance().getResources().getIdentifier("click_to_disappear", "id",
RootApplication.getInstance().getPackageName());
if (btnId == 0) {
L.e("请先定义id为click_to_disappear的view");
return;
}
mViews = new ArrayList<>();
mCurrentShowId = 0;
initListener();
for (int layoutId : layoutIds) {
//虽然layoutId不在该子module里面定义,但是依然能够inflate
View view = inflater.inflate(layoutId, null);
View btn = view.findViewById(btnId);
if (btn == null) {
L.e("layout id为" + layoutId + "的布局中没有定义click_to_disappear的view");
return;
}
btn.setOnClickListener(mListener);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//用来屏蔽其他view的点击事件
}
});
mViews.add(view);
}
} catch (InflateException e) {
L.e("传入layout id 有误", e);
mViews = null;
}
}
/**
* 使用完蒙版之后记得清空蒙版
*/
public void clearMask() {
mViews = null;
mListener = null;
instance = null;
}
/**
* 全屏展示蒙版,此蒙版将会覆盖top bar
*/
public void showMaskFullScreen() {
if (mViews == null) {
L.e("请先初始化该蒙版");
return;
}
mIsShowFullScreen = true;
mCurrentShowId = 0;
//将其添加在主体界面的上部,覆盖主体窗口
((ViewGroup) (((Activity) (RootApplication.getInstance())).getWindow().getDecorView())).addView(mViews.get
(mCurrentShowId), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
/**
* 在内容区域内显示蒙版,不会覆盖top bar
*/
public void showMaskInContent() {
if (mViews == null) {
L.e("请先初始化该蒙版");
return;
}
mIsShowFullScreen = false;
mCurrentShowId = 0;
//获取activity_base_layout.xml里面的base_content的id
int base_content = RootApplication.getInstance().getResources().getIdentifier("base_content", "id",
RootApplication.getInstance().getPackageName());
//将其添加在主体界面的上部,覆盖主体窗口
((ViewGroup) (((((Activity) (RootApplication.getInstance())).getWindow().getDecorView())).findViewById(base_content)))
.addView(mViews.get(mCurrentShowId), new ViewGroup.LayoutParams(-1, -1));
}
/**
* 手动调用来显示下一个蒙版
*/
public boolean showNextMask(){
//如果当前没有显示任何模板,返回false标识不处理
if (mViews ==null || mViews.size()==0 || mCurrentShowId >= mViews.size()){
return false;
}
if (mIsShowFullScreen){
//将上一次的蒙版去除
((ViewGroup) (((Activity) (RootApplication.getInstance())).getWindow().getDecorView())).removeView(mViews.get(mCurrentShowId));
mCurrentShowId++;
if (mCurrentShowId < mViews.size()) {
((ViewGroup) (((Activity) (RootApplication.getInstance())).getWindow().getDecorView()))
.addView(mViews.get(mCurrentShowId), new ViewGroup.LayoutParams(ViewGroup
.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}else{
//获取activity_base_layout.xml里面的content
int base_content = RootApplication.getInstance().getResources().getIdentifier("base_content", "id",
RootApplication.getInstance().getPackageName());
//将上一次的蒙版去除
((ViewGroup) (((((Activity) (RootApplication.getInstance())).getWindow().getDecorView())).findViewById
(base_content))).removeView(mViews.get(mCurrentShowId));
mCurrentShowId++;
if (mCurrentShowId < mViews.size()) {
((ViewGroup) (((((Activity) (RootApplication.getInstance())).getWindow().getDecorView())).findViewById
(base_content))).addView(mViews.get(mCurrentShowId), new ViewGroup.LayoutParams(ViewGroup.LayoutParams
.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
return true;
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/log/L.java
================================================
package com.android.libcore.log;
import android.text.TextUtils;
import android.util.Log;
import com.android.libcore.application.RootApplication;
import java.util.Locale;
/**
* Description: log打印类
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-07
*/
public final class L {
private static boolean LOG_ENABLE = true;
public static String LOG_TAG = "LOG_TAG";
private L(){}
static {
//一个进程只会调用一次static block,所以使用静态块的方式获取进程名
LOG_ENABLE = RootApplication.DEBUG;
LOG_TAG = "[PID:"+ android.os.Process.myPid() +"]";
}
/**
* 打印基本信息
* @param args 需要打印出来的额外信息,每次换行
*/
public static void i(String message, Object... args){
log(Log.INFO, message, null, args);
}
/**
* 打印警告
* @param args 需要打印出来的额外信息,每次换行
*/
public static void w(String message, Object... args){
log(Log.WARN, message, null, args);
}
/***
* 打印异常
* @param throwable 需要打印的异常信息
*/
public static void e(Throwable throwable){
log(Log.ERROR, null, throwable);
}
/***
* 打印异常
*/
public static void e(String message, Object... args){
log(Log.ERROR, message, null, args);
}
/***
* 打印异常
* @param throwable 需要打印的异常信息
* @param args 需要打印出来的额外信息,每次换行
*/
public static void e(String message, Throwable throwable, Object... args){
log(Log.ERROR, message, throwable, args);
}
private static void log(int priority, String message, Throwable throwable, Object... args){
if (!LOG_ENABLE)
return;
StringBuilder log = new StringBuilder();
//获取调用者类,函数信息和行数信息
StackTraceElement[] trace = new Throwable().fillInStackTrace().getStackTrace();
String caller = "";
for (int i = 2; i < trace.length; i++) {
Class> clazz = trace[i].getClass();
if (!clazz.equals(L.class)) {
String callingClass = trace[i].getClassName();
callingClass = callingClass.substring(callingClass.lastIndexOf('.') + 1);
callingClass = callingClass.substring(callingClass.lastIndexOf('$') + 1);
caller = callingClass + "." + trace[i].getMethodName()
+ "(line:" + trace[i].getLineNumber() +")";
break;
}
}
//将message附加到日志上
if (!TextUtils.isEmpty(message))
log.append(String.format(Locale.US, "[TID:%d] %s: %s",
Thread.currentThread().getId(), caller, message)).append("\n");
//附加异常信息
if (throwable != null)
log.append(Log.getStackTraceString(throwable)).append("\n");
//附加额外信息
for (Object object: args)
log.append(object.toString()).append("\n");
//打印日志
Log.println(priority, LOG_TAG, log.toString());
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/net/NetError.java
================================================
package com.android.libcore.net;
import com.android.volley.VolleyError;
/**
* Description: 网络访问错误error
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-09-15
*/
public class NetError extends Exception{
public int errorCode;
public String errorMessage;
/**
* 将volley的错误信息转换成通用的信息
*/
public void transferVolleyError(VolleyError error){
if (error.networkResponse != null)
this.errorCode = error.networkResponse.statusCode;
this.errorMessage = error.toString();
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/net/imageloader/ImageLoader.java
================================================
package com.android.libcore.net.imageloader;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import com.android.libcore.net.NetError;
import com.android.volley.RequestQueue;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.Volley;
import java.lang.reflect.Field;
/**
* Description: 图片加载类
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-10-21
*/
public class ImageLoader {
/** 最大的图片缓存大小 */
private final int MAXDISKCACHEBYTES = 10 * 1024 *1024;
private static volatile ImageLoader instance;
private com.android.volley.toolbox.ImageLoader mImageLoader;
private ImageLoader(){
RequestQueue requestQueue = Volley.newRequestQueue(RootApplication.getInstance(), MAXDISKCACHEBYTES);
VolleyLruCache lruCache = new VolleyLruCache();
mImageLoader = new com.android.volley.toolbox.ImageLoader(requestQueue, lruCache);
}
public static ImageLoader getInstance(){
if (instance == null){
synchronized (ImageLoader.class){
if (instance == null)
instance = new ImageLoader();
}
}
return instance;
}
/** 通过反射获取imageview的大小 */
private int getImageViewFieldValue(Object object, String fieldName) {
int value = 0;
try {
Field field = ImageView.class.getDeclaredField(fieldName);
field.setAccessible(true);
int fieldValue = (Integer) field.get(object);
if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {
value = fieldValue;
}
} catch (Exception e) {
L.e(e);
}
return value;
}
/**
* 加载图片
* @param url 图片url
* @param imageView 需要加载图片的视图
*/
public void loadImage(String url, final ImageView imageView){
loadImage(url, imageView, null);
}
/**
* 只带回调的图片加载
* @param url 图片url
* @param listener 图片加载回调
*/
public void loadImage(String url, final OnLoadCallBack listener){
loadImage(url, 0, 0, listener);
}
/**
* 带回调的加载图片
* @param url 图片url
* @param width 需要加载的图片宽
* @param height 需要加载的图片高
* @param listener 加载图片完成回调
*/
public void loadImage(String url, int width, int height, final OnLoadCallBack listener){
loadImage(url, width, height, null, listener);
}
/**
* 带回调的加载图片
* @param url 图片url
* @param imageView 需要加载图片的视图
* @param listener 加载图片的回调
*/
public void loadImage(String url, final ImageView imageView, final OnLoadCallBack listener){
int width = getImageViewFieldValue(imageView, "mMaxWidth");
int height = getImageViewFieldValue(imageView, "mMaxHeight");
loadImage(url, width, height, imageView, listener);
}
/**
* 加载图片
* @param url 图片url
* @param width 图片加载的图片宽
* @param height 图片加载的图片高
* @param imageView 需要加载图片的视图
*/
public void loadImage(String url, int width, int height, final ImageView imageView){
loadImage(url, width, height, imageView, null);
}
/**
* 加载图片
* @param url 图片url
* @param imageView 需要加载图片的视图
* @param width 需要加载视图的宽
* @param height 需要加载视图的高
* @param listener 加载图片回调
*/
public void loadImage(String url, int width, int height, final ImageView imageView, final OnLoadCallBack listener){
mImageLoader.get(url, new com.android.volley.toolbox.ImageLoader.ImageListener() {
@Override
public void onResponse(com.android.volley.toolbox.ImageLoader.ImageContainer response, boolean isImmediate) {
if (imageView != null)
imageView.setImageBitmap(response.getBitmap());
if (listener != null)
listener.onLoadSuccess(response.getBitmap(), response.getRequestUrl());
}
@Override
public void onErrorResponse(VolleyError error) {
if (listener != null) {
NetError netError = new NetError();
netError.transferVolleyError(error);
listener.onLoadFail(netError);
}
}
}, width, height);
}
/**
* 加载图片
* @param url 图片url
* @param imageView 需要加载该图片的url
* @param defaultImageResId 加载图片时的默认资源id
* @param errorImageResId 加载图片失败时显示的图片资源id
*/
public void loadImage(String url, final ImageView imageView, int defaultImageResId, int errorImageResId){
int width = getImageViewFieldValue(imageView, "mMaxWidth");
int height = getImageViewFieldValue(imageView, "mMaxHeight");
loadImage(url,width, height, imageView, defaultImageResId, errorImageResId);
}
/**
* 加载图片
* @param url 图片url
* @param imageView 需要加载该图片的url
* @param defaultImageResId 加载图片时的默认资源id
* @param errorImageResId 加载图片失败时显示的图片资源id
* @param width 加载图片的宽度
* @param height 加载图片的高度
*/
public void loadImage(String url, int width, int height, final ImageView imageView,
int defaultImageResId, int errorImageResId){
com.android.volley.toolbox.ImageLoader.ImageListener listener =
com.android.volley.toolbox.ImageLoader.getImageListener(imageView,
defaultImageResId, errorImageResId);
mImageLoader.get(url, listener, width, height);
}
/**
* 加载图片回调
*/
public interface OnLoadCallBack {
void onLoadSuccess(Bitmap bitmap, String url);
void onLoadFail(NetError error);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/net/imageloader/VolleyLruCache.java
================================================
package com.android.libcore.net.imageloader;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import com.android.volley.toolbox.ImageLoader;
/**
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-10-20
*/
public class VolleyLruCache extends LruCache implements ImageLoader.ImageCache{
private static int getCacheSize(){
return (int)(Runtime.getRuntime().maxMemory()/1024/8);
}
public VolleyLruCache() {
this(getCacheSize());
}
private VolleyLruCache(int size){
super(size);
}
@Override
public Bitmap getBitmap(String url) {
return get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/net/netapi/BaseNetApi.java
================================================
package com.android.libcore.net.netapi;
import android.app.Activity;
import android.content.Context;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import com.android.libcore.net.NetError;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest;
import com.android.volley.toolbox.JsonArrayRequest;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import org.json.JSONArray;
import org.json.JSONObject;
import java.lang.reflect.Constructor;
import java.util.Map;
/**
* Description: 所有网络访问基础类,使用了volley框架,并且进行了简单的基础
* 封装,所有进行网络访问的地方都使用该封装类 ,方便以后更换网络访问框架,
* 只需修改libcore,libcore-ui层相关类即可,这也是封装的唯一目的
*
* 不能在libcore,libcore-ui层的相关类之外直接使用volley框架内容
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-08
*/
public abstract class BaseNetApi {
/** 网络访问requestQueue */
private RequestQueue requestQueue;
private Context mContext;
private RequestQueue getRequestQueue(int maxDiskCacheBytes){
if (requestQueue == null)
requestQueue = Volley.newRequestQueue(mContext, maxDiskCacheBytes);
return requestQueue;
}
protected RequestQueue getRequestQueue(){
return getRequestQueue(-1);
}
/**
* 回调接口
*/
public interface OnNetCallback{
void onSuccess(T result);
void onFail(NetError error);
}
private boolean checkIfExtendsRequest(Class clazz){
while (clazz.getSuperclass() != null){
clazz = clazz.getSuperclass();
if (clazz == Request.class)
return true;
}
return false;
}
/**
* 网络请求
*/
protected void makeRequest(final Context context, Class extends Request> clazz,
String url, final Map params, final OnNetCallback callback){
//网络请求
Request request = null;
//失败回调
Response.ErrorListener errorListener = null;
//成功回调
Response.Listener listener = null;
//判空
if (callback != null) {
errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (context instanceof Activity && (((Activity)(context)).isFinishing())) {
L.i("activity finish, not callback");
return ;
}
NetError netError = new NetError();
netError.transferVolleyError(error);
callback.onFail(netError);
}
};
listener = new Response.Listener() {
@Override
public void onResponse(T response) {
if (context instanceof Activity && (((Activity)(context)).isFinishing())) {
L.i("activity finish, not callback");
return ;
}
callback.onSuccess(response);
}
};
}
//启动网络请求
if (clazz == ImageRequest.class){
throw new IllegalArgumentException("please use imageloader");
}else {
try {
Constructor constructor = clazz.getConstructor(Integer.class, String.class, Response.Listener.class,
Response.ErrorListener.class, Map.class);
int method = Request.Method.GET;
if (params != null)
method = Request.Method.POST;
request = (Request) constructor.newInstance(method, url, listener, errorListener, params);
} catch (Exception e) {
L.e("error reflect", e);
return;
}
}
//自定义超时时间,重试次数
// request.setRetryPolicy(new DefaultRetryPolicy());
getRequestQueue().add(request);
}
/**
* 对{@linkplain StringRequest}的封装类
*/
private static class StringRequestImpl extends StringRequest{
private Map params;
public StringRequestImpl(Integer method, String url, Response.Listener listener,
Response.ErrorListener errorListener, Map params) {
super(method, url, listener, errorListener);
this.params = params;
}
@Override
protected Map getParams() throws AuthFailureError {
return params;
}
}
/**
* 对{@linkplain JsonObjectRequest}的封装类
*/
private static class JsonObjectRequestImpl extends JsonObjectRequest{
private Map params;
public JsonObjectRequestImpl(Integer method, String url, Response.Listener listener,
Response.ErrorListener errorListener, Map params) {
super(method, url, new JSONObject(params), listener, errorListener);
this.params = params;
}
@Override
protected Map getParams() throws AuthFailureError {
return params;
}
}
/**
* 对{@linkplain JsonArrayRequest}的封装类
*/
private static class JsonArrayRequestImpl extends JsonArrayRequest{
private Map params;
public JsonArrayRequestImpl(Integer method, String url, Response.Listener listener,
Response.ErrorListener errorListener, Map params) {
super(method, url, new JSONObject(params), listener, errorListener);
this.params = params;
}
@Override
protected Map getParams() throws AuthFailureError {
return params;
}
}
/**
* string 请求
* @param context 相关上下文
* @param url 网络访问url
* @param params 网络请求参数
* @param callback 网络请求回调
*/
public void stringRequest(Context context, String url, Map params, OnNetCallback callback){
mContext = context;
makeRequest(context, StringRequestImpl.class, url, params, callback);
}
/**
* jsonObject 请求
* @param context 相关上下文
* @param url 网络访问url
* @param params 网络请求参数
* @param callback 网络请求回调
*/
public void jsonObjectRequest(Context context, String url, Map params, OnNetCallback callback){
mContext = context;
makeRequest(context, JsonObjectRequestImpl.class, url, params, callback);
}
/**
* jsonArray 请求
* @param context 相关上下文
* @param url 网络访问url
* @param params 网络请求参数
* @param callback 网络请求回调
*/
public void jsonArrayRequest(Context context, String url, Map params, OnNetCallback callback){
mContext = context;
makeRequest(context, JsonArrayRequestImpl.class, url, params, callback);
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/utils/CommonUtils.java
================================================
package com.android.libcore.utils;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.ViewConfiguration;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* Description: 基础工具类,该类的所有函数如下所示(所有添加到该类的函数都应该在这标识出来,以防重复)
*
* {@link #dp2px(float)}用来将dp转换为px
* {@link #px2dp(float)}用来将px转换为dp
* {@link #getScreenWidth()}获取手机屏幕宽度
* {@link #getScreenHeight()}获取手机屏幕高度(有些手机会除去navigation bar高度)
* {@link #isNetworkAvailable()}用来判断网络是否可用
* {@link #isNetworkWifi()}用来判断网络是否是wifi
* {@link #hasNavigationBar()}判断手机是否会有navigation bar
* {@link #md5(String)}用来对字符串进行md5加密
* {@link #pathToUri(String)}手机图片路径path转uri
* {@link #uriToPath(Uri)}手机图片uri转路径path
*
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-14
*/
public class CommonUtils {
public static int dp2px(float dp){
final float scale = RootApplication.getInstance().getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
public static int px2dp(float px) {
final float scale = RootApplication.getInstance().getResources().getDisplayMetrics().density;
return (int) (px / scale + 0.5f);
}
public static int getScreenWidth(){
DisplayMetrics dm = new DisplayMetrics();
((Activity)RootApplication.getInstance()).getWindow().getWindowManager().getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
}
public static int getScreenHeight(){
DisplayMetrics dm = new DisplayMetrics();
((Activity)RootApplication.getInstance()).getWindow().getWindowManager().getDefaultDisplay().getMetrics(dm);
return dm.heightPixels;
}
/**
* 判断当前网络是是否可用
*/
public static boolean isNetworkAvailable(){
ConnectivityManager cm = (ConnectivityManager) RootApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
return info!=null && info.isAvailable();
}
/**
* 判断当前的网络是否是wifi
*/
public static boolean isNetworkWifi(){
if (!isNetworkAvailable()){
L.e("当前网络可用,请先调用isNetworkAvailable()函数");
}
ConnectivityManager cm = (ConnectivityManager) RootApplication.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info.getType() == ConnectivityManager.TYPE_WIFI)
return true;
return false;
}
/**
* 检查手机是否会有虚拟底部navigation bar
*/
public static boolean hasNavigationBar(){
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
boolean hasHomeKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME);
if(hasBackKey && hasHomeKey) {
return false;
}
return true;
}
/**
* md5加密
*/
public static String md5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Huh, MD5 should be supported?", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Huh, UTF-8 should be supported?", e);
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) hex.append("0");
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
/**
* 用来将uri转变成path,兼容19版本以上
* 原文地址http://stackoverflow.com/questions/19834842/android-gallery-on-kitkat-returns-different-uri-for-intent-action-get-content
* @param uri The Uri to query.
*/
public static String uriToPath(final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(RootApplication.getInstance(), uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(RootApplication.getInstance(), contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(RootApplication.getInstance(), contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(RootApplication.getInstance(), uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
/**
* 手机图片path转uri
*/
public static Uri pathToUri(String path){
Uri uri = null;
if (path != null) {
path = Uri.decode(path);
ContentResolver cr = RootApplication.getInstance().getContentResolver();
StringBuffer buff = new StringBuffer();
buff.append("(").append(MediaStore.Images.ImageColumns.DATA)
.append("=").append("'" + path + "'").append(")");
Cursor cur = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[] { MediaStore.Images.ImageColumns._ID },
buff.toString(), null, null);
int index = 0;
for (cur.moveToFirst(); !cur.isAfterLast(); cur.moveToNext()) {
index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID);
index = cur.getInt(index);
}
if (index == 0) {
} else {
Uri uri_temp = Uri.parse("content://media/external/images/media/" + index);
if (uri_temp != null) {
uri = uri_temp;
}
}
}
return uri;
}
/**
* 获取进程名
*/
public static String getCurrentProcessName(Context context){
String currentProcessName = "";
int pid = android.os.Process.myPid();
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses())
{
if (processInfo.pid == pid)
{
currentProcessName = processInfo.processName;
break;
}
}
return currentProcessName;
}
/**
* 制造崩溃
*/
public static int makeCrash(){
int a[] = new int[2];
return a[3];
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/utils/FileUtils.java
================================================
package com.android.libcore.utils;
import android.os.Environment;
import android.text.format.Formatter;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Description: 文件相关的操作,如果需要在SD卡主目录下建立子目录,请在{@link ExternalStorageType}
* 枚举下建立相同的变量,带上目录名字即可,所有创建之后的目录末尾自带"/"文件分隔符
* image,voice,video目录下会有.nomedia文件来屏蔽系统扫描
*
*
* {@link #checkAndCreateFile(String)}根据path创建文件
* {@link #checkAndCreateChildDirectory(String)}根据path创建子目录,自动会在末尾添加"/"文件分隔符
*
* {@link #getExternalStoragePath()}获取SD卡根目录,不要在此进行操作以防污染主目录
* {@link #getExternalStorageTempPath()}获取SD卡主目录下缓存目录
* {@link #createFileInImageDirectory(String)}在temp目录下创建文件并返回
* {@link #clearExternalStorageTemp()}应用退出之后删除temp目录
* {@link #getExternalStorageFilePath()} 获取SD卡主目录下文件目录
* {@link #createFileInFileDirectory(String)} 在file目录下创建文件并返回
* {@link #getExternalStorageImagePath()}获取SD卡主目录下图片目录
* {@link #createFileInImageDirectory(String)}在image目录下创建文件并返回
* {@link #getExternalStorageVoicePath()}获取SD卡主目录下声音目录
* {@link #createFileInVoiceDirectory(String)}在voice目录下创建文件并返回
* {@link #getExternalStorageVideoPath()}获取SD卡主目录下视频目录
* {@link #createFileInVideoDirectory(String)}在video目录下创建文件并返回
* {@link #getExternalStorageHtmlPath()}获取SD卡主目录下网页目录
* {@link #createFileInHtmlDirectory(String)}在html目录下创建文件并返回
*
* {@link #getFileOrDirectorySize(String)}获取目录或者文件的大小,单位KB
*
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-22
*/
public class FileUtils {
/** 外部SD卡根目录下的文件夹名 */
private static final String EXTERNAL_STORAGE_PATH = "/FrameWork";
/** 当temp目录大小超过该值的时候,清空该目录,单位为KB */
private static final long K_BYTES_TO_DELETE = 10 * 1024;
/**
* 检测并且创建文件
*/
public static File checkAndCreateFile(String path){
File file = new File(path);
if (!file.exists())
try {
file.createNewFile();
}catch (IOException e){
e.printStackTrace();
}
return file;
}
/**
* 创建主目录下子目录,自动会在末尾添加"/"文件分隔符
*/
public static String checkAndCreateChildDirectory(String path){
File file = new File(path);
if (!file.exists())
file.mkdirs();
if (!file.exists())
return null;
return path+"/";
}
/**
* 外部{@link #EXTERNAL_STORAGE_PATH}目录或者/data/data/com.android.framework/files/目录(如果SD卡不可用),
* 末尾自带"/"符号
*/
public static String getExternalStoragePath(){
String path = null;
//需要检测外部SD卡的挂载状态
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
path = checkAndCreateChildDirectory(Environment.getExternalStorageDirectory().getPath() + EXTERNAL_STORAGE_PATH);
}
if (path == null){
//如果外部SD卡不可用,使用"/data/data/com.android.framework/files/"目录
path = RootApplication.getInstance().getFilesDir().getPath();
}
if (!path.subSequence(path.length()-1, path.length()).equals("/"))
path += "/";
return path;
}
/**
* 所有在外部存储目录下的子目录都需要在此定义文件夹名
*/
public enum ExternalStorageType{
TEMP("temp"),FILE("file"),IMAGE("image"),VOICE("voice"),VIDEO("video"),HTML("html");
private String typeName;
ExternalStorageType(String typeName){
this.typeName = typeName;
}
public String getFilePath(String parentPath) {
String path = parentPath;
if (!(parentPath.charAt(parentPath.length()-1)=='/')){
path += "/";
}
return path+typeName;
}
}
/**
* 获取外部临时文件目录,过大会自动删除
*/
public static String getExternalStorageTempPath(){
ExternalStorageType type = ExternalStorageType.TEMP;
return checkAndCreateChildDirectory(type.getFilePath(getExternalStoragePath()));
}
/**
* 在外部{@link #getExternalStorageTempPath()}目录下创建文件,
*/
public static File createFileInTempDirectory(String filename){
return checkAndCreateFile(getExternalStorageTempPath() + filename);
}
/**
* 删除外部临时文件目录
*/
public static void clearExternalStorageTemp(){
L.i("application close clear temp directory");
if (getFileOrDirectorySize(getExternalStorageTempPath()) >= K_BYTES_TO_DELETE) {
File file = new File(getExternalStorageTempPath());
File[] files = file.listFiles();
for (File temp : files)
temp.delete();
}
}
/**
* 获取外部文件目录,过大会自动删除
*/
public static String getExternalStorageFilePath(){
ExternalStorageType type = ExternalStorageType.FILE;
return checkAndCreateChildDirectory(type.getFilePath(getExternalStoragePath()));
}
/**
* 在外部{@link #getExternalStorageFilePath()}目录下创建文件,
*/
public static File createFileInFileDirectory(String filename){
return checkAndCreateFile(getExternalStorageTempPath() + filename);
}
/**
* 获取外部图片文件目录,在该目录下会创建.nomedia文件防止系统扫描
*/
public static String getExternalStorageImagePath(){
ExternalStorageType type = ExternalStorageType.IMAGE;
String path = type.getFilePath(getExternalStoragePath());
String result = checkAndCreateChildDirectory(path);
checkAndCreateNoMedia(path);
return result;
}
/**
* 在外部{@link #getExternalStorageImagePath()}目录下创建文件,
*/
public static File createFileInImageDirectory(String filename){
return checkAndCreateFile(getExternalStorageImagePath() + filename);
}
/**
* 获取外部声音文件目录,在该目录下会创建.nomedia文件防止系统扫描
*/
public static String getExternalStorageVoicePath(){
ExternalStorageType type = ExternalStorageType.VOICE;
String path = type.getFilePath(getExternalStoragePath());
String result = checkAndCreateChildDirectory(path);
checkAndCreateNoMedia(path);
return result;
}
/**
* 在外部{@link #getExternalStorageVoicePath()}目录下创建文件,
*/
public static File createFileInVoiceDirectory(String filename){
return checkAndCreateFile(getExternalStorageVoicePath() + filename);
}
/**
* 获取外部视频文件目录,在该目录下会创建.nomedia文件防止系统扫描
*/
public static String getExternalStorageVideoPath(){
ExternalStorageType type = ExternalStorageType.VIDEO;
String path = type.getFilePath(getExternalStoragePath());
String result = checkAndCreateChildDirectory(path);
checkAndCreateNoMedia(path);
return result;
}
/**
* 在外部{@link #getExternalStorageVoicePath()}目录下创建文件,
*/
public static File createFileInVideoDirectory(String filename){
return checkAndCreateFile(getExternalStorageVideoPath() + filename);
}
/**
* 获取外部网页文件目录
*/
public static String getExternalStorageHtmlPath(){
ExternalStorageType type = ExternalStorageType.HTML;
return checkAndCreateChildDirectory(type.getFilePath(getExternalStoragePath()));
}
/**
* 在外部{@link #getExternalStorageHtmlPath()}目录下创建文件,
*/
public static File createFileInHtmlDirectory(String filename){
return checkAndCreateFile(getExternalStorageHtmlPath() + filename);
}
/**
* 获取文件或者目录大小,单位为B
*/
public static long getFileOrDirectorySize(String path){
long size = 0;
if (path == null)
return size;
File file = new File(path);
if (!file.exists())
return size;
if (file.isDirectory()){
File[] files = file.listFiles();
if (files != null) {
for (File temp : files) {
size += getFileOrDirectorySize(temp.getAbsolutePath());
}
}
}else{
size = getFileSize(file);
}
return size;
}
/**
* 文件拷贝
* @param src 源文件
* @param des 目标文件
*/
public static void copyFile(File src, File des){
try {
int byteRead = 0;
InputStream inputStream = new FileInputStream(src);
if (!des.exists())
des.createNewFile();
OutputStream outputStream = new FileOutputStream(des);
byte[] buffer = new byte[1024];
while ( (byteRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, byteRead);
}
inputStream.close();
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 检测该目录下是否有nomedia文件,如果没有就创建
*/
private static void checkAndCreateNoMedia(String path){
checkAndCreateFile(path + "/.nomedia");
}
/**
* @return 返回文件大小,单位为byte
*/
private static long getFileSize(File file){
long size = 0;
if (file.exists()){
try {
FileInputStream fis = new FileInputStream(file);
size = fis.available();
}catch (Exception e){
e.printStackTrace();
}
}
return size;
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/utils/ImageUtils.java
================================================
package com.android.libcore.utils;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.ExifInterface;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.webkit.WebView;
import com.android.libcore.Toast.T;
import com.android.libcore.application.RootApplication;
import com.android.libcore.log.L;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Description: 图片相关处理类
*
*
* {@link #centerSquareScaleBitmap(Bitmap, int)}截取图片的正中部分
* {@link #toRoundCorner(Bitmap, int)}将图片截成圆角的方法
* {@link #saveBitmap(Bitmap, String, Runnable)}保存图片到制定路径下
* {@link #screenShot(Activity)}截取手机当前屏幕
* {@link #viewShot(View)}截取view的整个显示内容
* {@link #compressBitmap(String, int, int)}等比例压缩图片至指定大小
* {@link #resizeBitmap(Bitmap, int, int)}不等比例缩放bitmap至指定长宽
* {@link #getPictureDegree(String)}获取图片的旋转角度
* {@link #rotateBitmap(Bitmap, int)}旋转图片至指定角度
*
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-31
*/
public class ImageUtils {
/**
* @param bitmap 原图
* @param edgeLength 希望得到的正方形部分的边长
* @return 截取正中部分后的位图。
*/
public static Bitmap centerSquareScaleBitmap(Bitmap bitmap, int edgeLength){
if(null == bitmap || edgeLength <= 0){
return null;
}
Bitmap result = bitmap;
int widthOrg = bitmap.getWidth();
int heightOrg = bitmap.getHeight();
//从图中截取正中间的正方形部分。
int xTopLeft = (widthOrg - edgeLength) / 2;
int yTopLeft = (heightOrg - edgeLength) / 2;
if(xTopLeft==0 && yTopLeft==0)
return result;
try{
result = Bitmap.createBitmap(bitmap, xTopLeft, yTopLeft, edgeLength, edgeLength);
}
catch(OutOfMemoryError e){
return result;
}
return result;
}
/**
* 获取圆角位图的方法
* @param bitmap 需要转化成圆角的位图
* @param pixels 圆角的度数,数值越大,圆角越大
* @return 处理后的圆角位图
*/
public static Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
try {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = pixels;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}catch (OutOfMemoryError e){
e.printStackTrace();
return null;
}
}
/**
* 截屏,只能截取当前屏幕显示的区域,不包含status bar
*/
public static Bitmap screenShot(Activity activity){
View view = activity.getWindow().getDecorView();
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
Bitmap b1 = view.getDrawingCache();
// 获取状态栏高度
Rect frame = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;
// 获取屏幕长和高
int width = CommonUtils.getScreenWidth();
int height = CommonUtils.getScreenHeight();
// 去掉标题栏
Bitmap b = Bitmap.createBitmap(b1, 0, statusBarHeight, width, height - statusBarHeight);
view.destroyDrawingCache();
return b;
}
/**
* view截图,webview和scrollview(scrollview需要传入子view)之类的view能够截取整个长度的bitmap,
* 如果webview内容很多,view.draw(Canvas)方法会很耗时,在子进程中操作会有额外的问题,所以会暂时阻塞
* UI主线程,求方法~
*/
public static Bitmap viewShot(final View view){
if (view == null)
return null;
view.setDrawingCacheEnabled(true);
view.buildDrawingCache();
// if (view.getMeasuredWidth()<=0 || view.getMeasuredHeight()<=0) {
int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(measureSpec, measureSpec);
// }
if (view.getMeasuredWidth()<=0 || view.getMeasuredHeight()<=0) {
L.e("ImageUtils.viewShot size error");
return null;
}
Bitmap bm;
try {
bm = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
}catch (OutOfMemoryError e){
System.gc();
try {
bm = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
}catch (OutOfMemoryError ee){
L.e("ImageUtils.viewShot error", ee);
return null;
}
}
Canvas bigCanvas = new Canvas(bm);
Paint paint = new Paint();
int iHeight = bm.getHeight();
bigCanvas.drawBitmap(bm, 0, iHeight, paint);
view.draw(bigCanvas);
return bm;
}
/**
* 保存bitmap到指定路径下
* @param finish 保存完之后的回调,不在主线程执行
*/
public static void saveBitmap(final Bitmap bitmap, final String filePath, final Runnable finish) {
if (bitmap == null){
L.w("bitmap is null");
return;
}
new Thread(new Runnable() {
@Override
public void run() {
try {
File file = new File(filePath);
if (!file.exists())
file.createNewFile();
else {
file.delete();
file.createNewFile();
}
//使用FileOutputStream防止OOM
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (finish != null)
finish.run();
}
}).start();
}
/**
* 等比例压缩图片至合适大小,width=0表示加载原图
*/
public static Bitmap compressBitmap(String filePath, int width, int height){
Bitmap bitmap;
BitmapFactory.Options options = new BitmapFactory.Options();
//返回原图
if (width ==0 || height ==0){
//do nothing
}else{
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
int widthScale = (int)((float) options.outWidth / (float) width);
int heightScale = (int)((float) options.outHeight / (float) height);
//选择缩放比例较大的那个
int scale = (widthScale > heightScale ? widthScale : heightScale);
if (scale < 1)
scale = 1;
options.inSampleSize = scale;
}
options.inJustDecodeBounds = false;
try {
bitmap = BitmapFactory.decodeFile(filePath, options);
}catch (OutOfMemoryError e){
System.gc();
try {
bitmap = BitmapFactory.decodeFile(filePath, options);
}catch (OutOfMemoryError ee){
L.e("ImageUtils compressBitmap error", ee);
return null;
}
}
return bitmap;
}
/**
* 不等比例缩放bitmap至指定长宽
*/
public static Bitmap resizeBitmap(Bitmap srcBitmap, int w, int h) {
if (srcBitmap == null) {
return null;
}
int width = srcBitmap.getWidth();
int height = srcBitmap.getHeight();
float scaleWidth = ((float) w) / width;
float scaleHeight = ((float) h) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
return Bitmap.createBitmap(srcBitmap, 0, 0, width, height, matrix, true);
}
/**
* 获取图片的旋转角度
*/
public static int getPictureDegree(String path) {
int degree = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
default:
degree = 0;
}
} catch (IOException e) {
e.printStackTrace();
}
return degree;
}
/**
* 旋转图片
* @param bitmap 原始图片
* @param degrees 原始图片的角度
*/
public static Bitmap rotateBitmap(Bitmap bitmap, int degrees){
if (degrees == 0 || null == bitmap) {
return bitmap;
}
Matrix matrix = new Matrix();
matrix.setRotate(degrees, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
Bitmap bmp = null;
try {
bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}catch (OutOfMemoryError error){
L.e("ImageUtils rotateBitmap error", error);
}
return bmp;
}
/**
* 检测一个文件是否为图片
* @param filePath 该文件路径
*/
public static boolean isFileImage(String filePath){
return filePath.endsWith(".png") || filePath.endsWith(".PNG")
|| filePath.endsWith(".jpg") || filePath.endsWith(".JPG")
|| filePath.endsWith(".jpeg") || filePath.endsWith(".JPEG")
|| filePath.endsWith(".bmp") || filePath.endsWith(".BMP");
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/volley/BaseVolleyApi.java
================================================
package com.android.libcore.volley;
import com.android.libcore.application.RootApplication;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
/**
* Description: 封装的volley的请求
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-09-16
*/
public abstract class BaseVolleyApi {
private static RequestQueue requestQueue;
private static ImageLoader imageLoader;
public static RequestQueue getRequestQueue() {
if (requestQueue == null) {
synchronized (BaseVolleyApi.class){
if (requestQueue == null)
requestQueue = Volley.newRequestQueue(RootApplication.getInstance());
}
}
return requestQueue;
}
public static ImageLoader getImageLoader() {
if (imageLoader == null) {
synchronized (BaseVolleyApi.class) {
if (imageLoader == null){
VolleyLruCache cache = new VolleyLruCache();
imageLoader = new ImageLoader(getRequestQueue(), cache);
}
}
}
return imageLoader;
}
}
================================================
FILE: libcore/src/main/java/com/android/libcore/volley/VolleyLruCache.java
================================================
package com.android.libcore.volley;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import com.android.volley.toolbox.ImageLoader;
/**
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-10-20
*/
public class VolleyLruCache extends LruCache implements ImageLoader.ImageCache{
private static int getCacheSize(){
return (int)(Runtime.getRuntime().maxMemory()/1024/8);
}
public VolleyLruCache() {
this(getCacheSize());
}
private VolleyLruCache(int size){
super(size);
}
@Override
public Bitmap getBitmap(String url) {
return get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}
}
================================================
FILE: libcore/src/main/res/values/strings.xml
================================================
================================================
FILE: libcore-ui/.gitignore
================================================
/build
================================================
FILE: libcore-ui/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 25
buildToolsVersion '25.0.0'
defaultConfig {
minSdkVersion 11
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.1'
compile project(':libcore')
}
================================================
FILE: libcore-ui/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/zzp/adt-bundle-linux-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: libcore-ui/src/main/AndroidManifest.xml
================================================
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/activity/BaseActivity.java
================================================
package com.android.libcore_ui.activity;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.libcore.activity.RootActivity;
import com.android.libcore.log.L;
import com.android.libcore.utils.CommonUtils;
import com.android.libcore_ui.R;
/**
* Description: 继承自{@link RootActivity}的基础activity,在这里进行页面界面的统一
*
* 使用{@link #setContentView(int)},{@link #setContentView(View)}和
* {@link #setContentView(View, ViewGroup.LayoutParams)}来设置内容区域布局
*
* 使用{@link #setOriginalContentView(int)},{@link #setOriginalContentView(View)}和
* {@link #setOriginalContentView(View, ViewGroup.LayoutParams)}来设置整体布局
*
* 应用整体样式现在有status bar透明和底部navigation bar透明两种样式
* 应用可以使用默认action bar样式,自定义top bar样式有ViewGroup和toolbar两种样式
*
*
*
* {@linkplain #mReceiver}用来在组件之间进行广播的接收
* {@linkplain #setTitle(String)}用来设置页面标题
* {@linkplain #addOptionsMenu(View)}用来在自定义top bar的情况下在右侧添加一个按钮
* {@linkplain #onCreateOptionsMenu(Menu)}用来在使用toolbar的情况下操作toolbar
* {@linkplain #addNavigationOnBottom(ViewGroup)}将一个和NavigationBar的高度一样的空白的view添加到viewGroup中
* {@linkplain #onHandleMessageFromFragment(Message)}用来处理fragment传递过来的消息
*
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-08
*/
public abstract class BaseActivity extends RootActivity{
/** 填充19版本以上SDK status bar */
protected View v_status_bar;
/** 头部top bar容器 */
protected FrameLayout fl_top_bar;
/** 头部top bar */
protected View top_bar;
/** 默认action bar */
protected ActionBar actionBar;
/** 内容区域 */
protected FrameLayout base_content;
/** 底部navigation是否透明,如果使用的是Activity_translucent_navigation_bar style,
* 变量则会变成true,调用addBlankOnBottom(View view)函数可以将一个空白的view添加到
* 底部用以填充透明的navigation bar,防止覆盖内容区域 */
protected boolean isNavigationTransparent;
private int mThemeID = 0;
/**应用如果使用NoActionBar主题,则默认为使用toolbar,反之则使用系统actionbar */
protected boolean useToolbar = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
mThemeID = getApplicationInfo().theme;
//如果系统的主题为Activity_translucent_navigation_bar,但是手机没有navigation bar,
// 则将其设置回status bar主题,setTheme()调用一定要在onCreate()之前
if (!CommonUtils.hasNavigationBar()
&& mThemeID==R.style.Activity_translucent_navigation_bar) {
setTheme(R.style.Activity_translucent_status_bar);
mThemeID = R.style.Activity_translucent_status_bar;
}
super.onCreate(savedInstanceState);
initLayout();
}
/**
* 初始化布局
*/
protected void initLayout(){
setOriginalContentView(R.layout.activity_base_layout);
base_content = (FrameLayout) findViewById(R.id.base_content);
defineStyle();
//如果系统没有指定action bar,则选用自定义action bar
if (getSupportActionBar()==null)
chooseTopBar();
else{
useToolbar = false;
actionBar = getSupportActionBar();
}
}
/**
* 定义应用的基本样式
*/
protected void defineStyle(){
//SDK19版本以上才支持样式选择
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
(mThemeID==R.style.Activity_translucent_status_bar ||
mThemeID==R.style.Activity_translucent_navigation_bar)){
v_status_bar = findViewById(R.id.v_status_bar);
int id = getResources().getIdentifier("status_bar_height", "dimen", "android");
v_status_bar.getLayoutParams().height = getResources().getDimensionPixelOffset(id);
//21版本以下使用windowTranslucentStatus,使用自定义view填充status bar
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
v_status_bar.setBackgroundColor(ContextCompat.getColor(this, R.color.bar_color));
v_status_bar.setVisibility(View.VISIBLE);
}
//21版本和以上,特殊处理
else{
if (mThemeID==R.style.Activity_translucent_navigation_bar){
v_status_bar.setBackgroundColor(ContextCompat.getColor(this, R.color.bar_color));
v_status_bar.setVisibility(View.VISIBLE);
}
}
if (CommonUtils.hasNavigationBar()) {
isNavigationTransparent = (mThemeID == R.style.Activity_translucent_navigation_bar);
}
}
}
/**
* 选择应用top bar方式
*/
protected void chooseTopBar(){
fl_top_bar = (FrameLayout) findViewById(R.id.fl_top_bar);
//添加top bar,现在有两种样式的top bar可以使用:一种是自定的viewGroup
if (!useToolbar) {
top_bar = View.inflate(this, R.layout.activity_top_bar_layout, null);
View rl_back = top_bar.findViewById(R.id.rl_back);
rl_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
TextView tv_title = (TextView) top_bar.findViewById(R.id.tv_title);
// 通过 android::label 设置的标题
if (!TextUtils.isEmpty(getTitle()))
tv_title.setText(getTitle());
}
//一种是使用系统控件toolbar
else{
top_bar = View.inflate(this, R.layout.activity_top_toolbar_layout, null);
Toolbar toolbar = (Toolbar) top_bar;
setSupportActionBar(toolbar);
toolbar.setTitleTextAppearance(this, R.style.toolbar_title_appearance);
toolbar.setTitleTextColor(ContextCompat.getColor(this, R.color.white));
toolbar.setNavigationIcon(R.mipmap.ic_arrow_back);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
// 通过 android::label 设置的标题
if (!TextUtils.isEmpty(getTitle()))
toolbar.setTitle(getTitle());
}
fl_top_bar.addView(top_bar);
}
/**
* 设置内容区域布局
*/
@Override
public void setContentView(int layoutResID) {
LayoutInflater inflater = LayoutInflater.from(this);
View v = inflater.inflate(layoutResID, null);
base_content.addView(v);
}
/**
* 省去类型转换
*/
protected T $(int id){
return (T) super.findViewById(id);
}
/**
* 设置内容区域布局
*/
@Override
public void setContentView(View view) {
base_content.addView(view);
}
/**
* 设置内容区域布局
*/
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
base_content.addView(view, params);
}
/**
* 设置整体布局
*/
protected void setOriginalContentView(int layoutResID){
super.setContentView(layoutResID);
}
/**
* 设置整体布局
*/
protected void setOriginalContentView(View view){
super.setContentView(view);
}
/**
* 设置整体布局
*/
protected void setOriginalContentView(View view, ViewGroup.LayoutParams params){
super.setContentView(view, params);
}
/**
* 设置标题
*/
protected void setTitle(String title){
if (!useToolbar) {
if (actionBar == null)
((TextView) top_bar.findViewById(R.id.tv_title)).setText(title);
else
actionBar.setTitle(title);
}else{
((Toolbar) top_bar).setTitle(title);
}
}
/**
* 将view添加进top bar右侧的相关区域中,适用于不使用toolbar样式
*/
protected void addOptionsMenu(View view){
if (useToolbar || actionBar!=null){
L.e("该样式无法使用addOptionsMenuView,请使用onCreateOptionsMenu");
}else{
((ViewGroup) top_bar.findViewById(R.id.rl_top_extra_content)).addView(view);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (useToolbar || actionBar!=null) {
return super.onCreateOptionsMenu(menu);
}else{
L.e("该样式无法使用onCreateOptionsMenu,请使用addOptionsMenu");
return false;
}
}
/**
* 将一个空白和navigation bar高度一致的view添加进传入的参数view中,保证传进来的view要在正确的位置
*/
public void addNavigationOnBottom(ViewGroup view){
if (CommonUtils.hasNavigationBar() && isNavigationTransparent) {
int id = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
View navigationView = new View(this);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, getResources().getDimensionPixelOffset(id));
navigationView.setLayoutParams(params);
navigationView.setBackgroundColor(ContextCompat.getColor(this, R.color.transparent));
view.addView(navigationView);
}
}
/**
* 是否使用toolbar样式
*/
public boolean isUseToolbar() {
return useToolbar;
}
/**
* 处理来自fragment的消息
*/
protected void onHandleMessageFromFragment(Message msg){}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/activity/BaseActivityWithPopWindow.java
================================================
package com.android.libcore_ui.activity;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import com.android.libcore.utils.CommonUtils;
import com.android.libcore_ui.R;
import com.android.libcore_ui.activity.widget.BottomBarGroupLinearLayout;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Description: 自带底部弹出框的{@link BaseActivity}
*
* 自定义底部弹出框规则:
*
* {@link #addItemToBottomPopWindow(int, int, String)}方法用来在底部弹出的框内加上选项,用组id,
* 元素id,和元素名称来标识,显示的顺序和添加的顺序一致,如果需要在中间插入一个组元素,则初始化添加的时候调用
* {@link #addItemToBottomPopWindow(int, int, String)}函数的时候,groupId传一个新值,itemId传递一个小于0
* 的数值即可,先占一个空位,方便以后来对该groupId位置的元素进行操作
* {@link #removeItemFromBottomPopWindow(int, int)}方法用来删除在底部添加的按钮选项
* {@link #showBottomPopWindow()}方法用来显示底部popwindow,调用之前确保已经调用
* {@link #addItemToBottomPopWindow(int, int, String)}方法
* {@link #onItemClickCallback(int, int)}方法由子类继承用来处理底部弹出框的点击回调
*
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-11-05
*/
public class BaseActivityWithPopWindow extends BaseActivity{
/** 全屏的半透明显示 */
protected View ll_full_screen;
/** 底部popWindow */
protected ScrollView sv_bottom_content;
protected LinearLayout ll_bottom_content;
/** 填充19版本以上SDK navigation bar */
protected View v_navigation_bar;
/** 底部弹出框数据集合 */
protected LinkedHashMap> bottomItems;
protected LayoutInflater inflater;
protected ObjectAnimator popAnimation;
protected ObjectAnimator reverseAnimation;
/** 底部弹出框的默认高度 */
protected int scrollViewMeasureHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void initLayout() {
setOriginalContentView(R.layout.activity_base_layout_with_popwindow);
base_content = (FrameLayout) findViewById(R.id.base_content);
bottomItems = new LinkedHashMap<>();
inflater = LayoutInflater.from(this);
sv_bottom_content = (ScrollView) findViewById(R.id.sv_bottom_content);
ll_bottom_content = (LinearLayout) findViewById(R.id.ll_bottom_content);
ll_full_screen = findViewById(R.id.ll_full_screen);
ll_full_screen.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doReverseAnimation();
}
});
defineStyle();
//如果系统没有指定action bar,则选用自定义action bar
if (getSupportActionBar()==null)
chooseTopBar();
else{
useToolbar = false;
actionBar = getSupportActionBar();
}
}
@Override
protected void defineStyle() {
super.defineStyle();
//SDK19版本以上并且导航栏透明
if (Build.VERSION.SDK_INT >= 19 &&
(getApplicationInfo().theme==R.style.Activity_translucent_status_bar ||
getApplicationInfo().theme==R.style.Activity_translucent_navigation_bar)
&& isNavigationTransparent){
v_navigation_bar = findViewById(R.id.v_navigation_bar);
v_navigation_bar.setVisibility(View.VISIBLE);
int id = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
v_navigation_bar.getLayoutParams().height = getResources().getDimensionPixelOffset(id);
}
}
/**
* 通过添加item到底部bar来创建一系列的选项
* @param groupId 该item的组id,不同的组id在不同的区域内,请使用大于0的数字来表示
* @param itemId 该item的item id,用来标示该item,组内的两个item不能有相同的item id,要不然回调无法识别
* @param name 用来显示该item的名字
*/
protected void addItemToBottomPopWindow(int groupId, int itemId, String name){
ArrayList temp;
if (bottomItems.containsKey(groupId)) {
if (itemId < 0){
throw new IllegalArgumentException("groupId can be found,so itemId must bigger than 0 or equal 0");
}
temp = bottomItems.get(groupId);
ItemHolder holder = new ItemHolder();
holder.itemId = itemId;
holder.name = name;
temp.add(holder);
}
else {
temp = new ArrayList<>();
if (itemId >= 0) {
ItemHolder holder = new ItemHolder();
holder.itemId = itemId;
holder.name = name;
temp.add(holder);
}
bottomItems.put(groupId, temp);
}
buildBottomPopWindow();
}
/**
* 将item从底部bar中删除
*/
protected void removeItemFromBottomPopWindow(int groupId, int itemId){
if (bottomItems.containsKey(groupId)){
ArrayList temp = bottomItems.get(groupId);
for (ItemHolder holder : temp){
if (holder.itemId == itemId){
temp.remove(holder);
buildBottomPopWindow();
return;
}
}
throw new IllegalArgumentException("can't find this itemId in this groupId");
}else{
throw new IllegalArgumentException("can't find this groupId");
}
}
/**
* 通过{@link #bottomItems}建立底部弹出框
*/
private void buildBottomPopWindow(){
if (bottomItems.size() <= 0)
return;
//现将底部弹出框的所有选项去除
ll_bottom_content.removeAllViews();
popAnimation = null;
reverseAnimation = null;
final Iterator iterator = bottomItems.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry> entry = (Map.Entry>) iterator.next();
Integer groupId = entry.getKey();
ArrayList holder = entry.getValue();
//如果该groupId的items不为0,代表需要将该group显示出来
if (holder.size() >= 0){
BottomBarGroupLinearLayout group = (BottomBarGroupLinearLayout) inflater.inflate(R.layout.bottom_group_layout, null);
group.setmGroupId(groupId);
group.setCallback(new BottomBarGroupLinearLayout.GroupItemClickCallback() {
@Override
public void callback(int groupId, int itemId) {
onItemClickCallback(groupId, itemId);
}
});
for (ItemHolder temp : holder){
group.addItemToGroup(temp.itemId, temp.name);
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
int margin = CommonUtils.dp2px(10);
params.setMargins(margin, 0, margin, margin);
ll_bottom_content.addView(group, params);
}
}
//每次组建完底部弹出框之后,就开始计算他的高度
int width = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
sv_bottom_content.measure(width, width);
scrollViewMeasureHeight = sv_bottom_content.getMeasuredHeight();
}
@Override
public void onBackPressed() {
if (sv_bottom_content.getVisibility() == View.VISIBLE){
doReverseAnimation();
}else {
super.onBackPressed();
}
}
/**
* 点击底部弹出框的回调
*/
protected void onItemClickCallback(int groupId, int itemId){
doReverseAnimation();
}
/**
* 执行反向动画将其隐藏
*/
private void doReverseAnimation(){
if (Build.VERSION.SDK_INT < 11) {
sv_bottom_content.setVisibility(View.GONE);
ll_full_screen.setVisibility(View.GONE);
}else{
//如果弹出动画还在执行,则直接将弹出动画的值置为最终值,代表该动画结束,接着直接进行收进动画
popAnimation.end();
//避免用户连续快速点击造成短时间内执行两次收进动画,此处进行判断
if (reverseAnimation != null && reverseAnimation.isRunning()){
return;
}
if (reverseAnimation == null) {
reverseAnimation = ObjectAnimator.ofInt(sv_bottom_content, "bottomMargin", 0, -scrollViewMeasureHeight);
reverseAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (Integer) animation.getAnimatedValue();
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) sv_bottom_content.getLayoutParams();
params.bottomMargin = value;
sv_bottom_content.setLayoutParams(params);
((View) (sv_bottom_content.getParent())).invalidate();
if (value <= -scrollViewMeasureHeight){
sv_bottom_content.setVisibility(View.GONE);
}
ll_full_screen.setAlpha((float) (((scrollViewMeasureHeight + value) * 1.0) / (scrollViewMeasureHeight * 1.0)));
if (ll_full_screen.getAlpha()<=0){
ll_full_screen.setVisibility(View.GONE);
}
}
});
reverseAnimation.setDuration(500);
}
reverseAnimation.start();
}
}
/**
* 用来显示该popwindow,保证在调用该方法之前已经调用{@link #addItemToBottomPopWindow(int, int, String)}方法
*/
protected void showBottomPopWindow(){
if (Build.VERSION.SDK_INT >= 11) {
//如果上次的动画还在执行,直接停止
if (reverseAnimation != null){
reverseAnimation.end();
}
sv_bottom_content.setVisibility(View.VISIBLE);
ll_full_screen.setVisibility(View.VISIBLE);
//需要滚动到顶部
sv_bottom_content.scrollTo(0, 0);
if (popAnimation == null) {
popAnimation = ObjectAnimator.ofInt(sv_bottom_content, "bottomMargin", -scrollViewMeasureHeight, 0);
popAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (Integer) animation.getAnimatedValue();
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) sv_bottom_content.getLayoutParams();
params.bottomMargin = value;
sv_bottom_content.setLayoutParams(params);
((View) (sv_bottom_content.getParent())).invalidate();
ll_full_screen.setAlpha((float) (((scrollViewMeasureHeight + value) * 1.0) / (scrollViewMeasureHeight * 1.0)));
}
});
popAnimation.setDuration(500);
}
popAnimation.start();
}else{
ll_full_screen.setVisibility(View.VISIBLE);
sv_bottom_content.setVisibility(View.VISIBLE);
//需要滚动到顶部
sv_bottom_content.scrollTo(0, 0);
}
}
/**
* 底部item的数据集合
*/
private class ItemHolder{
private int itemId;
private String name;
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/activity/BaseFragment.java
================================================
package com.android.libcore_ui.activity;
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.libcore.activity.RootFragment;
/**
* Description: 基础fragment
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-27
*/
public abstract class BaseFragment extends RootFragment{
/** fragment所依附的activity */
protected BaseActivity activity;
/** 整个activity的头部bar,如果某些activity需要改变bar样式,修改该view的子view即可 */
public View top_bar;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof BaseActivity) {
this.activity = (BaseActivity) activity;
top_bar = ((BaseActivity)activity).top_bar;
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mViewContainer = setContentView(inflater, container);
initView();
initData();
return mViewContainer;
}
protected abstract View setContentView(LayoutInflater inflater, @Nullable ViewGroup container);
protected abstract void initView();
protected abstract void initData();
/**
* 根据id值获取view
*/
protected View findViewById(int id){
return mViewContainer.findViewById(id);
}
/**
* 设置该activity页面的标题
*/
protected void setTitle(String title){
activity.setTitle(title);
}
/**
* 用来fragment和activity之间的通信
* @param msg fragment发送给activity的消息
*/
protected void sendMessageToActivity(Message msg){
//通过intent传递的数据量最好不要超过1M,而使用msg.obg变量则可以使用堆中的剩余存储
activity.onHandleMessageFromFragment(msg);
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/activity/widget/BottomBarGroupLinearLayout.java
================================================
package com.android.libcore_ui.activity.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.libcore_ui.R;
/**
* Description: 专用控件,底部弹出框的容器,该控件不是常用控件
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-14
*/
public class BottomBarGroupLinearLayout extends LinearLayout{
private int mGroupId;
private boolean mHasSetGroupId = false;
private LayoutInflater mInflater;
private GroupItemClickCallback mCallback;
public BottomBarGroupLinearLayout(Context context) {
super(context);
}
public BottomBarGroupLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
/** 设置该项的groupId */
public void setmGroupId(int mGroupId){
this.mGroupId = mGroupId;
mHasSetGroupId = true;
mInflater = LayoutInflater.from(getContext());
}
public void addItemToGroup(int itemId, String name){
if (!mHasSetGroupId){
throw new IllegalArgumentException("set mGroupId first");
}
View view = mInflater.inflate(R.layout.bottom_item_layout, null);
TextView tv_item_name = (TextView) view.findViewById(R.id.tv_item_name);
tv_item_name.setText(name);
//设置textview的弧角
if (getChildCount() == 0){
tv_item_name.setBackgroundResource(R.drawable.bottom_button_all_selector);
}else{
tv_item_name.setBackgroundResource(R.drawable.bottom_button_bottom_selector);
if (getChildCount() == 1){
getChildAt(getChildCount() - 1).findViewById(R.id.tv_item_name).setBackgroundResource(R.drawable.bottom_button_top_selector);
}
else{
getChildAt(getChildCount() - 1).findViewById(R.id.tv_item_name).setBackgroundResource(R.drawable.bottom_button_middle_selector);
}
}
view.setTag(itemId);
this.addView(view);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallback.callback(mGroupId, (Integer) v.getTag());
}
});
//将最后一个view的底部分割线去除
for (int i=0; i 1){
View v = ll_bottom_button.getChildAt(ll_bottom_button.getChildCount() - 1);
v = v.findViewById(R.id.tv_text);
if (v != null)
v.setBackgroundResource(R.drawable.dialog_button_middle_selector);
}
}
@Override
public BaseDialog setPositiveButton(String positive) {
LinearLayout layout = generateLayout(positive);
layout.setTag(POSITIVE_LISTENER);
ll_bottom_button.addView(layout);
return this;
}
@Override
public BaseDialog setPositiveButton(View positive) {
View layout = positive;
//超过一个view,应该加上一条分割线
if (ll_bottom_button.getChildCount() > 0){
layout = inflater.inflate(R.layout.dialog_item_button_layout, null);
((ViewGroup)layout).removeViewAt(1);
((ViewGroup)layout).addView(positive);
}
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.weight = 1;
layout.setTag(POSITIVE_LISTENER);
layout.setOnClickListener(this);
ll_bottom_button.addView(layout, params);
return this;
}
@Override
public BaseDialog setNegativeButton(String negative) {
LinearLayout layout = generateLayout(negative);
layout.setTag(NEGATIVE_LISTENER);
ll_bottom_button.addView(layout);
return this;
}
@Override
public BaseDialog setNegativeButton(View negative) {
View layout = negative;
//超过一个view,应该加上一条分割线
if (ll_bottom_button.getChildCount() > 0){
layout = inflater.inflate(R.layout.dialog_item_button_layout, null);
((ViewGroup)layout).removeViewAt(1);
((ViewGroup)layout).addView(negative);
}
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.weight = 1;
layout.setTag(NEGATIVE_LISTENER);
layout.setOnClickListener(this);
ll_bottom_button.addView(layout, params);
return this;
}
@Override
public BaseDialog setNeutralButton(String neutral) {
LinearLayout layout = generateLayout(neutral);
layout.setTag(NEUTRAL_LISTENER);
ll_bottom_button.addView(layout);
return this;
}
@Override
public BaseDialog setNeutralButton(View neutral) {
View layout = neutral;
//超过一个view,应该加上一条分割线
if (ll_bottom_button.getChildCount() > 0){
layout = inflater.inflate(R.layout.dialog_item_button_layout, null);
((ViewGroup)layout).removeViewAt(1);
((ViewGroup)layout).addView(neutral);
}
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.weight = 1;
layout.setTag(NEUTRAL_LISTENER);
layout.setOnClickListener(this);
ll_bottom_button.addView(layout, params);
return this;
}
@Override
public BaseDialog addOtherButton(String other, int other_listener) {
if (!checkIllegalId(other_listener)){
throw new IllegalArgumentException("按钮id重复");
}
LinearLayout layout = generateLayout(other);
layout.setTag(other_listener);
mIds.add(other_listener);
ll_bottom_button.addView(layout);
return this;
}
@Override
public BaseDialog addOtherButton(View other, int other_listener) {
if (!checkIllegalId(other_listener)){
throw new IllegalArgumentException("按钮id重复");
}
reBuildCircle();
View layout = other;
layout = inflater.inflate(R.layout.dialog_item_button_layout, null);
//超过一个view,应该加上一条分割线
if (ll_bottom_button.getChildCount() == 0){
((ViewGroup)layout).removeViewAt(0);
}
((ViewGroup)layout).removeViewAt(1);
((ViewGroup)layout).addView(other, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
LinearLayout.LayoutParams params =
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
params.weight = 1;
other.setTag(other_listener);
other.setOnClickListener(this);
layout.setTag(other_listener);
layout.setOnClickListener(this);
ll_bottom_button.addView(layout, params);
return this;
}
@Override
public BaseDialog setGravity(int gravity) {
Window window = this.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.gravity = gravity;
window.setAttributes(params);
return this;
}
@Override
public BaseDialog setPosition(int x, int y) {
Window window = this.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
window.setAttributes(params);
return this;
}
@Override
public BaseDialog setWidth(int width) {
Window window = this.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.width = width;
window.setAttributes(params);
return this;
}
@Override
public BaseDialog setHeight(int height) {
Window window = this.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.height = height;
window.setAttributes(params);
return this;
}
@Override
public BaseDialog setAlpha(float alpha) {
Window window = this.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.alpha = alpha;
window.setAttributes(params);
return null;
}
@Override
public void show() {
if (!mHasTitle)
rl_title.setVisibility(View.GONE);
super.show();
}
@Override
public void onClick(View v) {
this.dismiss();
mListener.onButtonClick((Integer) v.getTag());
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/dialog/DialogCreator.java
================================================
package com.android.libcore_ui.dialog;
import android.view.View;
import com.android.libcore_ui.application.BaseApplication;
import java.util.ArrayList;
/**
* Description: dialog的生成类,用来获取所需要基本常用的dialog
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-16
*/
public class DialogCreator {
/**
* 创建dialog函数
* @param title 只能为string和view
* @param message 只能为string和view
* @param positive 只能为string和view
*/
public static AppDialog createDialog(Object title, Object message, Object positive){
return createDialog(title, message, positive, null);
}
/**
* 创建dialog函数
* @param title 只能为string和view
* @param message 只能为string和view
* @param positive 只能为string和view
* @param negative 只能为string和view
*/
public static AppDialog createDialog(Object title, Object message, Object positive, Object negative){
return createDialog(title, message, positive, negative, null);
}
/**
* 创建dialog函数
* @param title 只能为string和view
* @param message 只能为string和view
* @param positive 只能为string和view
* @param negative 只能为string和view
* @param neutral 只能为string和view
*/
public static AppDialog createDialog(Object title, Object message, Object positive, Object negative, Object neutral){
return createDialog(title, message, positive, negative, neutral, null);
}
/**
* 创建dialog函数
* @param title 只能为string和view
* @param message 只能为string和view
* @param positive 只能为string和view
* @param negative 只能为string和view
* @param neutral 只能为string和view
* @param others 需要另外加上按钮的数据集合
*/
public static AppDialog createDialog(Object title, Object message, Object positive, Object negative, Object neutral,
ArrayList others){
AppDialog dialog = new AppDialog(BaseApplication.getInstance());
if (title != null) {
if (title instanceof String) {
dialog.setTitle((String) title);
} else if (title instanceof View) {
dialog.setTitle((View) title);
} else {
throw new IllegalArgumentException("title 只能为string和view");
}
}
if (message != null) {
if (message instanceof String) {
dialog.setMessage((String) message);
} else if (message instanceof View) {
dialog.setMessage((View) message);
} else {
throw new IllegalArgumentException("message 只能为string和view");
}
}
if(positive != null) {
if (positive instanceof String) {
dialog.setPositiveButton((String) positive);
} else if (positive instanceof View) {
dialog.setPositiveButton((View) positive);
} else {
throw new IllegalArgumentException("positive 只能为string和view");
}
}
if (negative != null) {
if (negative instanceof String) {
dialog.setNegativeButton((String) negative);
} else if (negative instanceof View) {
dialog.setNegativeButton((View) negative);
} else {
throw new IllegalArgumentException("negative 只能为string和view");
}
}
if (neutral != null) {
if (neutral instanceof String) {
dialog.setNeutralButton((String) neutral);
} else if (neutral instanceof View) {
dialog.setNeutralButton((View) neutral);
} else {
throw new IllegalArgumentException("neutral 只能为string和view");
}
}
if (others!=null && others.size() > 0){
for (OtherButton temp : others){
if (temp.other instanceof String){
dialog.addOtherButton((String)temp.other, temp.id);
}else{
dialog.addOtherButton((View)temp.other, temp.id);
}
}
}
return dialog;
}
/**
* 另外需要添加按钮的数据集合
*/
public static class OtherButton{
Object other;
Integer id;
/** 该类只提供该两种构造函数 */
public OtherButton(String text, Integer id){
other = text;
this.id = id;
}
public OtherButton(View view, Integer id){
other = view;
this.id = id;
}
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/dialog/LoadingDialog.java
================================================
package com.android.libcore_ui.dialog;
import android.app.Dialog;
import android.content.Context;
import android.widget.TextView;
import com.android.libcore_ui.R;
/**
* Description: 默认应用加载框,可自定义
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-11-03
*/
public class LoadingDialog extends Dialog{
private TextView tv_loading_text;
public LoadingDialog(Context context) {
this(context, 0);
}
public LoadingDialog(Context context, int themeResId) {
super(context, R.style.theme_dialog);
setContentView(R.layout.loading_dialog_layout);
tv_loading_text = (TextView) findViewById(R.id.tv_loading_text);
}
public void setLoadingText(String text){
tv_loading_text.setText(text);
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/net/NetApi.java
================================================
package com.android.libcore_ui.net;
import android.content.Context;
import com.android.libcore.net.netapi.BaseNetApi;
import com.android.libcore_ui.net.request.XMLRequest;
import org.xmlpull.v1.XmlPullParser;
import java.util.Map;
/**
* Description: 对基础netapi类的完善,可以在request包下添加自定义的request,并且
* 在这个类里添加相应的函数
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-09-15
*/
public class NetApi extends BaseNetApi{
private volatile static NetApi instance;
public static NetApi getInstance(){
if (instance == null){
synchronized (NetApi.class){
if (instance == null){
instance = new NetApi();
}
}
}
return instance;
}
public void xmlRequest(Context context, String url, Map params, OnNetCallback callback){
makeRequest(context, XMLRequest.class, url, params, callback);
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/net/request/XMLRequest.java
================================================
package com.android.libcore_ui.net.request;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* Description: xml请求
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-09-16
*/
public class XMLRequest extends Request{
private Response.Listener mListener;
private Map params;
public XMLRequest(int method, String url, Response.Listener listener,
Response.ErrorListener errorListener, Map params) {
super(method, url, errorListener);
mListener = listener;
this.params = params;
}
@Override
protected Map getParams() throws AuthFailureError {
return params;
}
@Override
protected void onFinish() {
super.onFinish();
mListener = null;
}
@Override
protected void deliverResponse(XmlPullParser response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String xmlString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlString));
return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (XmlPullParserException e) {
return Response.error(new ParseError(e));
}
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/permanentdbcache/PermanentCacheDB.java
================================================
package com.android.libcore_ui.permanentdbcache;
import android.database.sqlite.SQLiteDatabase;
import com.android.libcore.database.BaseDB;
import com.android.libcore.database.IBaseDBTable;
import com.android.libcore.log.L;
import java.util.ArrayList;
/**
* Description: 缓存{@link PermanentCacheDBHelper}的数据库表
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-20
*/
public class PermanentCacheDB extends BaseDB{
public PermanentCacheDB(IBaseDBTable table, boolean writable) {
super(table, writable);
}
@Override
protected String getDBName() {
return "permanentCache.db";
}
@Override
protected int getDBVersion() {
return 1;
}
@Override
protected void onDBCreate(SQLiteDatabase db) {
try {
db.beginTransaction();
String sql;
sql = "create table if not exists "+ TABLES.CACHE.getTableName() +"_" + getDBVersion() + " (";
sql += TABLES.CACHE.getTableColumns().get(0)+" varchar(40) not null primary key default '', ";
sql += TABLES.CACHE.getTableColumns().get(1)+" varchar(4000) not null default ''";
sql += ")";
db.execSQL(sql);
db.setTransactionSuccessful();
} catch (Exception e) {
L.e("sql语句错误", e);
} finally {
db.endTransaction();
}
}
@Override
protected void onDBUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (newVersion){
case 2:
break;
default:
break;
}
}
/**
* 将该数据库中所有的表使用枚举封装
*/
public enum TABLES implements IBaseDBTable {
CACHE("cache"){
@Override
public ArrayList getTableColumns() {
ArrayList columns = new ArrayList<>();
columns.add("key");//键
columns.add("value");//值
return columns;
}
};
private String table_name;
TABLES(String table_name){
this.table_name = table_name;
}
@Override
public String getTableName() {
return table_name;
}
@Override
public ArrayList getTableColumns() {
return null;
}
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/permanentdbcache/PermanentCacheDBHelper.java
================================================
package com.android.libcore_ui.permanentdbcache;
import com.android.libcore.database.BaseDBHelper;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Description: 用来封装{@link PermanentCacheDB}的相关存取操作,存储的值会永久 的
* 存储在数据库中,所以该db只能用来存放和应用周期相关的信息
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-20
*/
public class PermanentCacheDBHelper extends BaseDBHelper{
private volatile static PermanentCacheDBHelper instance;
public static PermanentCacheDBHelper getInstance(){
if (instance == null){
synchronized (PermanentCacheDBHelper.class){
if (instance == null){
instance = new PermanentCacheDBHelper();
}
}
}
return instance;
}
private PermanentCacheDBHelper(){
mTable = PermanentCacheDB.TABLES.CACHE;
}
/**
* 设置键值,操作少,所以在UI线程
* @return 删除成功返回true
*/
public boolean set(String key, String value){
ArrayList columns = mTable.getTableColumns();
HashMap map = new HashMap<>();
map.put(columns.get(0), key);
map.put(columns.get(1), value);
return insert(map, true) > 0;
}
/**
* 删除键,操作少,所以在UI线程
* @return 删除成功返回true
*/
public boolean del(String key){
String selection = mTable.getTableColumns().get(0)+"=?";
String[] selectionArgs = new String[]{key};
return delete(selection, selectionArgs) > 0;
}
/**
* 清空该表的所有数据
*/
public boolean clear(){
return delete("1=1", null) > 0;
}
/**
* 根据键获取值,操作少,所以在UI线程
* @return value
*/
public String get(String key){
String selection = mTable.getTableColumns().get(0)+"=?";
String[] selectionArgs = new String[]{key};
ArrayList> result = query(selection, selectionArgs, null, null, null, null);
if (result != null){
return result.get(0).get(mTable.getTableColumns().get(1));
}
return null;
}
@Override
protected void initInsertDB() {
mDb = new PermanentCacheDB(mTable, true);
}
@Override
protected void initDeleteDB() {
mDb = new PermanentCacheDB(mTable, true);
}
@Override
protected void initUpdateDB() {
mDb = null;
}
@Override
protected void initQueryDB() {
mDb = new PermanentCacheDB(mTable, false);
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/volley/VolleyApi.java
================================================
package com.android.libcore_ui.volley;
import com.android.libcore.volley.BaseVolleyApi;
/**
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-09-16
*/
public class VolleyApi extends BaseVolleyApi{
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/volley/request/XMLRequest.java
================================================
package com.android.libcore_ui.volley.request;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.toolbox.HttpHeaderParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
/**
* Description: xml请求
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-09-16
*/
public class XMLRequest extends Request{
private Response.Listener mListener;
public XMLRequest(int method, String url, Response.Listener listener,
Response.ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
@Override
protected void onFinish() {
super.onFinish();
mListener = null;
}
@Override
protected void deliverResponse(XmlPullParser response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String xmlString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
XmlPullParser xmlPullParser = factory.newPullParser();
xmlPullParser.setInput(new StringReader(xmlString));
return Response.success(xmlPullParser, HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (XmlPullParserException e) {
return Response.error(new ParseError(e));
}
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/web/WebFragment.java
================================================
package com.android.libcore_ui.web;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.TextView;
import com.android.libcore.Toast.T;
import com.android.libcore.dialog.BaseDialog;
import com.android.libcore_ui.R;
import com.android.libcore_ui.activity.BaseFragment;
import com.android.libcore_ui.dialog.DialogCreator;
import java.lang.reflect.Method;
/**
* Description: 最基本的webFragment,可以作为fragment嵌入activity的任何部分
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-08-03
*/
public class WebFragment extends BaseFragment{
public static final String EXTRA_URL = "extra_url";
protected WebView webView;
protected FrameworkWebViewClient webViewClient = new FrameworkWebViewClient();
protected FrameworkChromeClient chromeClient = new FrameworkChromeClient();
protected String url;
protected WebCallback callback;
@Override
protected View setContentView(LayoutInflater inflater, @Nullable ViewGroup container) {
return new WebView(activity);
}
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void initView() {
webView = (WebView) mViewContainer;
webView.setWebViewClient(webViewClient);
webView.setWebChromeClient(chromeClient);
WebSettings settings = webView.getSettings();
//设置网页大小自适应
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
//支持js
settings.setJavaScriptEnabled(true);
}
@Override
protected void initData() {
Bundle data = getArguments();
if (data != null)
url = data.getString(EXTRA_URL);
if (url != null)
webView.loadUrl(url);
}
public WebView getWebView(){
return webView;
}
/**
* 加载url
*/
public void loadUrl(String url){
webView.loadUrl(url);
}
/**
* 能否返回上一个页面
*/
public boolean canGoBack(){
return webView.canGoBack();
}
/**
* 返回上一个页面
*/
public void goBack(){
webView.goBack();
}
/**
* 刷新webview
*/
public void refresh(){
webView.reload();
}
/**
* 停止加载
*/
public void stopLoading(){
webView.stopLoading();
}
/** 是否是内部url,不需要跳转url */
protected boolean handleUrlBeforeLoad(String url){
return false;
}
protected void onPageStarted(WebView view, String url, Bitmap favicon){}
protected void onPageFinished(WebView view, String url){}
private class FrameworkWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!handleUrlBeforeLoad(url))
view.loadUrl(url);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
if (callback != null)
callback.onPageStarted(url, favicon);
WebFragment.this.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
if (callback != null)
callback.onPageFinished(url);
WebFragment.this.onPageFinished(view, url);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
T.getInstance().showShort("errorCode:" + errorCode + " description:" + description + " failingUrl:" + failingUrl);
super.onReceivedError(view, errorCode, description, failingUrl);
}
}
protected void onProgressChanged(WebView view, int newProgress){}
protected void onReceivedIcon(WebView view, Bitmap icon){}
protected void onReceivedTitle(WebView view, String title){}
private class FrameworkChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (callback != null)
callback.onProgressChanged(newProgress);
WebFragment.this.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
if (callback != null)
callback.onReceivedIcon(icon);
WebFragment.this.onReceivedIcon(view, icon);
}
@Override
public void onReceivedTitle(WebView view, String title) {
if (callback != null)
callback.onReceivedTitle(title);
WebFragment.this.onReceivedTitle(view, title);
}
//js警告框
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
T.getInstance().showShort(message);
result.confirm();
return true;
}
//确认框
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
DialogCreator.createDialog(null, message, getString(R.string.confirm), getString(R.string.cancel))
.setOnButtonClickListener(new BaseDialog.ButtonClickListener() {
@Override
public void onButtonClick(int button_id) {
if (button_id == 0){
result.confirm();
}else{
result.cancel();
}
}
})
.show();
return true;
}
//提示框
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
final View v = View.inflate(getActivity(), R.layout.dialog_js_prompt_message_layout, null);
((TextView)(v.findViewById(R.id.tv_message))).setText(message);
((EditText)(v.findViewById(R.id.et_content))).setText(defaultValue);
Dialog dialog = DialogCreator.createDialog(null, v, getString(R.string.confirm), getString(R.string.cancel))
.setOnButtonClickListener(new BaseDialog.ButtonClickListener() {
@Override
public void onButtonClick(int button_id) {
if (button_id == 0) {
result.confirm(((EditText) (v.findViewById(R.id.et_content))).getText().toString());
} else {
result.cancel();
}
}
});
dialog.setCanceledOnTouchOutside(false);
dialog.show();
return true;
}
}
@Override
public void onResume() {
super.onResume();
invokeMethod("onResume");
webView.resumeTimers();
}
@Override
public void onPause() {
super.onPause();
//当页面不可见时,停止内核所有的动作,省电,省流量等
invokeMethod("onPause");
//不仅仅针对当前的webview而是全局的全应用程序的webview,它会暂停所有webview的layout,parsing,javascript timer,降低CPU功耗。
webView.pauseTimers();
}
protected void invokeMethod(String method){
try {
Method m = WebView.class.getMethod(method);
m.setAccessible(true);
m.invoke(webView);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 设置回调
*/
public void setCallback(WebCallback callback) {
this.callback = callback;
}
/** web页的回调 */
public static class WebCallback{
public void onPageStarted(String url, Bitmap favicon){}
public void onPageFinished(String url){}
public void onProgressChanged(int progress){}
public void onReceivedIcon(Bitmap icon){}
public void onReceivedTitle(String title){}
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/web/webactivity/WebActivity.java
================================================
package com.android.libcore_ui.web.webactivity;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Message;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.MenuItemCompat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import com.android.libcore.utils.CommonUtils;
import com.android.libcore_ui.R;
import com.android.libcore_ui.activity.BaseActivity;
import com.android.libcore_ui.web.WebFragment;
import java.util.ArrayList;
/**
* Description: 应用基础的网页浏览activity
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-27
*/
public class WebActivity extends BaseActivity{
public static final String EXTRA_URL = "extra_url";
private WebActivityFragment webView;
private ProgressBar pb_bar;
private ImageView refresh;
private boolean isLoading = false;
/** 因为onReceivedTitle方法在goBack时不会调用,所以用一个list存储title */
private ArrayList titles = new ArrayList<>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
initData();
}
protected void initView() {
setContentView(R.layout.activity_web_layout);
webView = new WebActivityFragment();
pb_bar = (ProgressBar) findViewById(R.id.pb_bar);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.fl_content, webView);
ft.commit();
refresh = new ImageView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(CommonUtils.dp2px(30), CommonUtils.dp2px(30));
params.setMargins(0, 0, CommonUtils.dp2px(10), 0);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
refresh.setLayoutParams(params);
if (!isUseToolbar())
addOptionsMenu(refresh);
refresh.setBackgroundResource(R.mipmap.ic_refresh);
refresh.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isLoading)
webView.stopLoading();
else
webView.refresh();
}
});
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
((ViewGroup) findViewById(R.id.fl_bottom_blank)).removeAllViews();
}else{
addNavigationOnBottom((ViewGroup) findViewById(R.id.fl_bottom_blank));
}
}
protected void initData() {
Bundle bundle = new Bundle();
String url = getIntent().getStringExtra(EXTRA_URL);
bundle.putString(WebFragment.EXTRA_URL, url);
webView.setArguments(bundle);
addNavigationOnBottom((ViewGroup) findViewById(R.id.fl_bottom_blank));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
if (isUseToolbar()){
getMenuInflater().inflate(R.menu.menu_webactivity_refresh, menu);
MenuItem item = menu.findItem(R.id.menu_refresh);
View view = MenuItemCompat.getActionView(item);
((ViewGroup) view).addView(refresh);
return true;
}
else
return super.onCreateOptionsMenu(menu);
}
@Override
public void onBackPressed() {
if (webView.canGoBack())
webView.goBack();
else
super.onBackPressed();
}
@Override
protected void onHandleMessageFromFragment(Message msg) {
if (msg.what == 1){
isLoading = true;
refresh.setBackgroundResource(R.mipmap.ic_refresh_close);
}else if (msg.what == 2){
isLoading = false;
refresh.setBackgroundResource(R.mipmap.ic_refresh);
}else if (msg.what == 3){
int newProgress = (int) msg.obj;
if (newProgress == 100)
pb_bar.setVisibility(View.GONE);
else
pb_bar.setVisibility(View.VISIBLE);
pb_bar.setProgress(newProgress);
}else if (msg.what == 4){
Bitmap icon = (Bitmap) msg.obj;
BitmapDrawable drawable = new BitmapDrawable(getResources(), icon);
drawable.setBounds(0, 0, CommonUtils.dp2px(20), CommonUtils.dp2px(20));
// setTitle.setCompoundDrawables(drawable, null, null, null);
}else if (msg.what == 5){
String title = (String) msg.obj;
titles.add(" "+title);
setTitle(" " + title);
}
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/web/webactivity/WebActivityFragment.java
================================================
package com.android.libcore_ui.web.webactivity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Message;
import android.webkit.WebSettings;
import android.webkit.WebView;
import com.android.libcore_ui.R;
import com.android.libcore_ui.web.WebFragment;
import java.util.List;
/**
* Description: 用于在{@link WebActivity}中显示的webfragment
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-07-27
*/
public class WebActivityFragment extends WebFragment {
@Override
protected void initView() {
super.initView();
WebSettings settings = webView.getSettings();
//支持缩放
settings.setBuiltInZoomControls(true);
settings.setSupportZoom(true);
//隐藏缩放栏
if (Build.VERSION.SDK_INT >= 11)
settings.setDisplayZoomControls(false);
}
@Override
protected boolean handleUrlBeforeLoad(String url) {
try {
PackageManager pm = getActivity().getPackageManager();
// 以下固定写法
final Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
List infos = pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);
//如果除了浏览器之外还有应用能够打开该链接,则打开一个选择器
if (infos.size() >= 2) {
startActivity(intent);
}
} catch (Exception e) {
// 防止没有安装的情况
e.printStackTrace();
}
return false;
}
@Override
protected void onPageStarted(WebView view, String url, Bitmap favicon) {
Message msg = Message.obtain();
msg.what = 1;
sendMessageToActivity(msg);
}
@Override
protected void onPageFinished(WebView view, String url) {
Message msg = Message.obtain();
msg.what = 2;
sendMessageToActivity(msg);
}
@Override
protected void onProgressChanged(WebView view, int newProgress) {
Message msg = Message.obtain();
msg.what = 3;
msg.obj = newProgress;
sendMessageToActivity(msg);
}
@Override
protected void onReceivedIcon(WebView view, Bitmap icon) {
Message msg = Message.obtain();
msg.what = 4;
msg.obj = icon;
sendMessageToActivity(msg);
}
@Override
protected void onReceivedTitle(WebView view, String title) {
Message msg = Message.obtain();
msg.what = 5;
msg.obj = title;
sendMessageToActivity(msg);
}
}
================================================
FILE: libcore-ui/src/main/java/com/android/libcore_ui/widget/FlowLayout.java
================================================
package com.android.libcore_ui.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import com.android.libcore_ui.R;
/**
* Description: 流式布局
*
* @author zzp(zhao_zepeng@hotmail.com)
* @since 2015-11-14
*/
public class FlowLayout extends ViewGroup{
public static final int VERTICAL = 0;
public static final int HORIZONTAL = 1;
private final int CENTER = 1;
private final int TOP = 2;
private final int BOTTOM =3;
private final int LEFT = 4;
private final int RIGHT = 5;
//默认间隙
private int mVerticalSpacing = 10;
private int mHorizontalSpacing = 10;
//布局方向
private int orientation = HORIZONTAL;
//子view放置gravity
private int childGravity = 1;
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
getAttrValue(attrs);
}
private void getAttrValue(AttributeSet attrs){
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.FlowLayout);
mVerticalSpacing = typedArray.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 10);
mHorizontalSpacing = typedArray.getDimensionPixelSize(R.styleable.FlowLayout_horizontalSpacing, 10);
orientation = typedArray.getInt(R.styleable.FlowLayout_orientation, HORIZONTAL);
int gravity = typedArray.getInt(R.styleable.FlowLayout_childGravity, Gravity.TOP);
if (orientation == HORIZONTAL) {
gravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
if (gravity == Gravity.TOP)
childGravity = TOP;
else if (gravity == Gravity.BOTTOM)
childGravity = BOTTOM;
else
childGravity = CENTER;
}else{
gravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
if (gravity == Gravity.LEFT)
childGravity = LEFT;
else if (gravity == Gravity.RIGHT)
childGravity = RIGHT;
else
childGravity = CENTER;
}
typedArray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (getChildCount() <= 0){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int paddingTop = getPaddingTop();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingBottom = getPaddingBottom();
int width;
int height;
int childWidth;
int childHeight;
//该行最大子view大小
int maxChildSize = 0;
//剩余大小
int lastSize;
//水平布局,宽度固定,高度变化
if (orientation == HORIZONTAL) {
width = MeasureSpec.getSize(widthMeasureSpec);
height = 0;
lastSize = width - paddingLeft - paddingRight;
//如果第一个子view的大小已经超过容器大小
if (lastSize < getChildAt(0).getLayoutParams().width)
throw new ChildSizeTooLongException("the 0 child's width too long");
}
//垂直布局,高度固定,宽度变化
else{
width = 0;
height = MeasureSpec.getSize(heightMeasureSpec);
lastSize = height - paddingTop - paddingBottom;
//如果第一个子view的大小已经超过容器大小
if (lastSize < getChildAt(0).getLayoutParams().height)
throw new ChildSizeTooLongException("the 0 child's height too long");
}
//每行的第一个item的序号
int firstItemOfLine = 0;
//x,y坐标
int x = paddingLeft;
int y = paddingTop;
int childSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
LayoutParams lp = (LayoutParams) child.getLayoutParams();
childHeight = lp.height;
childWidth = lp.width;
if (childHeight <= 0 || childWidth <= 0) {
child.measure(childSpec, childSpec);
childWidth = child.getMeasuredWidth();
childHeight = child.getMeasuredHeight();
}
child.measure(MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
if (orientation == HORIZONTAL) {
lastSize = lastSize - childWidth - mHorizontalSpacing;
}else{
lastSize = lastSize - childHeight - mVerticalSpacing;
}
//需要换行
if (lastSize < 0) {
if (orientation == HORIZONTAL) {
//根据gravity将上一行的子view放置在正确的位置上
for (int j=firstItemOfLine; j childHeight ? maxChildSize : childHeight;
}else{
//计算出这一列子view中宽度最大的view
maxChildSize = maxChildSize > childWidth ? maxChildSize : childWidth;
}
}
lp.setXY(x, y);
if (orientation == HORIZONTAL) {
x += childWidth + mHorizontalSpacing;
}else{
y += childHeight + mVerticalSpacing;
}
}
if (orientation == HORIZONTAL) {
height += maxChildSize;
height += + paddingBottom + paddingTop;
//不要忘记最后一行
for (int i=firstItemOfLine; i
================================================
FILE: libcore-ui/src/main/res/drawable/bg_edittext.xml
================================================
================================================
FILE: libcore-ui/src/main/res/drawable/bg_edittext_focus.xml
================================================
================================================
FILE: libcore-ui/src/main/res/drawable/bg_edittext_not_focus.xml
================================================
================================================
FILE: libcore-ui/src/main/res/drawable/bg_loading_dialog.xml
================================================
================================================
FILE: libcore-ui/src/main/res/drawable/bg_progress_bar.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/bottom_button_all_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/bottom_button_bottom_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/bottom_button_middle_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/bottom_button_shape.xml
================================================
================================================
FILE: libcore-ui/src/main/res/drawable/bottom_button_top_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/dialog_button_bottom_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/dialog_button_bottomleft_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/dialog_button_bottomright_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/drawable/dialog_button_middle_selector.xml
================================================
-
-
================================================
FILE: libcore-ui/src/main/res/layout/activity_base_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/activity_base_layout_with_popwindow.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/activity_top_bar_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/activity_top_toolbar_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/activity_web_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/bottom_group_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/bottom_item_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/bottom_popwindow_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/dialog_base_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/dialog_item_button_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/dialog_js_prompt_message_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/loading_dialog_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/layout/menu_refresh_layout.xml
================================================
================================================
FILE: libcore-ui/src/main/res/menu/menu_webactivity_refresh.xml
================================================
================================================
FILE: libcore-ui/src/main/res/values/attrs.xml
================================================
================================================
FILE: libcore-ui/src/main/res/values/colors.xml
================================================
#00000000
#000000
#88000000
#ffffff
#88ffffff
#bbffffff
#ff2888f4
#ff33b5e5
#44000000
#353535
#808080
#d2d2d2
================================================
FILE: libcore-ui/src/main/res/values/strings.xml
================================================
正在加载
确定
取消
刷新
选择打开应用
================================================
FILE: libcore-ui/src/main/res/values/styles.xml
================================================
================================================
FILE: libcore-ui/src/main/res/values-v19/styles.xml
================================================
================================================
FILE: libcore-ui/src/main/res/values-v21/styles.xml
================================================
================================================
FILE: libcore-ui/src/main/res/values-v23/styles.xml
================================================
================================================
FILE: settings.gradle
================================================
include ':libcore', ':libcore-ui', ':testsample'
================================================
FILE: testsample/.gitignore
================================================
/build
================================================
FILE: testsample/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion '25.0.0'
defaultConfig {
applicationId "com.android.framework"
minSdkVersion 11
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.1'
compile project(':libcore-ui')
}
================================================
FILE: testsample/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/zzp/adt-bundle-linux-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
================================================
FILE: testsample/src/main/AndroidManifest.xml
================================================
================================================
FILE: testsample/src/main/assets/1.html
================================================
================================================
FILE: testsample/src/main/assets/baidu.html
================================================
百度一下,你就知道 <meta http-equiv=refresh content="0; url=http://www.baidu.com/baidu.html?from=noscript">
中 中 中
================================================
FILE: testsample/src/main/assets/baidu_files/activity_start_52498d2c.js
================================================
F.module("activity:skin/lottery",function(g,f,e){var d=g("superui:component/share"),c=g("superui:component/dialog"),a=g("superui:util/tool");StyleTMP=".activity-skin-lottery .sui-dialog-tipstext strong{color:#009944;padding:0 5px;}.activity-skin-lottery .sui-dialog-subcontent{padding-left:0px;padding-right:0px;text-justify:inter-ideograph;text-align:center}.activity-skin-lottery .sui-dialog-subcontent a{text-decoration: underline;}",URI="/home/skin/submit/activitylottery";var b={init:function(){if(s_session.userTips.isReqSkinLottery){this.lotreq()}a.addStyle(StyleTMP);this.initSkin()},lotreq:function(){$.ajaxpost(URI,{},function(h){if(h.bsLottery&&h.bsLottery!="-1"){b.showLottery(h)}})},showLottery:function(k){var i=this,m,j,h=k.bsShare;m=c.alert({width:350,identity:"skinLottery",type:k.bsLottery=="1"?"success":"warning",dialogClassName:"activity-skin-lottery",content:$.decodeHTML(k.bsTitle),subcontent:'"+$.decodeHTML(k.bsText)+"
",buttons:[{type:"ok",title:"立即分享",listener:function(){return false}}]});var l=new d({identity:"activityskin",target:j=m.footer.find(".sui-dialog-buttonok"),text:h.text,url:h.url,pic:h.img,arrowLeft:30,width:200,trigger:"mouseenter"});j.on("mousedown",function(){return false})},initSkin:function(){e.listen("superplus:skin/skin_control","skinhide",function(){if(b.isStar()){b.lotreq()}})},isStar:function(){var i=false,h=$(".s-skin-layer"),k=h.find(".choose-nav");var j=h.find(".choose-li");if(j.hasClass("nav-1009")){if(j.parent().find(".no-img").length<11){i=true}}return i}};f.init=function(){b.init()}});F.module("activity:activity/superplus",function(b,a){a.init=function(){F.call("activity:skin/lottery","init")}});F.call("activity:activity/superplus","init");
================================================
FILE: testsample/src/main/assets/baidu_files/all_async_search_57459356.js
================================================
var define;var require;var esl;(function(b){var o={};var C={};var x=1;var g=2;var s=3;var p=4;var ah=X();var N;function i(ap,aq){var an=[];function am(ar){if(ar.indexOf(".")===0){an.push(ar)}}if(typeof ap==="string"){am(ap)}else{aa(ap,function(ar){am(ar)})}if(an.length>0){throw new Error("[REQUIRE_FATAL]Relative ID is not allowed in global require: "+an.join(", "))}var ao=n.waitSeconds;if(ao&&(ap instanceof Array)){if(N){clearTimeout(N)}N=setTimeout(I,ao*1000)}return ah(ap,aq)}i.version="1.8.6";i.loader="esl";i.toUrl=ah.toUrl;function I(){var aq=[];var ar=[];var ao={};var ap={};var an={};function am(aw,av){if(an[aw]||l(aw,p)){return}an[aw]=1;if(!l(aw,s)){if(!ao[aw]){ao[aw]=1;aq.push(aw)}}var au=o[aw];if(!au){if(!ap[aw]){ap[aw]=1;ar.push(aw)}}else{if(av){if(!ao[aw]){ao[aw]=1;aq.push(aw)}aa(au.depMs,function(ax){am(ax.absId,ax.hard)})}}}for(var at in C){am(at,1)}if(aq.length||ar.length){throw new Error("[MODULE_TIMEOUT]Hang( "+(aq.join(", ")||"none")+" ) Miss( "+(ar.join(", ")||"none")+" )")
}}var z;function R(aq,ap,ao){if(ao==null){if(ap==null){ao=aq;aq=null}else{ao=ap;ap=null;if(aq instanceof Array){ap=aq;aq=null}}}if(ao==null){return}var am=window.opera;if(!aq&&document.attachEvent&&(!(am&&am.toString()==="[object Opera]"))){var an=t();aq=an&&an.getAttribute("data-require-id")}if(aq){y(aq,ap,ao);if(z){clearTimeout(z)}}else{r[0]={deps:ap,factory:ao}}}R.amd={};function ad(){var am=n.config[this.id];if(am&&typeof am==="object"){return am}return{}}function y(ao,an,am){if(!o[ao]){o[ao]={id:ao,depsDec:an,deps:an||["require","exports","module"],factoryDeps:[],factory:am,exports:{},config:ad,state:x,require:X(ao),depMs:[],depMkv:{},depRs:[],depPMs:[]}}}function ag(ar){var ao=o[ar];if(!ao||l(ar,g)){return}var aq=ao.deps;var an=ao.factory;var ap=0;if(typeof an==="function"){ap=Math.min(an.length,aq.length);!ao.depsDec&&an.toString().replace(/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,"").replace(/require\(\s*(['"'])([^'"]+)\1\s*\)/g,function(au,at,av){aq.push(av)})}var am=[];aa(aq,function(au,at){var ax=W(au);
var aw=af(ax.mod,ar);var ay;var av;if(aw&&!B[aw]){if(ax.res){av={id:au,mod:aw,res:ax.res};C[aw]=1;ao.depPMs.push(aw);ao.depRs.push(av)}ay=ao.depMkv[aw];if(!ay){ay={id:ax.mod,absId:aw,hard:at=am}function al(an){var am=o[an];if(am&&am.invokeFactory){am.invokeFactory()}}function ae(an,ao){var am=[];aa(an,function(aq,ap){am[ap]=ao[aq]||u(aq)});return am}var V={};function f(ao,an){if(l(ao,p)){an();return}var am=V[ao];if(!am){am=V[ao]=[]}am.push(an)}function D(ap){var ao=V[ap]||[];var an=o[ap];an.state=p;var am=ao.length;while(am--){ao[am]()}ao.length=0;delete V[ap]}function u(am){if(l(am,p)){return o[am].exports}return null}var B={require:i,exports:1,module:1};var r=[];function q(am){aa(r,function(an){y(am,an.deps,an.factory)});r.length=0;ag(am)}function a(ap,ar,am,an){if(typeof ap==="string"){al(ap);
if(!l(ap,p)){throw new Error('[MODULE_MISS]"'+ap+'" is not exists!')}return u(ap)}an=an||{};var aq=0;if(ap instanceof Array){ao();if(!aq){aa(ap,function(at){if(!(B[at]||l(at,p))){f(at,ao);if(!an[at]){(at.indexOf("!")>0?F:j)(at,am)}ag(at)}});S()}}function ao(){if(!aq){var at=1;aa(ap,function(au){if(!B[au]){return(at=!!l(au,p))}});if(at){aq=1;(typeof ar==="function")&&ar.apply(b,ae(ap,B))}}}}var P={};function j(an){if(P[an]||o[an]){return}P[an]=1;var am=document.createElement("script");am.setAttribute("data-require-id",an);am.src=K(an+".js");am.async=true;if(am.readyState){am.onreadystatechange=ao}else{am.onload=ao}ak(am);function ao(){var ap=am.readyState;if(typeof ap==="undefined"||/^(loaded|complete)$/.test(ap)){am.onload=am.onreadystatechange=null;am=null;q(an);for(var aq in C){ag(aq)}S()}}}function F(am,ao){if(o[am]){return}var ar=W(am);var aq={id:am,state:g};o[am]=aq;function an(at){aq.exports=at||true;D(am)}an.fromText=function(au,at){C[au]=1;new Function(at)();q(au)};function ap(at){var au=ao?o[ao].require:ah;
at.load(ar.res,au,an,ad.call({id:am}))}ap(u(ar.mod))}var n={baseUrl:"./",paths:{},config:{},map:{},packages:[],waitSeconds:0,noRequests:{},urlArgs:{}};i.config=function(an){function ap(ar){am.push(ar)}if(an){for(var ao in n){var aq=an[ao];var am=n[ao];if(aq){if(ao==="urlArgs"&&typeof aq==="string"){n.urlArgs["*"]=aq}else{if(am instanceof Array){aa(aq,ap)}else{if(typeof am==="object"){for(var ao in aq){am[ao]=aq[ao]}}else{n[ao]=aq}}}}}aj()}};aj();var L;var J;var M;var ac;var ab;function H(ao,am){var an=U(ao,1,am);an.sort(m);return an}function aj(){n.baseUrl=n.baseUrl.replace(/\/$/,"")+"/";L=H(n.paths);M=H(n.map,1);aa(M,function(am){am.v=H(am.v)});J=[];aa(n.packages,function(am){var an=am;if(typeof am==="string"){an={name:am.split("/")[0],location:am,main:"main"}}an.location=an.location||an.name;an.main=(an.main||"main").replace(/\.js$/i,"");an.reg=Y(an.name);J.push(an)});J.sort(m);ac=H(n.urlArgs,1);ab=H(n.noRequests);aa(ab,function(an){var ao=an.v;var am={};an.v=am;if(!(ao instanceof Array)){ao=[ao]
}aa(ao,function(ap){am[ap]=1})})}function Z(an,am,ao){aa(am,function(ap){if(ap.reg.test(an)){ao(ap.v,ap.k,ap);return false}})}function K(ap){var au=/(\.[a-z0-9]+)$/i;var an=/(\?[^#]*)$/;var ar="";var at=ap;var ao="";if(an.test(ap)){ao=RegExp.$1;ap=ap.replace(an,"")}if(au.test(ap)){ar=RegExp.$1;at=ap.replace(au,"")}var am=at;var aq;Z(at,L,function(aw,av){am=am.replace(av,aw);aq=1});if(!aq){Z(at,J,function(ax,av,aw){am=am.replace(aw.name,aw.location)})}if(!/^([a-z]{2,10}:\/)?\//i.test(am)){am=n.baseUrl+am}am+=ar+ao;Z(at,ac,function(av){am+=(am.indexOf("?")>0?"&":"?")+av});return am}function X(am){var ao={};function an(ar,av){if(typeof ar==="string"){if(!ao[ar]){ao[ar]=a(af(ar,am))}return ao[ar]}else{if(ar instanceof Array){var au=[];var ap=[];var at=[];aa(ar,function(az,aw){var ay=W(az);var ax=af(ay.mod,am);ap.push(ax);C[ax]=1;if(ay.res){au.push(ax);at[aw]=null}else{at[aw]=ax}});var aq={};aa(ap,function(ax){var aw;Z(ax,ab,function(ay){aw=ay});if(aw){if(aw["*"]){aq[ax]=1}else{aa(ap,function(ay){if(aw[ay]){aq[ax]=1;
return false}})}}});a(ap,function(){aa(at,function(ax,aw){if(ax==null){at[aw]=af(ar[aw],am)}});a(at,av,am)},am,aq)}}}an.toUrl=function(ap){return K(af(ap,am))};return an}function af(ar,am){if(!ar){return""}am=am||"";var ap=W(ar);if(!ap){return ar}var aq=ap.res;var ao=k(ap.mod,am);aa(J,function(at){var au=at.name;if(au===ao){ao=au+"/"+at.main;return false}});Z(am,M,function(at){Z(ao,at,function(av,au){ao=ao.replace(au,av)})});if(aq){var an=u(ao);aq=an.normalize?an.normalize(aq,function(at){return af(at,am)}):af(aq,am);ao+="!"+aq}return ao}function k(an,av){if(an.indexOf(".")===0){var aw=av.split("/");var au=an.split("/");var am=aw.length-1;var ao=au.length;var ar=0;var ap=0;pathLoop:for(var at=0;at255){f.push("*")}f.push(k[j])}if(b&&b>0&&f.length>b){k=f.join("").substring(0,b-1).replace(/\*/g,"")+g}else{return m}return k},getByteLength:function(j){var d=[],g=j.split("");
for(var f=0,b=g.length;f255){d.push("*")}d.push(g[f])}return d.length},_isValidKey:function(a){return(new RegExp('^[^\\x00-\\x20\\x7f\\(\\)<>@,;:\\\\\\"\\[\\]\\?=\\{\\}\\/\\u0080-\\uffff]+\x24')).test(a)},setCookie:function(d,f,b){f=encodeURIComponent(f);if(!jQuery._isValidKey(d)){return}b=b||{};var a=b.expires;if("number"==typeof b.expires){a=new Date();a.setTime(a.getTime()+b.expires)}document.cookie=d+"="+f+(b.path?"; path="+b.path:"")+(a?"; expires="+a.toGMTString():"")+(b.domain?"; domain="+b.domain:"")+(b.secure?"; secure":"")},getCookie:function(b){var f="";if(jQuery._isValidKey(b)){var d=new RegExp("(^| )"+b+"=([^;]*)(;|\x24)"),a=d.exec(document.cookie);if(a){f=a[2]||null;if("string"==typeof f){f=decodeURIComponent(f);return f}}}return null},removeCookie:function(b,a){a=a||{};a.expires=new Date(0);jQuery.setCookie(b,"",a)},limitWd:function(n,b){if(n===""){return""}n=n+"";var f=[],j=n.split(""),d=j.length,k=0,m=b||255;if(d<=parseInt(m/2)){return n}for(var g=0;
g255){k+=2}else{k+=1}if(k===m){j=n.substring(0,g+1);return j}else{if(k>m){j=n.substring(0,g);return j}}}return n}});function addEV(d,b,a){if(window.attachEvent){d.attachEvent("on"+b,a)}else{if(window.addEventListener){d.addEventListener(b,a,false)}}}function _aMC(d){var b=d,a=-1;while(b=b.parentNode){a=parseInt(b.getAttribute("id"));if(a>0){return a}}}function al_c(a){while(a.tagName!="TABLE"){a=a.parentNode}return a.getAttribute("id")}function al_c2(b,a){while(a--){while((b=b.parentNode).tagName!="TABLE"){}}return b.getAttribute("id")}function c(a){var k=a.p1;if(a.fm=="alop"&&!("rsv_xpath" in a)){if(k&&G(k).getAttribute("srcid")=="6677"){}else{return true}}if(k&&!("p5" in a)){a.p5=k}var b=window.document.location.href,g="",d="",m="",f=window["BD_PS_C"+(new Date()).getTime()]=new Image();for(v in a){switch(v){case"title":d=a[v].replace(/<[^<>]+>/g,"");if(d&&d.length>100){d=d.substring(0,100)}d=encodeURIComponent(d);break;case"mu":case"url":d=escape(a[v]);break;
default:d=a[v]}g+="&"+v+"="+d}if(!("mu" in a)){try{if(("p2" in a)&&G(a.p1).getAttribute("mu")&&a.fm!="pl"){m="&mu="+escape(G(a.p1).getAttribute("mu"))}}catch(i){}}if(window.bds&&bds.comm){var j=bds.comm.ubsurl+"?q="+bds.comm.queryEnc+g+m+"&rsv_sid="+bds.comm.sid+"&cid="+bds.comm.cid+"&qid="+bds.comm.queryId+"&t="+new Date().getTime();if(bds.comm.inter){j=j+"&rsv_inter="+bds.comm.inter}if(bds.comm.seinfo&&bds.comm.seinfo.rsv_pstg){j=j+"&rsv_pstg="+bds.comm.seinfo.rsv_pstg}if(bds.comm.cftime&&bds.comm.cftime!=0){j=j+"&rsv_cftime="+bds.comm.cftime}if(bds.comm.resultPage){j=j+"&rsv_iorr=1"}else{j=j+"&rsv_iorr=0"}if(bds.comm.tn){j=j+"&rsv_tn="+bds.comm.tn}if(bds.comm.indexSid){j+="&rsv_isid="+bds.comm.indexSid}if(bds.comm.lastVoiceQuery){j+="&rsv_lavo="+encodeURIComponent(bds.comm.lastVoiceQuery)}if(Cookie.get("ispeed")){j+="&rsv_ispeed="+Cookie.get("ispeed")}if(/ssl_sample/.test(location.href)){var l=location.href.match(/ssl_sample=[^=&]+/i);j+="&rsv_"+l[0]}if(/ssl_s=/.test(location.href)){var l=location.href.match(/ssl_s=[^=&]+/i);
j+="&rsv_"+l[0]}j+="&rsv_ssl="+(location.protocol==="https:"?1:0);j+="&path="+encodeURIComponent(b);j+="&rsv_did="+(bds.comm.did?bds.comm.did:"");f.src=j}return true}$(window).on("resize",function(){if("pageState" in window&&pageState!=0){bds.util.setContainerWidth();bds.event.trigger("se.window_resize")}});(function(){var b=bds.util&&bds.util.domain?bds.util.domain.get("http://s1.bdstatic.com"):"http://s1.bdstatic.com";var a=bds.util&&bds.util.domain?bds.util.domain.get("http://ecmb.bdimg.com"):"http://ecmb.bdimg.com";require.config({baseUrl:b+"/r/www/cache/biz",packages:[{name:"ecma",location:a+"/public01"},{name:"ecmb",location:a+"/public01"}],paths:{aladdin:b+"/r/www/aladdin",ui:b+"/r/www/cache/amd/ui","ui/config":b+"/r/www/cache/amd/ui/Control","ui/lib":b+"/r/www/cache/amd/ui/Control","ui/Control":b+"/r/www/cache/amd/ui/Control"},urlArgs:{"ui/ImgZoomHover":"20141104","ui/ImgZoomHover1":"20141104","ui/ImgZoomHover2":"20141104","ui/ImgZoomHover3":"20141104"}})})();function TagQ(a,b){return b.getElementsByTagName(a)
}function h(b){b.style.behavior="url(#default#homepage)";b.setHomePage(bds.comm.domain);var a=window["BD_PS_C"+(new Date()).getTime()]=new Image();a.src=bds.comm.ubsurl+"?fm=hp&tn="+bds.comm.tn+"&t="+new Date().getTime()}function setHeadUrl(b){var d=G("kw").value;d=encodeURIComponent(d);var a=b.href;a=a.replace(new RegExp("("+b.getAttribute("wdfield")+"=)[^&]*"),"\x241"+d);b.href=a}bds.util.addStyle=function(b){if(isIE){var d=document.createStyleSheet();d.cssText=b}else{var a=document.createElement("style");a.type="text/css";a.appendChild(document.createTextNode(b));document.getElementsByTagName("HEAD")[0].appendChild(a)}};bds.util.getContentRightHeight=function(){return($("#content_right").get(0))?$("#content_right").get(0).offsetHeight:0};bds.util.getContentLeftHeight=function(){return($("#content_left").get(0))?$("#content_left").get(0).offsetHeight:0};if(!window.A){function G(a){return document.getElementById(a)}window.bds=window.bds||{};bds.util=bds.util||{};bds.util.getWinWidth=function(){return window.document.documentElement.clientWidth
};bds.util.setContainerWidth=function(){var g=G("container"),b=G("wrapper"),a=function(i,j){j.className=j.className.replace(i,"")},f=function(i,j){j.className=(j.className+" "+i).replace(/^\s+/g,"")},d=function(i,j){return i.test(j.className)};if(bds.util.getWinWidth()<1207){if(g){a(/\bcontainer_l\b/g,g);if(!d(/\bcontainer_s\b/,g)){f("container_s",g)}}if(b){a(/\bwrapper_l\b/g,b);if(!d(/\bwrapper_s\b/,b)){f("wrapper_s",b)}}bds.comm.containerSize="s"}else{if(g){a(/\bcontainer_s\b/g,g);if(!d(/\bcontainer_l\b/,g)){f("container_l",g)}}if(b){a(/\bwrapper_s\b/g,b);if(!d(/\bwrapper_l\b/,b)){f("wrapper_l",b)}}bds.comm.containerSize="l"}};(function(){var d=[],i=false;var b=function(k,j){try{k.call(j)}catch(l){}},f=function(){this.ids=[];this.has=true;this.list=[];this.logs=[];this.loadTimes=[];this.groupData=[];this.mergeFns=[];this._currentContainer=null};window.A=bds.aladdin={};b(f,window.A);bds.ready=function(j){if(typeof j!="function"){return}if(i){b(j)}else{d.push(j)}};bds.doReady=function(){i=true;
while(d.length){b(d.shift())}};bds.clearReady=function(){i=false;d=[]};A.__reset=f;var a=(function(){var j=document.getElementsByTagName("script");return function(){var l=j[j.length-1];if(window.currentScriptElem){l=window.currentScriptElem}var k=l;while(k){if(k.className){if(/(?:^|\s)result(?:-op)?(?:$|\s)/.test(k.className)){if(tplname=k.getAttribute("tpl")){return k}}}k=k.parentNode}}})(),g=function(j,m,l){var n;if(!j.initIndex){n={container:j,data:{},handlers:[]};j.initIndex=A.groupData.length+1;A.groupData.push(n)}else{n=A.groupData[j.initIndex-1]}if(typeof m=="function"){n.handlers.push(m)}else{if(typeof m=="object"){for(var o in m){if(m.hasOwnProperty(o)){n.data[o]=m[o]}}}else{n.data[m]=l}}};A.init=A.setup=function(m,l){if(m===undefined||m===null){return}var j=A._currentContainer||a();if(!j){return}g(j,m,l)};A.merge=function(k,j){A.mergeFns.push({tplName:k,fn:j})}})()}function ns_c_pj(i,f){var j=encodeURIComponent(window.document.location.href),d="",a="",b="",g=bds&&bds.comm&&bds.comm.did?bds.comm.did:"";
wd=bds.comm.queryEnc,nsclickDomain=bds&&bds.util&&bds.util.domain?bds.util.domain.get("http://nsclick.baidu.com"):"http://nsclick.baidu.com",img=window["BD_PS_C"+(new Date()).getTime()]=new Image(),src="";for(v in i){switch(v){case"title":a=encodeURIComponent(i[v].replace(/<[^<>]+>/g,""));break;case"url":a=encodeURIComponent(i[v]);break;default:a=i[v]}d+=v+"="+a+"&"}b="&mu="+j;src=nsclickDomain+"/v.gif?pid=201&"+(f||"")+d+"path="+j+"&wd="+wd+"&rsv_sid="+(bds.comm.ishome&&bds.comm.indexSid?bds.comm.indexSid:bds.comm.sid)+"&rsv_did="+g+"&t="+new Date().getTime();if(Cookie.get("H_PS_SKIN")&&Cookie.get("H_PS_SKIN")!="0"){src+="&rsv_skin=1"}img.src=src;return true}function ns_c(b,a){if(a===true){return ns_c_pj(b,"pj=www&rsv_sample=1&")}return ns_c_pj(b,"pj=www&")}A.uiPrefix="//www.baidu.com/cache/aladdin/ui/";(function(){var b=window.bds.aladdin;var g=[];var l={},i=0;var d=function(q,p){try{q.call(p)}catch(r){}};var f=function(p){p.ajaxId=++i;l[p.ajaxId]=p};var n=function(p){delete l[p.ajaxId]
};var k=function(p){if(!p.ajaxId){return false}return l.hasOwnProperty(p.ajaxId)};var m=function(q){var p={};if(q){try{var s=new Function("return "+q)();if(s){p=s}}catch(t){}}return p},a=function(){var r=$(".result-op").get().concat($(".result").get()),t={};for(var q=0,p,s;s=r[q];q++){if(p=s.getAttribute("tpl")){if(t[p]){t[p].push(s)}else{t[p]=[s]}}}return t};var o=[],j=[];b.addDisposeHandler=function(p){j.push(p)};b.dispose=function(){while(o.length){var r=o.shift();d(r.fn,r.obj)}var p=j;for(var q=0;q1){if(z){a(x.shift(),function(){if(x.length>0){a(x,z)}})}else{while(x.length){a(x.shift())}}return}u=x[0];if(u==="jquery"&&window.jQuery){!g.ui.jquery&&(g.ui.jquery=window.jQuery);z&&z();return}var y=u.replace(/\./g,"/");var t=u.replace(/^[\s\S]*\./,"");var w=g.uiPrefix+y+"/"+t;if(y.search("style/")==0){o(w+".css",z)
}else{w+=".js";if(l.hasOwnProperty(u)){if(typeof l[u]=="function"){w=l[u](u,w)}else{if(typeof l[u]=="string"){w=l[u]}}}else{if(l.hasOwnProperty("*")){w=l["*"](u,w)}}if(z){r(w,z)}else{k(w)}}}a.cache=b;function o(u,w){w=w||m;if(u in b){w();return}var t=p.createElement("link");t.rel="stylesheet";t.type="text/css";t.href=u;t.setAttribute("data-for","A.ui");n.appendChild(t);b[u]=1;w()}function k(t){if(i){r(t,m);return}if(t in b){return}p.write('