Initial commit

This commit is contained in:
Nuisance 2021-10-19 08:18:35 +08:00
parent 4744811515
commit 298a1af86f
1976 changed files with 131049 additions and 2 deletions

865
README.md
View File

@ -1,3 +1,864 @@
# home-school-contact-app
# 基于Android的家庭学校联系平台APP开发与实现
基于Android的家庭学校联系平台APP开发与实现
# 摘要
学校教育与家庭教育的不一致,容易产生教育断层的局面,而现有的校讯通等家校互动平台,又存在教师与家长单向沟通等方面的问题。如今信息技术的飞速发展为家校共育的健康发展提供了强有力的保障,“互联网+”已成趋势。学校和教师要一改过去传统的沟通交流方式,借用互联网与手机短信相结合的家校互动信息平台,扩展沟通交流渠道,架起家校沟通的桥梁,从而让家校教育形成合力,提高教育的时效性,促进学生健康成长。因此,本文就目前“互联网+教育”的新趋势进行了说明以明确进行研究的目的。随后将对本平台实现的过程和原理进行一一探讨以让读者能了解实现其的具体方法。最后为了实现这个软件平台本文对这个软件作了系统分析和系统设计最终实现了该软件。通过测试该平台能正常运行在Android系统下的智能设备也验证了本文所探讨的设计正确性和重要性。
**关键词:**互联网、家校互动、家校协作、功能应用
# Abstract
Nowadays, the inconsistency of school education and family education may easily result in education gap. And the existing home-school interactive platforms like School Paper still have some problems including the issue of one-way communication between teachers and parents. The rapid development of information technology has provided a strong guarantee for the healthy development of home-school coeducation. Currently, the” Internet+” mode has become a trend, which requires the schools and teachers update the traditional communication ways with parents. They can combine the internet with mobile phone text message to establish a home-school interaction information platform, expanding the communication channel, setting up the bridge of communication so as to produce a unified strength of home-school education, improving the efficiency of education and promote the healthy growth of students. Therefore, This paper puts forward the aim of this research by describing the current trend of “Internet +Education”. In addition, It explores the process and principles of the new platform to make the readers understand the specific methods. Finally, in order to achieve this software platform, this paper makes a systematic analysis and system design. Through the test, the platform can run in the intelligent equipment of Android system, thus verifying the correctness and importance of the design discussed in this paper.
**Key words:** Internet; home schoolinteraction; home school cooperation; Function Application
# 1. 概述
## 1.1 研究背景和意义
社会的发展智能手机的普及让各种各样的手机应用APP成为我们生活中必不可少的一部分教育行业也搭上了这趟车走上了“互联网+”教育,为了方便学生、家长、学校三体互动,让家长和学生能及时收到学校发送的消息,满足用户之前的实时交流,“爱吖校推”应运而生。
“爱吖校推”是一款基于Android的家校互动平台。随着社会的发展各种APP在手机行业发展的助推下应用越来越广泛。某权威调研机构表示2016年Android系统已经占领市场份额高达81.3%而大名鼎鼎的iPhone屈居第二仅占17.8%更令人惊奇的是Android的市场份额还在持续增多。
社交是人类社会性群体的基本属性。而“爱吖校推”就是一款基于教育行业的社交类APP。它支持所有的即时通讯应该包含的功能文件发送、位置发送、音视频通话、图片、视频等同样也支持校方和教师发送公告作业并推送到相应群体的Android智能终端。在当前微信用户如日中天的基础上“爱吖校推”采用微信朋友圈的方式支持消息发送、点赞、评论、拍照、秒拍、微视频等群体社交真正进入微社交时代。这是一件非常有意义的事情。
## 1.2 国内现状
家校互动的需求长期已有,它的研究和设计从本世纪初就开始了,而且也取得了不小的成效。但在早期由于技术的限制,所以存在信息的单项沟通,比如早期的“校讯通”。它就是单纯的通过收发短信来达到家校信息交流,教师得不到任何的反馈。之后随着移动互联网的发展,微信平台如鱼得水,其双向沟通性让一线教师感觉是雪中送炭,但其信息筛选性一直为人诟病。虽然微信等即时通讯软件一定意义上解决了家校互动的问题,但这样的处理,无疑是增加了教师的工作量。
近年来,有 “爱上学”、“和校园”、“爱学习”、“校讯通”等已经运行的家校互动支持平台20多个这些平台主要实现信息发布查看等功能对于信息的及时推送功能匮乏加之在校大学生多用QQ群或者微信群作为沟通平台常常使得通知公告信息错过导致了学生之间的信息不对等而国内超高量的外出打工家长想看到没有手机的孩子实在困难。所以“爱吖校推”在操作简单的基础上优化了拍照和微视频让老师可以把学生的学校情况实时地分享给大家。消息的离线推送也使得信息的准确到达率迅速提升而附带音视频通话的及时通讯板块也是拉近了学生、家长、教师之间的距离。
## 1.3 论文的思路和结构
该论文分十个部分进行逐一讲解:
首先把概述作为了第一部分,该系统平台的研究背景和意义以及国内现状作为主要讲述内容。
第二部分是研究方案和架构概述,主要阐述了本课题预计花费时间的设计方案、软件开发设计方案以及设计目标,最后做了架构概述。
第三部分是需求分析,主要从用户需求、性能需求和功能需求三方面阐述了需求板块需要具备的东西。本部分还用了用例图、用例说明增加相关人员的理解。
第四部分是概要设计主要从Android端和服务器端分别阐述了功能总体设计然后画了数据库E-R图最后是系统类图和界面设计。
第五部分是详细设计主要从数据库设计、CS协议通信、时序图三个方面阐述。
第六部分是系统实现,主要介绍了本次系统实现所用到的开发工具,并展示了开发界面总览和核心功能代码的讲解。
第七部分是软件测试分版块进行功能、性能、安全、交叉事件以及兼容性板块进行测试并修改系统bug。
第八、九部分是本次毕业设计中的收获和结论以及自己的感想。
最后是本文所参考的各种有价值的资料列表。
# 2. 研究方案和架构概述
## 2.1 预计花费时间设计方案
- 花费7天查阅了关于即时通讯的资料以及小米推送的官方文档并对其进行分析整理
- 花费10天查阅了一些技术博客和相关论坛以及GitHub上比较火的框架和项目
- 花费15天进行数据库设计并对系统框架做一个全局性思考
- 花费10天编写后台API数据接口并做简单测试
- 花费1个月编写Android端代码并对后台数据可行性进行验证修改
- 最后进行常规测试并在各大机器上运行以保证不会出现致命Bug
## 2.2 软件开发设计方案
采用MVC开发模式按照功能可划分为发通知发作业互评点赞图片并茂即时通讯小米推送等模块。
功能模块细化:
- **班级圈**:班级圈包含教师可发放通知、作业,基本支持图文并茂社区化和微视频上传。家长可查看自己班级的每一条信息,以及进行互评回复点赞
- **即时通讯**即时通讯板块主要依赖于环信在环信SDK的大前提下引入基本的即时通讯和音视频通话
- **社区板块**:社区板块是用户只要在一个班级即可进行类似朋友圈的交流,依然可以进行互评点赞回复
- **发现板块**:发现板块主要是为加载一些广告和活动
- **我的板块**:我的板块主要是提供用户信息的更改设置等
- **推送板块**当前推送继承了Google推送、华为推送和小米推送以最大的可能提高推送接收率
## 2.3 本课题的设计目标
模块功能实现的目标:
- **班级圈**:保证班级圈数据的正常显示,非本班人员应该不具备查看该班信息权限的能力。采用广播、接口回调及其其它方式完成数据的传递和更新
- **即时通讯**:保证音视频通话的离线呼起,保证长连接的引用,保证用户能正常收发消息
- **社区**:同班级圈
- **发现**:保证广告的通畅性和可行性
- **我的板块**:保证用户信息的修改处理正常,做到信息不泄漏
- **推送**:保证推送成功率与正确率
- **交互性良好**采用materialdesign 风格设计,以及动画效果的引用,使用户和软件具有更加青睐的交互体验,并通过信息圈子推送,增加了用户粘性
- **代码风格佳**:在编码过程中,严格要求分包逻辑,采用模块化分包,并对代码进行合理的封装处理,使代码更加模块化,让其他人能更易上手
- **实用性**:通过不断的更新产品功能和接收用户反馈,让该产品更加地符合消费者思维
## 2.4 架构概述
本系统采用C/S架构分为客户端和服务器端。
客户端被分为了表现层、业务逻辑层和数据访问层三个层面。
- **表现层**主要通过Android应用页面来展示数据以及一系列事件响应的UI控件
- **业务逻辑层**主要用于业务逻辑的处理。通常由业务服务Service类和业务实体类Entity组成
- **数据访问层**本系统采用的数据库是MySQL采用XAMPP进行服务器搭建采用PHP作为后台数据接口编写用花生壳做域名解析以达到Android客户端与服务器之间的访问
# 3. 需求分析
需求分析是“爱吖校推”应用分析的必要阶段,下面分软硬件需求、功能需求和用户需求三方面做介绍,
## 3.1 软硬件需求
本系统的软硬件需求如下:
- **在Android平台上运行**,系统在4.0以上
- **后台数据库**MySQL
- **开发环境**JavaJDK1.7 ,Windows 10
- **开发工具**AndroidStudio、Eclipse For PHP、XAMPP、
- **个人计算机**:华硕飞行堡垒笔记本
## 3.2 功能需求
“爱吖校推”作为一款功能性软件,其功能需求相当重要。以下为“爱吖校推”的功能需求:
**发通知、发作业**
发通知和发作业是学校教师特有的功能教师可以通过“爱吖校推”平台进行通知和作业的发放每当发一条信息该班的所有人员便可以收到来自服务器的信息推送提醒家长打开APP查看。每一条通知和作业家长都可以点赞和互评和回复。这样让家长和学校的关系更贴切也增加了信息筛选机制从而避免了QQ群、微信群等多余信息的影响。
**传视频、传照片**
传视频是在社区和通知作业板块均具备的功能,紧跟微视频的时代步伐,教师可以把孩子在学校的精彩表演,录制下来发到班群里,家长便可以看到。家长也可以把孩子在家里做的有意义的事情放到社区,让同一个班级的家长朋友们借鉴。良好的图文并茂社区化,不仅增进了家长和学校的交流,还增进了家长之间的联系和友谊。
**即时通讯**
即时通讯板块是一个整体的板块基本包含QQ微信的所有功能依然可以发图片、发消息、发语音、发定位、音视频通话等。意在增加朋友之间的联系和家长和学校教师的一对一交流和多对多交流。
**发现板块**
发现板块主要是轮播的一些优秀且有利于教师家长的APP功能板块并且会组织一些活动邀请大家参加。
**离线推送**
离线推送在家校互动平台软件中是一个必备功能,也算是一个核心功能,有它才能保证用户真正收到来自教师发放的信息,以及即时通讯过来的消息。同时音视频通话等即时性要求较高的功能,也得依赖它。并且,推送信息到通知栏的方式向用户传递信息,也是可以增加用户粘性。
## 3.3 用户需求
在“爱吖校推”应用的开发过程中,为了尽量满足学校老师和家长用户的要求。目前得到的需求有:
- 图片显示清晰,但不能太大,以免浪费流量
- 微视频的压缩要处理好,不能太浪费流量
- 即时通讯要通畅
- 要具备离线推送,确保家长用户能收到教师发送的作业和通知
- 要有权限管理,不能让外班人员看到本班的消息
- 公告和作业不能插广告
- 应用不能经常闪退
- 应用不能太大,也不能太占内存
- 运行要流畅,不能出现卡顿现象
## 3.4 用例图
### 3.4.1 登录板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/6138909c0da0575049377ef43e3b0279.writebug)
### 3.4.2 班圈板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/fdcd5ec8f15f42161b6b9f75e0fc0ea0.writebug)
### 3.4.3 消息板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/11d972e6972c3854dfe64962e989f62f.writebug)
### 3.4.4 发现板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/d59d37f60fa3a4e21abf7c90ae73c730.writebug)
### 3.4.5 我的板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/240cb69f902d9d7575f84f40b26225e3.writebug)
## 3.5 用例说明
### 3.5.1 UC1用户登录
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/65e5c68cf43a3556df1276f47d4a04ac.writebug)
### 3.5.2 UC2用户注册
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/f7c278e20635684af503e65203109d58.writebug)
### 3.5.3 UC3找回密码
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/c6ee4a4cc9766a2fa69b7536d03b8802.writebug)
### 3.5.4 UC4发布信息
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/897866eafcd4cc3bdfe3d3dd07dfe851.writebug)
### 3.5.5 UC5查看所有信息
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/8392bca0a6240a17dc9affa204501a6f.writebug)
### 3.5.6 UC6查看信息详情
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/f05e50e19c9425bd76bd733e28f38f3c.writebug)
### 3.5.7 UC7点赞评论回复
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/bb09530905a87514525ab1fad79e7a97.writebug)
### 3.5.8 UC8查看联系人和会话
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/b43a4e687791ec5e717743740649dfd5.writebug)
### 3.5.9 UC9聊天
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/2665c813c2037221d40aba56ae9a65cc.writebug)
### 3.5.10 UC10音视频通话
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/f6c2e69479a250aaacb59fdcf7a6f68f.writebug)
### 3.5.11 UC11修改个人信息
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/0cf5d4114e98a999b3112663cdee3533.writebug)
### 3.5.12 UC12修改孩子信息
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/3d746f9584ab9d7b539e2e208e191381.writebug)
### 3.5.13 UC13退出登录
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/ad96395e56cd7c327446c6643b2a7680.writebug)
# 4. 概要设计
## 4.1 系统功能总体设计图
### 4.1.1Android端功能总体设计图
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/aae74aa8d2ddfa0640c734b9f5183abf.writebug)
### 4.1.2 服务器端功能总体设计图
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/a71ad85743e731250e6e0225b653f7e5.writebug)
## 4.2 数据库E-R图设计
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/a9bab1ecbabc432385cab7313f99dcac.writebug)
## 4.3 系统类图
### 4.3.1 APP端登录板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/c5a13c145509eab0503ba3c9b2a52a47.writebug)
### 4.3.2 APP端主页板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/d40060ae6e4f270a20be48f6e3f459ec.writebug)
### 4.3.3 APP端班圈板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/2e07225345252b0c4169e4b474234feb.writebug)
### 4.3.4 APP端发布板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/1786fb557a0c2cbc77186c6a4986a29e.writebug)
### 4.3.5 APP端消息板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/6c68f70b142b35443b47fbb91c606734.writebug)
### 4.3.6 APP端发现板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/e53e756f71f60cb167eee2b8ea01235c.writebug)
### 4.3.7 APP端我的板块
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/e298be3bf6e8c9d3fedd56a694afd8a0.writebug)
## 4.4 界面设计
**图片选取界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/949e68f2066dbb66446cfd81d2688b3f.writebug)
**图片选取界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/cd163a94614be73c4482949b7b1328b3.writebug)
**登录界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/72793c3d5f02f6bb0f2a64d0fb421b1c.writebug)
**手机号验证界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/5ae302c62197b1f3b08a5ce500963b4e.writebug)
**主界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/0cc6fba67ab2c9ad21b38e3ebd001787.writebug)
**课程表界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/23f1d6e55cf03004549868eebbc3ea21.writebug)
**联系人列表界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/2959bc98336bc3c52df472f01694597f.writebug)
**聊天界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/35b1838814b50246a6417d23314e4980.writebug)
**音频呼叫界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/eeebd14c1718cece6b2cffed2eebf37b.writebug)
**发布信息界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/9b1d9b6e7be0482f05b25f5d6a082272.writebug)
**发布界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/be4b85cd5a26591ddb89d2f1ad2270b4.writebug)
**我的板块界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/da80445ae480e15eeed48c54ee77cefb.writebug)
**按住拍界面设计**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/cb18b74b1d9a5eb3987a39d7be00b3ea.writebug)
# 5. 详细设计
## 5.1 数据库详细设计文档
本软件的数据库为MySQL数据库主要是搭建在XAMPP上结合PHP存在。主要分为以下几个数据表
### 5.1.1 用户表设计aiya_user
| 字段 | 属性 | 备注 |
| ------------ | ------------------ | --------------------- |
| username | varchar(20)(主键) | 账号(手机号) |
| password | varchar(20) | 密码 |
| nickname | varchar(20) | 用户昵称 |
| type | int(1) | 用户类型 1教师、2 家长、3管理员 |
| classid | int(10)(外键) | 班级id |
| avatar | Varchar(100) | 用户头像地址 |
| birthday | date | 生日,实际存储为时间戳 |
| address | textnull | 用户地址 |
| child_name | varchar(20) null | 孩子姓名 |
| child_avatar | textnull | 孩子头像地址 |
### 5.1.2 班级信息表设计aiya_class
| 字段 | 属性 | 备注 |
| ---------- | ------------- | ---- |
| classid | int(10)(主键自增) | 班级id |
| classname | varchar(50) | 班级名称 |
| schoolname | text | 学校名称 |
### 5.1.3 主贴表设计aiya_main
| 字段 | 属性 | 备注 |
| -------- | --------------- | -------------------------- |
| mainid | int(10)(主键自增) | 帖子id, |
| classid | int10外键 | 班级id |
| username | varchar(20)(外键) | 用户名 |
| time | timestamp | 发布时间实际存储相当于long型时间戳 |
| infotype | int(1) | 主贴类型1 代表公告 2 代表作业 3 代表动态) |
| content | text | 帖子内容 |
### 5.1.4 评论表设计(aiya_comment)
| 字段 | 属性 | 备注 |
| -------- | --------------- | ----------------------- |
| infoid | int(11)(主键自增) | 信息id |
| mainid | int(10)(外键) | 主贴id用于识别隶属于哪一条帖子的评论 |
| username | varchar(20)(外键) | 用户名,用于识别发布人信息 |
| time | bigint(20) | 发布时间long型时间戳 |
| content | text | 发布内容 |
| reply | varchar(20)(外键) | 用户表username作为外键用于回复@功能 |
### 5.1.5 点赞表设计aiya_praise
| 字段 | 属性 | 备注 |
| -------- | ------------- | ------------------ |
| praiseid | int(10)(主键自增) | 点赞信息id |
| mainid | int(10)(外键) | 主贴表外键,用于识别赞的是哪一条主贴 |
| username | varchar(20) | 用户表外键,用于识别是谁点赞了 |
### 5.1.6 主贴图片表设计aiya_pic
| 字段 | 属性 | 备注 |
| ------ | -------------- | ------ |
| picid | int(10)(主键自增) | 图片id |
| mainid | int(10)(主贴表外键) | 主贴id |
| url | text | 图片存放地址 |
## 5.2 CS协议通信文档
说明返回格式为code,msg,data三个字段code为0是代表请求逻辑正确-1为请求异常
### 5.2.1 用户系统
**获取用户是否注册APP**
接口地址: /user/usable_mobile.PHP
方式和返回GET JSON
请求参数:
| 名称 | 类型 | 说明 |
| ------ | ------ | ----- |
| mobile | string | 用户手机号 |
**注册**
接口地址: /user/register.PHP
方式和返回POST JSON
请求参数:
| 名称 | 类型 | 说明 |
| -------- | ------ | ------------- |
| username | string | 用户手机号 |
| password | string | 用户密码 |
| nickname | string | 昵称 |
| birthday | string | 生日传递long型时间戳 |
| avatar | string | 头像上传的地址 |
**登录**
接口地址: /user/login.PHP
方式和返回POST JSON
请求参数:
| 名称 | 类型 | 说明 |
| -------- | ------ | ----- |
| username | String | 用户手机号 |
| password | string | 用户密码 |
**重置密码**
接口地址: /user/reset_pwd.PHP
方式和返回POST JSON
请求参数:
| 名称 | 类型 | 说明 |
| -------- | ------ | ----- |
| username | string | 用户手机号 |
| password | string | 用户新密码 |
**上传头像**
接口地址: /user/avatar.PHP
方式和返回POST JSON
请求参数:
| 名称 | 类型 | 说明 |
| ---- | ---- | ------- |
| file | File | 需要上传的文件 |
**更新头像url**
接口地址: /user/update_avatar.PHP
方式和返回GET JSON
请求参数:
| 名称 | 类型 | 说明 |
| -------- | ------ | ------------ |
| username | string | 用户名 |
| iconUrl | string | 头像地址 |
| type | int | 类型1为自己, 2为孩子 |
### 5.2.2 信息系统
**异步获取主贴等信息**
接口地址: /info/info_main.PHP
方式和返回GET JSON
请求参数:
| 名称 | 类型 | 说明 |
| -------- | ---- | ------------------------ |
| classid | int | 班级id用于识别可见度 |
| infotype | int | 信息类型 1公告2作业3动态 |
| count | int | 信息起始数默认一次获取10条需要更改联系后台 |
**获取发布信息人的信息**
接口地址: /info/get_user.PHP
方式和返回GET JSON
请求参数:
| 名称 | 类型 | 说明 |
| -------- | ------ | ---- |
| username | string | 用户名 |
**评论信息**
接口地址: /info/insert_comment.PHP
方式和返回POST JSON
请求参数:
| 名称 | 类型 | 说明 |
| -------- | ------ | ------ |
| mainId | int | 主贴id |
| username | string | 用户名 |
| content | string | 评论内容 |
| reply | string | 回复人用户名 |
**更新点赞信息**
接口地址: /info/praise.PHP
方式和返回GET JSON
请求参数:
| 名 | 类型 | 说明 |
| -------- | ------ | ---- |
| mainId | int | 主贴id |
| username | string | 用户名 |
## 5.3 时序图
### 5.3.1 登录时序图
该时序图是实现用例UC1用户的登录。
- 用户进入LoginActivity登录界面后按照提示输入账号名必须为正确的手机号和密码不少于6位
- 先采用StringUtil工具类对输入数据进行验证再把LoginPresenter把输入的数据传递给网络交互类AppService让其与服务器进行数据交互并返回给LoginPresenter通过回调机制让View层显示相关信息若是登录成功则正确跳转到应用主页面否则显示相关错误信息
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/79882ffef9d2cfe7238acfdf8da0a13b.writebug)
### 5.3.2 发布时序图
该时序图是实现用例UC4发布信息。
- 用户进入发布页面,可以输入相关话题信息,也可上传附件(微视频和图片不共存)
- 如果上传附件,则调用压缩相关的工具类进行附件压缩,如果压缩失败,则显示相关错误信息
- 未输入信息无法点击发布如果点击发布按钮则让ReleasPresenter处理相关逻辑并把发布话题的信息传递给AppService类做网络访问处理服务器返回相关信息采用回调机制让View显示出相关信息
- 如果发布成功,则返回到主页面,并发送广播提示主页面进行数据刷新
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/e3075b7e28c2b31fa70d2ca5d13d5596.writebug)
### 5.3.3 圈子信息时序图
该时序图是实现用例UC6查看信息详情。
- 用户在主页面可以看到话题相关信息(包括通知、作业、社区)
- 如果点击任何一条信息,则可以跳转到详情页面,可以查看到相关点赞信息和评论信息
- 点击评论可以对该条话题信息进行评论,点击评论人可对该用户进行回复
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/f02379593c516587d95273f2ae0f0d65.writebug)
### 5.3.4 聊天时序图
该时序图是实现用例UC9聊天。
- 用户可以从会话页面或者联系人页面进入聊天页面ChatActivity
- 可以发送任何的文本消息,也可以点击下方“加号”按钮进行语音图片视频等文件的发送
- 可以直接调用音视频通话,向对方发起通话
- 任何的与服务器交互逻辑均交给EMClient类进行处理
- 被呼叫的用户可以选择拒绝音视频通话并把相关信息返回给EMClient类
- 监听类收到EMClient返回的信息后处理相应回调显示相关信息
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/2656ab164af05af5807b50638069513e.writebug)
# 6. 系统实现
## 6.1 开发工具简介
Android Studio :AndroidStudio 是Google推广的一款全新的Android开发工具采用全新的Gradle方式进行编译同时对原有的Eclipse开发的项目进行了支持。在2016年年底Google宣布停止对Eclipse的支持与维护彻底地宣布了Android Studio作为“Google亲儿子”的地位。其强大的市场占有率成为了使用趋势我们不能墨守成规需要向着新趋势看齐。
XAMPP: XAMPPApache+MySQL+PHP+PERL原来的名字叫 LAMPP但最新的几个版本被更名为XAMPP主要是为了避免误解。它作为一款建站集成软件包功能非常完善其强大的兼容性更是征服了用户不仅提供了Windows、Mac等主流操作系统更是对Linux、Solaris等其它操作系统做了支持。更完美的是它还支持包含简体中文、繁体中文、英文、韩文等多国语言包。但XAMPP最著名的还是它的便捷性使用XAMPP只需要下载、解压、启动三个步骤就能让Apache服务器运行在机器上并且还支持读取PHP文件以及集成了MySQL的使用。 EclipseFor PHP:这款软件是Eclipse分支下专用于开发PHP的一款IDE支持PHP5和PHP7在这里我们主要用它来开发后台接口板块。
## 6.2 开发界面总览
### 6.2.1 Android开发界面总览
**Android 源码分包预览**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/3662af5a6197c3da9e309b0dbc7e80a9.writebug)
**Android 资源文件预览**
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/affac6058735375b202bed943424b69d.writebug)
### 6.2.2 PHP开发界面总览
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/98313a1f43173fa25a6d1f2e9294a05f.writebug)
### 6.2.3 数据库操作页面总览
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/fcf4e7b74a24006929d3bd432cafa203.writebug)
## 6.3 核心功能代码
### 6.3.1 图片压缩处理
项目中的图片压缩来源于我GitHub已经开源的一个开源库目前项目已经得到超700 Stars主要采取BitmapFactory的内部类Options以及Bitmap下的createScaleBitmap方法对图片进行质量压缩和尺寸压缩。
思路:
- Bitmap是一个相当大的对象特别容易导致OOM所以我们在压缩的时候并不能直接采用Bitmap而采用BitmapFatory.Options。它有一个相当强大的属性inJustDecodeBounds当这个属性为true的时候调用decode前缀的方法返回的就不是一个完整的Bitmap对象而是null。因为它禁止这些方法为Bitmap分配内存当设置这个属性为true时便会复制Options的三个属性它们分别是outWidth,outHeight和outMimeType。相当于不读取这个图片却获取到了它的参数的确很厉害。
- 另外一个不得不说的属性就是inSampleSize了可以理解为压缩比率设置好这个比率就能调用decodeXXXX方法获得缩略图了如果图片大小都一致则可以定死它。可问题是我们的图片大小通常是不一致的那我们压缩的重中之重就是获得这个正确的比率。因此咱们完全能够经过我们想要的长宽通过多次循环比对从而达到等比例压缩。
- 然而, inSampleSize官方注释告诉我们一个必须注意的点因为inSampleSize只能是2的整数次幂意味着如何我们通过循环算出来inSampleSize为6的话这时候只能向下取得整数次幂也就是4。这样明显是达不到我们想要求的标准的
- Bitmap的createScaleBitmap这个方法成功消除了我们的焦虑我们可以借用这个方法把我们之前得到的较大的缩略图进行二次缩小直到完全符合我们的要求
核心代码为:
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/e6880457974d7176278ecc23bd0a8f3d.writebug)
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/ea5a949f51327acd2d339ab6c1e80b54.writebug)
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/b5a8ee83912e51a8bac0d19a65428101.writebug)
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/5704570d88301a1b86c2d15c8d4c3a91.writebug)
### 6.3.2 相机适配处理
图片选取来自于我维护的一个开源库ImagePicker目前GitHub Star数超过1300+主要通过从数据库读取所有图片信息并返回到一个List中该List将把所有图片的path存储在一起然后把这些图片放在RecyclerView中显示项目UI完全仿照微信做处理。为了解决Intent传值限制我在项目中采用单例加锁的方式得以解决。
针对Android的适配上也是下了不少功夫主要表现在Android 6.0 的动态权限处理以及Android 7.0的相机打开限制当然还有必不可少的MIUI系统坑和三星机器的图片旋转问题。
下面谈下解决方案:
**6.0动态权限处理:**
在Android 6.0 API 23开始Android开始引入动态权限处理即除了在之前的AndroidManifest.xml文件中申明权限还需要在使用到权限的时候弹出用户是否授权的框。只需要重写onRequestPermissionsResult方法即可。示例代码如下:
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/04d4b3f2fb8d5274b6a26b9431232761.writebug)
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/1c0a29c4d182f6ef9a408763afff70d3.writebug)
**对于调用系统相机拍照后图片旋转:**
经常会遇到一种情况拍照的时候看到照片是正的但是当APP获取到这张图片的时候却发现旋转了90度也有可能是180,270不过90度比较多见这应该是手机传感器导致的。为了解决这种不一定在所有机器上都出现的问题我们可以引入Android系统提供的ExifInterface类来解决各个属性的操作。ExifInterface可以不用加载图片就获取到图片的长宽、旋转角度等多种属性我们可以通过ExitInterface获取图片的旋转角度degree来进行处理当满足degree不为0的时候调用Matrix的postRotate进行角度旋转核心代码为
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/f863e8e7caaf8eb0f133b9a5502a2562.writebug)
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/a4f196e955629485da750c89158a98e7.writebug)
**对于部分机型调起相机会回不去APP的适配处理拍完照闪退问题**
这也是相机适配中必须处理的地方由于Android系统厂商的ROM不一致会让一些ROM对自带相机应用做优化当某个APP通过Intent进入相机拍照界面时系统会把这个APP最上层的Activity销毁回收只需要重写onSaveInstanceState和onRestoreInstanceState方法对数据进行恢复和保存即可核心代码为
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/a280d2e57f8fc040e10b31c10fe5c4f9.writebug)
**Android 7.0调用系统相机的处理:**
由于Android 7.0 手机开始推广所以我们也不得不处理7.0的权限问题。在Android 7.0 以后file:// 不被允许作为一个附件的Uri的意图否则会抛出FileUriExposedException在这样的情况下我们只需要用FileProvider即可解决。核心代码如下
```java
<provider
Android:authorities="${APPlicationId}.provider"
Android:name=".ImagePickerProvider"
Android:exported="false"
Android:grantUriPermissions="true">
<meta-data
Android:name="Android.support.FILE_PROVIDER_PATHS"
Android:resource="@xml/provider_paths"/>
</provider>
```
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/ed8147ee0b3c51bd74e80fcf5b91de82.writebug)
![](http://www.write-bug.com/myres/static/uploads/2021/10/19/9b955d7b60b281ecdbaff4b9c95a0a23.writebug)
# 7. 软件测试
基于Android等移动终端平台的APP软件测试与传统的软件测试不同它不仅要求兼容性良好而且要求响应时间要在一定的限制范围。比如用户的操作响应时间一般不能超过3-5秒APP启动时间也不能太长。而对于Android操作系统庞大的第三方厂商定制导致Android系统各有差异。一个APP软件必须满足不用的屏幕分辨率都能正常显示并且能够正确的完成相应功能。如果在某个环境下界面功能显示不全则会导致软件功能无法正确使用也就失去了安装此软件的意义所以对其兼容性的要求也是很重要的一个方面。
## 7.1 功能模块测试
功能模块的测试是最基本的测试。我通过找出APP的测试点然后采用两款手机小米3SAndroid 5.0和小米5SAndroid 7.0以及Windows抓包工具Fidder分别对“爱吖校推”的功能模块和网络接口进行完整测试在测试过程出现了几个小问题。
- 图片选择页面出现选择异常,而后得以解决,因为导包错误,导致指向了另外一个文件
- 发布信息后没有刷新页面的Bug后面采用广播提醒UI刷新得以解决
在解决完相关bug后进行了新一轮的测试下面是简单的测试情况
### 7.1.1 用户登录注册模块测试
该模块测试主要是验证用户的注册登录是否能正常使用任何不正确逻辑都应该给出相应的提示。在注册时手机号必须符合规范密码不得少于6位否则提示输入不规范。注册时需要输入两次密码并且密码相同验证码输入必须正确否则提示相应错误。登录板块第二次登录应该自动登录。
| 测试项目 | 测试方法 | 预期结果 | 结论 |
| ---- | ------------------------ | ------ | ------- |
| 用户注册 | 在注册界面输入用户名,密码,其他信息(符合要求) | 注册成功 | 与预期结果一致 |
| 用户注册 | 在注册界面不输入内容或者输入信息不符合要求 | 注册失败 | 与预期结果一致 |
| 用户登录 | 在登录界面输入用户名,密码,且用户名和密码匹配 | 登录成功 | 与预期结果一致 |
| 用户登录 | 在登录界面不输入内容或者输入信息不正确 | 登录失败 | 与预期结果一致 |
| 用户登录 | 没有退出当前账号,第二次进入该系统 | 自动登录成功 | 与预期结果一致 |
### 7.1.2 信息发布模块测试
该模块测试主要是验证能否正常发布信息和上传图片及微视频当没有输入信息时候应当不能点击发送按钮。附件上传前要注意压缩并且上传后应该在班圈信息中得到正常显示中间有任何出错需要提示相应错误。而且在6.0以上系统的手机应该动态申请权限。在发布通知或者作业页面,应当发起推送到该班级圈子下的家长手机中。
| 测试项目 | 测试方法 | 预期结果 | | 结论 |
| ---- | ---------------------------------------- | ---------------------------------------- | ---- | ------- |
| 信息发布 | 不输入任何文字点击发布 | 发布按钮不能点击 | | 与预期结果一致 |
| 信息发布 | 输入信息点击发布 | 发布成功,班圈显示刷新显示本条内容 | | 与预期结果一致 |
| 信息发布 | 点击图片上传,进入图片选择页面,选择后点击确定返回 | 选择图片后在信息发布页面能显示正常的图片信息,并且首次使用该功能应该弹出申请权限的对话框 | | 与预期结果一致 |
| 信息发布 | 点击微视频上传,进入微视频录制页面,点击上传后返回 | 信息发布页面正常显示该条微视频的缩略图,点击缩略图能正常播放视频,首次使用该功能应该弹出动态申请权限的对话框 | | 与预期结果一致 |
| 信息发布 | 发布信息查看Fidder抓包情况 | Fidder抓包信息应当显示和接口预期一致 | | 与预期结果一致 |
| 信息发布 | 发布班级通知或者作业的时候查看Fidder抓包情况和该班级圈子的家长用户手机情况 | Fidder抓包信息应该和接口预期一致并且该班级圈下的家长应该收到信息推送 | | 与预期结果一致 |
### 7.1.3 信息交流模块测试
该模块测试主要是测试信息能否正常地点赞评论回复,在该功能中,如果本用户之前未点赞(灰色),应当把点赞按钮置为点赞状态(红色),点赞数+1。点击班圈某条信息可以正常进入到该信息的详情页面并可以评论返回后正常显示相关信息。
| 信息交流 | 测试方法 | 预期结果 | 结论 |
| ---- | --------- | ------------------------------------- | ------- |
| 信息交流 | 点击班圈的某条信息 | 应该能正常进入详情页面 | 与预期结果一致 |
| 信息交流 | 点击点赞按钮 | 在未点赞的时候应该为灰色,点赞后应该为红色,可以取消点赞,相应数目应该变化 | 与预期结果一致 |
| 信息交流 | 点击评论按钮 | 进入信息详情页面,并且弹出键盘 | 与预期结果一致 |
| 信息交流 | 点击评论的人 | 应该直接开始弹出软键盘,并且置为回复该用户的状态 | 与预期结果一致 |
| 信息交流 | 点击返回 | 如果该条信息详情有所更新,应当提醒班级圈正常显示点赞情况和评论数目情况 | 与预期结果一致 |
### 7.1.4 即时通讯模块测试
即时通讯模块测试主要是测试添加好友音视频通话聊天发送附件好友列表等能否正常显示以及APP置于后台能否正常收到离线推送的即时通讯消息。
| 即时通讯 | 测试方法 | 预期结果 | 结论 |
| ---- | ------------------------ | ---------------------------------------- | ------- |
| 即时通讯 | 点击消息Tab | 能查看到最近联系人 | 与预期结果一致 |
| 即时通讯 | 点击联系人Tab | 能正常显示联系人相关信息 | 与预期结果一致 |
| 即时通讯 | 点击某条会话或者联系人 | 能正常进入聊天页面,并能正常显示信息和聊天 | 与预期结果一致 |
| 即时通讯 | 点击音视频通话 | 进入音视频通话页面被呼叫用户应当能正常收到此信息并可选择挂断发起者可以收到用户B接受或者拒绝的反馈若是接受应当正常进行音视频聊天 | 与预期结果一致 |
| 即时通讯 | 用户B应用置于后台用户A给用户B发送文本消息 | 用户B手机能收到信息推送 | 与预期结果一致 |
| 即时通讯 | 用户B应用置于后台用户A向用户B发起音视频呼叫 | 用户B应当直接呼起音视频通话页面并能选择接受或者拒绝 | 与预期结果一致 |
## 7.3 性能测试
性能测试需要验证APP在各种外界压力下是否能正确响应在执行单一操作时候的响应时间重复操作一功能系统资源占用情况我们在项目中采用了LeakCanary开源框架并把它移植到项目中检查内存泄漏情况。并且使用Android内存泄漏分析工具MemoryAnalyzer检测内存使用情况最终通过分析优化了下面两个方面
- 图片压缩不要将整个图片以Bitmap读入内存防止OOM的发生替换为ExitInterface类获取图片信息并采用BitmapFatory的decodeXXX方法以及Bitmap的createScaleBitmap进行尺寸压缩最后再进行质量压缩得以解决
- 项目中有些地方采用了static静态对象持有Context等导致内存久久不能释放后面替换了ApplicationContext得以解决
- 测试过程中发现启动白屏现象较为严重,所以增加闪屏页得以缓解
## 7.4 安全测试
随着移动互联网的飞速发展而作为产业模式下的移动平台自然备受关注依托此平台的APP的安全性进而成为人们的焦点。所以我对软件权限等进行了细致检查得到以下结果
- 没有任何的泄密权限或者非法访问情况
- 没有出现任何的自启动,没有捆绑其他任何软件
- 数据加密均正常,不存在泄密危险
## 7.5 交叉事件测试
交叉事件测试又叫事件或者冲突测试。意思是当APP在运行中与此同时被另外的事件干扰比如接入电话查看短信后是否会导致APP崩溃或者数据丢失等异常。如果执行干扰的冲突事件后应用APP依然能正常运行不会出现崩溃、终端死机或者丢失数据等问题则视为我们的交叉事件测试通过。
在交叉事件测试中,我着重检查了几个方面:
APP运行时前台后切换或者横竖屏切换出现了数据的丢失经过修改后得以解决
APP运行时能正常接收电话和短信
运行“爱吖校推”并不会影响其他功能的使用依然能正常的查看QQ消息、微信消息等。
## 7.6 兼容性测试
在Android众多的第三方定制系统的大背景下各种各样奇葩的兼容性问题一定存在虽然在我们开发中采用的测试真机是公认最容易出问题的MIUI手机但依然不能以偏概全在兼容性测试阶段我采用腾讯云真机租用做了基本所有定制系统的兼容性测试。在兼容性测试中我着重处理了
Android 7.0 后不能直接通过Uri调用系统相机检查出问题后采用了文件FileProvider得以解决。
在三星手机的测试中出现了拍照后旋转问题最后在代码中通过ExitInterface等操作解决了这个问题。
# 8. 结论
本次毕业设计针对越来越被看好的“互联网+”教育着眼于促进教育现代化发展加强学校与家长的沟通交流。设计过程中采用较多的Design美学理念和动画效果增加用户粘性。提供推送服务极大的满足了用户不丢失重要班级信息。社区化的设计帮助用户群体更好的交流。
由于各方面的原因和经验匮乏等问题本应用的一些细节处理还不那么完美但我依然会完善下去。开发这款应用让我学到很多比如很多当前Android火热的框架Retrofit、Rx、即时通讯、推送以及图片压缩等尤其是后台板块的学习PHP作为当前比较热门的语言我直接从零学习到一步一步搭建起自己的后台收获巨大。
# 参考文献
[1] 明日科技Android从入门到精通[M]北京清华大学出版社2012.9
[2] 郭霖.第二行代码[M]北京清华大学出版社2016.11
[3] 李刚疯狂Android讲义第3版[M]北京电子工业出版社2015.
[4] 郭金尚Android经典项目案例开发实战宝典[M]北京清华大学出版社2013.9
[5] 刘金桥. 基于web的贝佳宠物医院管理系统设计与实现 2015-06-03
[6] 许瑾.第一次开发Android程序的历程[J]. 科技资讯2014.29.20
[7] 丁丽萍Android操作系统的安全性分析[J]信息网络安全2012.358-60
[8] 王珊.数据库系统概论.北京:电子工业出版社,2015
[9] (美)赞德斯彻.深入PHP面向对象、模式与实践第3版[M].机械工业出版社,2009.4
[10] 杨宇.PHP典型模块与项目实战大全[M].清华大学出版社,2012.1
[11] (美)林恩.贝伊利,迈克尔·莫里森着苏金国徐阳译OReilly:HeadFirst PHP &MySQL(中文版)中国电力出版社2010 386
[12] 马千里. 基于安卓手机的“视界”应用程序的设计和实现2016-05-31

BIN
文档/API接口文档.docx Normal file

Binary file not shown.

17
文档/Android/.gitattributes vendored Normal file
View File

@ -0,0 +1,17 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

1
文档/Android/app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

294
文档/Android/app/app.iml Normal file
View File

@ -0,0 +1,294 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="AiYaSchoolPush" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/SMSSDK-2.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.afollestad.material-dialogs/commons/0.9.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.afollestad.material-dialogs/core/0.9.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test.espresso/espresso-core/2.2.2/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test.espresso/espresso-idling-resource/2.2.2/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test/exposed-instrumentation-api-publish/0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test/rules/0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support.test/runner/0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/cardview-v7/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/gridlayout-v7/24.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/multidex-instrumentation/1.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/multidex/1.0.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-compat/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-ui/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-core-utils/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-fragment/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-media-compat/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v13/25.0.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/transition/25.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.baoyz.pullrefreshlayout/library/1.2.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.bigkoo/quicksidebar/1.0.3/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.daimajia.numberprogressbar/library/1.2/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.daimajia.slider/library/1.1.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.elyeproj.libraries/loaderviewlibrary/1.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.github.armcha/SpaceNavigationView/1.4.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.github.chrisbanes.photoview/library/1.2.4/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.github.mancj/MaterialSearchBar/0.3.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.github.nanchen2251/CompressHelper/1.0.2/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.github.sd6352051.niftydialogeffects/niftydialogeffects/1.0.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.jcodecraeer/xrecyclerview/1.2.7/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.lzy.net/okgo/2.1.4/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.lzy.widget/imagepicker/0.5.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.lzy.widget/ninegridview/0.2.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.mabeijianxi/small-video-record/1.0.8/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.marshalchen.ultimaterecyclerview/library/0.7.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.philliphsu/bottomsheetpickers/2.0.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.qiyukf.unicorn/unicorn/2.9.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.rengwuxian.materialedittext/library/2.1.4/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.squareup.leakcanary/leakcanary-android-no-op/1.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.squareup.leakcanary/leakcanary-android/1.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.timehop.stickyheadersrecyclerview/library/0.4.3/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/de.hdodenhof/circleimageview/2.1.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/de.keyboardsurfer.android.widget/crouton/1.8.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/in.srain.cube/ultra-ptr/1.0.11/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/io.reactivex/rxandroid/1.2.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/jp.wasabeef/recyclerview-animators/2.2.3/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/me.wangyuwei/ParticleView/1.0.5/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/me.zhanghai.android.materialprogressbar/library/1.3.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/multi-dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" scope="TEST" name="runner-0.5" level="project" />
<orderEntry type="library" exported="" name="commons-0.9.1.0" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="espresso-idling-resource-2.2.2" level="project" />
<orderEntry type="library" exported="" name="library-0.7.0" level="project" />
<orderEntry type="library" exported="" name="ParticleView-1.0.5" level="project" />
<orderEntry type="library" exported="" name="recyclerview-animators-2.2.3" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-library-1.3" level="project" />
<orderEntry type="library" exported="" name="leakcanary-analyzer-1.5" level="project" />
<orderEntry type="library" exported="" name="transition-25.1.1" level="project" />
<orderEntry type="library" exported="" name="ultra-ptr-1.0.11" level="project" />
<orderEntry type="library" exported="" name="support-v13-25.0.0" level="project" />
<orderEntry type="library" exported="" name="library-1.1.5" level="project" />
<orderEntry type="library" exported="" name="gridlayout-v7-24.2.1" level="project" />
<orderEntry type="library" exported="" name="imagepicker-0.5.5" level="project" />
<orderEntry type="library" exported="" name="core-0.9.1.0" level="project" />
<orderEntry type="library" exported="" name="library-1.3.0" level="project" />
<orderEntry type="library" exported="" name="parse-android-1.13.0" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="multidex-instrumentation-1.0.1" level="project" />
<orderEntry type="library" exported="" name="library-2.4.0" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="exposed-instrumentation-api-publish-0.5" level="project" />
<orderEntry type="library" exported="" name="MaterialSearchBar-0.3.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="javax.annotation-api-1.2" level="project" />
<orderEntry type="library" exported="" name="adapter-rxjava-2.1.0" level="project" />
<orderEntry type="library" exported="" name="ninegridview-0.2.0" level="project" />
<orderEntry type="library" exported="" name="rxjava-1.2.0" level="project" />
<orderEntry type="library" exported="" name="circleimageview-2.1.0" level="project" />
<orderEntry type="library" exported="" name="SMSSDK-2.1.1-" level="project" />
<orderEntry type="library" exported="" name="support-v4-25.1.1" level="project" />
<orderEntry type="library" exported="" name="support-compat-25.1.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="javawriter-2.1.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="leakcanary-android-no-op-1.5" level="project" />
<orderEntry type="library" exported="" name="library-1.2" level="project" />
<orderEntry type="library" exported="" name="support-media-compat-25.1.1" level="project" />
<orderEntry type="library" exported="" name="quicksidebar-1.0.3" level="project" />
<orderEntry type="library" exported="" name="MobTools-2016.0714.1402" level="project" />
<orderEntry type="library" exported="" name="support-annotations-25.1.1" level="project" />
<orderEntry type="library" exported="" name="support-vector-drawable-25.1.1" level="project" />
<orderEntry type="library" exported="" name="okio-1.9.0" level="project" />
<orderEntry type="library" exported="" name="niftydialogeffects-1.0.0" level="project" />
<orderEntry type="library" exported="" name="CompressHelper-1.0.2" level="project" />
<orderEntry type="library" exported="" name="haha-2.0.3" level="project" />
<orderEntry type="library" exported="" name="picasso-2.5.2" level="project" />
<orderEntry type="library" exported="" name="library-0.4.3" level="project" />
<orderEntry type="library" exported="" name="xrecyclerview-1.2.7" level="project" />
<orderEntry type="library" exported="" name="mobAPI-1.0.6" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-integration-1.3" level="project" />
<orderEntry type="library" exported="" name="design-25.1.1" level="project" />
<orderEntry type="library" exported="" name="okgo-2.1.4" level="project" />
<orderEntry type="library" exported="" name="small-video-record-1.0.8" level="project" />
<orderEntry type="library" exported="" name="SpaceNavigationView-1.4.1" level="project" />
<orderEntry type="library" exported="" name="bolts-tasks-1.4.0" level="project" />
<orderEntry type="library" exported="" name="leakcanary-watcher-1.5" level="project" />
<orderEntry type="library" exported="" name="cardview-v7-25.1.1" level="project" />
<orderEntry type="library" exported="" name="library-1.2.4" level="project" />
<orderEntry type="library" exported="" name="support-core-ui-25.1.1" level="project" />
<orderEntry type="library" exported="" name="leakcanary-android-1.5" level="project" />
<orderEntry type="library" exported="" name="library-1.2.0" level="project" />
<orderEntry type="library" exported="" name="glide-3.7.0" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="jsr305-2.0.1" level="project" />
<orderEntry type="library" exported="" name="support-core-utils-25.1.1" level="project" />
<orderEntry type="library" exported="" name="support-fragment-25.1.1" level="project" />
<orderEntry type="library" exported="" name="MobCommons-2016.0714.1402" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="espresso-core-2.2.2" level="project" />
<orderEntry type="library" exported="" name="unicorn-2.9.0" level="project" />
<orderEntry type="library" exported="" name="fastjson-1.2.20" level="project" />
<orderEntry type="library" exported="" name="library-2.1.4" level="project" />
<orderEntry type="library" exported="" name="loaderviewlibrary-1.2.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="rules-0.5" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="javax.inject-1" level="project" />
<orderEntry type="library" exported="" name="retrofit-2.1.0" level="project" />
<orderEntry type="library" exported="" name="gson-2.7" level="project" />
<orderEntry type="library" exported="" name="converter-gson-2.1.0" level="project" />
<orderEntry type="library" exported="" name="rxandroid-1.2.1" level="project" />
<orderEntry type="library" exported="" name="eventbus-3.0.0" level="project" />
<orderEntry type="library" exported="" name="multidex-1.0.1" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
<orderEntry type="library" exported="" name="okhttp-3.4.1" level="project" />
<orderEntry type="library" exported="" name="crouton-1.8.5" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-25.1.1" level="project" />
<orderEntry type="library" exported="" name="recyclerview-v7-25.1.1" level="project" />
<orderEntry type="library" exported="" name="bottomsheetpickers-2.0.0" level="project" />
<orderEntry type="library" exported="" name="animated-vector-drawable-25.1.1" level="project" />
<orderEntry type="module" module-name="easeUI_CN" exported="" />
<orderEntry type="library" exported="" name="play-services-base-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-clearcut-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-database-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-wearable-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-drive-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-storage-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-common-9.2.1" level="project" />
<orderEntry type="library" exported="" name="glide-3.6.1" level="project" />
<orderEntry type="library" exported="" name="play-services-tagmanager-api-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-wallet-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-iid-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-vision-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-gcm-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-messaging-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-auth-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-gass-9.2.1" level="project" />
<orderEntry type="library" exported="" name="MiPush_SDK_Client_2_2_19" level="project" />
<orderEntry type="library" exported="" name="play-services-tasks-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-cast-framework-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-cast-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-config-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-database-connection-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-panorama-9.2.1" level="project" />
<orderEntry type="library" exported="" name="HwPush_SDK_V2705_nomap" level="project" />
<orderEntry type="library" exported="" name="play-services-appinvite-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-auth-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-location-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-ads-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-analytics-impl-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-ads-lite-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-fitness-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-contextmanager-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-nearby-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-plus-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-analytics-impl-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-auth-module-9.2.1" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.0.0" level="project" />
<orderEntry type="library" exported="" name="play-services-appindexing-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-auth-common-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-storage-common-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-maps-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-games-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-identity-9.2.1" level="project" />
<orderEntry type="library" exported="" name="hyphenatechat_3.2.2" level="project" />
<orderEntry type="library" exported="" name="play-services-auth-base-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-analytics-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-iid-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-basement-9.2.1" level="project" />
<orderEntry type="library" exported="" name="mediarouter-v7-23.0.0" level="project" />
<orderEntry type="library" exported="" name="firebase-crash-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-safetynet-9.2.1" level="project" />
<orderEntry type="library" exported="" name="play-services-places-9.2.1" level="project" />
<orderEntry type="library" exported="" name="org.apache.http.legacy" level="project" />
<orderEntry type="library" exported="" name="play-services-tagmanager-9.2.1" level="project" />
<orderEntry type="library" exported="" name="firebase-analytics-9.2.1" level="project" />
</component>
</module>

View File

@ -0,0 +1,181 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.example.nanchen.aiyaschoolpush"
minSdkVersion 16
targetSdkVersion 22
versionCode 25
versionName "2.3.1"
multiDexEnabled true
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// dex优化
zipAlignEnabled false
//
minifyEnabled false
//
shrinkResources false
//
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// productFlavors {
// android.applicationVariants.all { variant ->
// variant.outputs.each { output ->
// output.outputFile = new File("AiYa-SchoolPush" + "-v" +
// defaultConfig.versionName + ".apk");
// }
// }
// }
// lint检查的error
lintOptions {
abortOnError false
}
dexOptions {
javaMaxHeapSize "4g"
}
// 使
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
defaultConfig {
ndk {
abiFilters 'armeabi-v7a'
}
}
}
repositories {
flatDir {
dirs 'libs' //aar的目录地址
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.1.1'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
// mob message
compile name: 'SMSSDK-2.1.1', ext: 'aar'
compile files('libs/MobTools-2016.0714.1402.jar')
compile files('libs/MobCommons-2016.0714.1402.jar')
// material design
compile 'com.android.support:design:25.1.1'
compile 'com.android.support:cardview-v7:25.1.1'
// photo
compile 'com.squareup.picasso:picasso:2.5.2'
// rx
compile 'io.reactivex:rxjava:1.2.0'
compile 'io.reactivex:rxandroid:1.2.1'
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
// bottom tab
compile 'com.github.armcha:SpaceNavigationView:1.4.1'
//
compile 'com.bigkoo:quicksidebar:1.0.3'
compile 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar'
//
compile 'com.nineoldandroids:library:2.4.0'
compile 'com.daimajia.slider:library:1.1.5@aar'
// Toast
compile('de.keyboardsurfer.android.widget:crouton:1.8.5@aar') {
// exclusion is not neccessary, but generally a good idea.
exclude group: 'com.google.android', module: 'support-v4'
}
//dialog
compile 'com.nineoldandroids:library:2.4.0'
compile 'com.github.sd6352051.niftydialogeffects:niftydialogeffects:1.0.0@aar'
//
compile 'me.wangyuwei:ParticleView:1.0.5'
//
compile 'com.elyeproj.libraries:loaderviewlibrary:1.2.1'
// Dialog弹出方式
compile 'com.nineoldandroids:library:2.4.0'
compile 'com.github.sd6352051.niftydialogeffects:niftydialogeffects:1.0.0@aar'
//
compile 'com.github.mancj:MaterialSearchBar:0.3.1'
// RecyclerView
compile 'com.marshalchen.ultimaterecyclerview:library:0.7.0'
//
compile 'com.daimajia.numberprogressbar:library:1.2@aar'
//
compile 'com.philliphsu:bottomsheetpickers:2.0.0'
// RecyclerView
compile 'com.jcodecraeer:xrecyclerview:1.2.7'
// Material Design风格的输入框开源
compile 'com.rengwuxian.materialedittext:library:2.1.4'
//
compile files('libs/parse-android-1.13.0.jar')
compile files('libs/bolts-tasks-1.4.0.jar')
// LeakCanary
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
// multidex
compile 'com.android.support:multidex:1.0.1'
//
compile 'com.qiyukf.unicorn:unicorn:2.9.0'
compile 'com.alibaba:fastjson:1.2.20'
// 访okgo okhttp
compile 'com.lzy.net:okgo:2.1.4'
//使 +
// image
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'org.greenrobot:eventbus:3.0.0'
// 仿
compile 'com.lzy.widget:ninegridview:0.2.0'
compile 'com.lzy.widget:imagepicker:0.5.5'
//
// material dialog
compile 'com.afollestad.material-dialogs:commons:0.9.1.0'
compile 'com.afollestad.material-dialogs:core:0.9.1.0'
compile project(':easeUI_CN')
compile 'com.mabeijianxi:small-video-record:1.0.8'
compile 'com.baoyz.pullrefreshlayout:library:1.2.0'
compile 'com.github.nanchen2251:CompressHelper:1.0.2'
}
repositories {
maven {
url 'https://dl.bintray.com/wangyuwei/maven'
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

17
文档/Android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Users\Administrator\AppData\Local\Android\Sdk1\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 *;
#}

View File

@ -0,0 +1,13 @@
package com.example.nanchen.aiyaschoolpush;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
</manifest>

View File

@ -0,0 +1,454 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.nanchen.aiyaschoolpush">
<!-- SMSSDK需要的权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 拍照权限 -->
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- 自动轮播 -->
<!-- if you want to load images from a file OR from the internet -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 环信开始 -->
<!-- Required -->
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- 环信结束 -->
<!-- 华为推送开始 -->
<!--<uses-permission android:name="android.permission.READ_PHONE_STATE"/>-->
<!--<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>-->
<!--<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>-->
<!--<uses-permission android:name="android.permission.WAKE_LOCK"/>-->
<!-- 华为推送结束 -->
<!-- 小米推送 Start -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<permission
android:name="com.example.nanchen.aiyaschoolpush.permission.MIPUSH_RECEIVE"
android:protectionLevel="signature"/>
<!-- 这里com.xiaomi.mipushdemo改成app的包名 -->
<uses-permission android:name="com.example.nanchen.aiyaschoolpush.permission.MIPUSH_RECEIVE"/>
<!-- 这里com.xiaomi.mipushdemo改成app的包名 -->
<!-- 小米推送 end -->
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/icon"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar">
<!-- 设置环信应用的AppKey -->
<meta-data
android:name="EASEMOB_APPKEY"
android:value="15680802251#aiyaschoolpush"/>
<!-- 声明SDK所需的service SDK核心功能 -->
<service
android:name="com.hyphenate.chat.EMChatService"
android:exported="true"/>
<!-- 声明SDK所需的receiver -->
<receiver android:name="com.hyphenate.chat.EMMonitorReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<data android:scheme="package"/>
</intent-filter>
<!-- 可选filter -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.USER_PRESENT"/>
</intent-filter>
</receiver>
<!--华为推送 Start -->
<!--<receiver android:name="com.huawei.push.receiver.MyPushReceiver" >-->
<!--<intent-filter>-->
<!--<action android:name="com.huawei.android.push.intent.REGISTRATION" />-->
<!--<action android:name="com.huawei.android.push.intent.RECEIVE" />-->
<!--<action android:name="com.huawei.android.push.intent.CLICK" />-->
<!--<action android:name="com.huawei.intent.action.PUSH_STATE" />-->
<!--</intent-filter>-->
<!--<meta-data-->
<!--android:name="CS_cloud_ablitity"-->
<!--android:value="@string/hwpush_ability_value" />-->
<!--</receiver>-->
<!--<receiver android:name="com.huawei.hms.support.api.push.PushEventReceiver" >-->
<!--<intent-filter>-->
<!--<action android:name="com.huawei.intent.action.PUSH" />-->
<!--</intent-filter>-->
<!--</receiver>-->
<!--华为推送 End -->
<!-- =========== 小米推送 start ================ -->
<service
android:name="com.xiaomi.push.service.XMPushService"
android:enabled="true"
android:process=":pushservice"/>
<service
android:name="com.xiaomi.push.service.XMJobService"
android:enabled="true"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
android:process=":pushservice"/>
<!--此service必须在3.0.1版本以后包括3.0.1版本)加入 -->
<service
android:name="com.xiaomi.mipush.sdk.PushMessageHandler"
android:enabled="true"
android:exported="true"/>
<service
android:name="com.xiaomi.mipush.sdk.MessageHandleService"
android:enabled="true"/>
<!--此service必须在2.2.5版本以后包括2.2.5版本)加入 -->
<receiver
android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</receiver>
<receiver
android:name="com.xiaomi.push.service.receivers.PingReceiver"
android:exported="false"
android:process=":pushservice">
<intent-filter>
<action android:name="com.xiaomi.push.PING_TIMER"/>
</intent-filter>
</receiver>
<!-- 这个接收器是自己编写继承 -->
<receiver
android:name=".helper.receiver.MiMessageReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE"/>
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED"/>
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.ERROR"/>
</intent-filter>
</receiver>
<!-- =========== 小米推送 end ================ -->
<!-- 闪屏页面 -->
<activity
android:name=".ui.activity.SplashActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- SMSSDK所必须的Activity申明 -->
<activity
android:name="com.mob.tools.MobUIShell"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:windowSoftInputMode="stateHidden|adjustResize"/>
<activity
android:name=".ui.activity.PlayVideoActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 主页面 -->
<activity
android:name=".ui.activity.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan">
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.MAIN"/> -->
<!-- <category android:name="android.intent.category.LAUNCHER"/> -->
<!-- </intent-filter> -->
</activity>
<!-- 引导页 -->
<activity
android:name=".ui.activity.GuideActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 登录页 -->
<activity
android:name=".ui.activity.LoginActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 注册页1 -->
<activity
android:name=".ui.activity.RegisterActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 注册页2 -->
<activity
android:name=".ui.activity.RegisterActivity2"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.MAIN"/> -->
<!-- <category android:name="android.intent.category.LAUNCHER"/> -->
<!-- </intent-filter> -->
</activity>
<!-- 关于我们 -->
<activity
android:name=".ui.activity.AboutActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 重置密码、忘记密码 -->
<activity
android:name=".ui.activity.ResetPwdActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 孩子信息 -->
<activity
android:name=".ui.activity.ChildInfoActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 发布信息专用 -->
<activity
android:name=".ui.activity.ReleaseActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 个人信息及修改 -->
<activity
android:name=".ui.activity.PersonalInfoActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 专为装网页的Activity -->
<activity
android:name=".ui.activity.WebActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait">
</activity>
<!-- 查看详情页 -->
<activity
android:name=".ui.activity.LookDetailActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
android:name=".ui.activity.ReleaseCommunityActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.ChatActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.VideoCallActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.VoiceCallActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.NewFriendsMsgActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.GroupsActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.PublicChatRoomsActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.AddContactActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.PickAtUserActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.ContextMenuActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.GroupDetailsActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.ExitGroupDialogActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.GroupBlacklistActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.EditActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.GroupSearchMessageActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.GroupPickContactsActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.UserProfileActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.ImageGridActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.RecorderVideoActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.NewGroupActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.PublicGroupsActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.GroupSimpleDetailActivity"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".ui.activity.PublicGroupsSearchActivity"
android:screenOrientation="portrait">
</activity>
<!-- 下载大图 -->
<activity
android:name="com.hyphenate.easeui.ui.EaseShowBigImageActivity"
android:screenOrientation="portrait"/>
<!-- 下载文件 -->
<activity
android:name="com.hyphenate.easeui.ui.EaseShowNormalFileActivity"
android:screenOrientation="portrait"
android:theme="@style/horizontal_slide"/>
<!-- 地图 -->
<activity
android:name="com.hyphenate.easeui.ui.EaseBaiduMapActivity"
android:screenOrientation="portrait"
android:theme="@style/horizontal_slide"/>
<!-- 看视频 -->
<activity
android:name="com.hyphenate.easeui.ui.EaseShowVideoActivity"
android:screenOrientation="portrait"
android:theme="@style/horizontal_slide"
android:windowSoftInputMode="stateAlwaysHidden"/>
<activity
android:name="com.lzy.imagepicker.ui.ImagePreviewDelActivity"
android:theme="@style/ImagePickerThemeFullScreen"/>
<receiver android:name=".helper.receiver.AvatarReceiver">
<intent-filter>
<action android:name="com.nanchen.android.AVATAR_ACTION"/>
</intent-filter>
</receiver>
<service
android:name=".net.NetworkStateService"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="com.text.service.NetworkStateService"/>
</intent-filter>
</service>
<activity android:name="mabeijianxi.camera.MediaRecorderActivity"/>
<activity android:name=".SendSmallVideoActivity"/>
<activity android:name=".VideoPlayerActivity"/>
</application>
</manifest>

Binary file not shown.

View File

@ -0,0 +1,209 @@
package com.example.nanchen.aiyaschoolpush;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Application;
import android.content.Context;
import android.os.Environment;
import android.os.Process;
import android.support.multidex.MultiDex;
import android.util.Log;
import com.example.nanchen.aiyaschoolpush.helper.DemoHelper;
import com.example.nanchen.aiyaschoolpush.helper.QiYuCloudServerHelper;
import com.lzy.imagepicker.ImagePicker;
import com.lzy.imagepicker.view.CropImageView;
import com.lzy.ninegrid.NineGridView;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.cookie.store.PersistentCookieStore;
import com.mob.mobapi.MobAPI;
import com.squareup.leakcanary.LeakCanary;
import com.xiaomi.channel.commonutils.logger.LoggerInterface;
import com.xiaomi.mipush.sdk.Logger;
import com.xiaomi.mipush.sdk.MiPushClient;
import java.io.File;
import java.util.List;
import java.util.logging.Level;
import cn.smssdk.SMSSDK;
import mabeijianxi.camera.VCamera;
import mabeijianxi.camera.util.DeviceUtils;
/**
* 启动的Application
*
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/09/08 15:51
*/
public class App extends Application {
private static final String MSG_APP_KEY = "16faeb1248a89";// 短信验证的app_key
private static final String MSG_APP_SECRET = "20d994397ced27b44b48ce80956a6f9d";// 短信验证的app_secret
private static final String MOB_APP_KEY = "1730bae762bbc";// MobApi的应用app_key
private static final String TAG = "App";
private static final String MIPUSH_APP_KEY = "5681752153371"; // 小米推送App_key
private static final String MIPUSH_APP_ID = "2882303761517521371"; //小米推送app_id
private static App app;
public static App getInstance() {
return app;
}
@Override
public void onCreate() {
super.onCreate();
app = this;
// LeakCanary
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// 初始化小米推送相关
initMiPush();
// 初始化短信验证SDK
SMSSDK.initSDK(this, MSG_APP_KEY, MSG_APP_SECRET);
// 初始化MobApiSDK
MobAPI.initSDK(getApplicationContext(), MOB_APP_KEY);
//init demo helper
DemoHelper.getInstance().init(App.getAppContext());
// 七鱼客服初始化
QiYuCloudServerHelper.initCloudServer(this);
// OkGo初始化
OkGo.init(this);
OkGo.getInstance().debug("okgo", Level.WARNING,true)
.setConnectTimeout(20000) //全局的连接超时时间
.setReadTimeOut(20000) //全局的读取超时时间
.setWriteTimeOut(20000) //全局的写入超时时间
.setCookieStore(new PersistentCookieStore());
// NineGridView的图片加载方式初始化
NineGridView.setImageLoader(new PicassoImageLoader());
initImagePicker(); // 初始化ImagePicker
// 小视频
// try {
// // 不知道小视频为什么不可用
// initSmallVideo(this);
// } catch (Exception e) {
// e.printStackTrace();
// throw new VideoException("当前手机暂不支持微视频");
// }
}
public static void initSmallVideo(Context context){
// 设置拍摄视频缓存路径
File dcim = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
if (DeviceUtils.isZte()) {
if (dcim.exists()) {
VCamera.setVideoCachePath(dcim + "/mabeijianxi/");
} else {
VCamera.setVideoCachePath(dcim.getPath().replace("/sdcard/",
"/sdcard-ext/")
+ "/mabeijianxi/");
}
} else {
VCamera.setVideoCachePath(dcim + "/mabeijianxi/");
}
VCamera.setDebugMode(true);
try{
VCamera.initialize(context);
} catch (Exception e){
e.printStackTrace();
throw new VideoException("当前手机暂不支持微视频");
}
}
private void initImagePicker() {
ImagePicker imagePicker = ImagePicker.getInstance();
imagePicker.setImageLoader(new GlideImageLoader()); //设置图片加载器
imagePicker.setShowCamera(true); //显示拍照按钮
imagePicker.setCrop(true); //允许裁剪单选才有效
imagePicker.setSaveRectangle(true); //是否按矩形区域保存
imagePicker.setSelectLimit(9); //选中数量限制
imagePicker.setStyle(CropImageView.Style.RECTANGLE); //裁剪框的形状
imagePicker.setFocusWidth(800); //裁剪框的宽度单位像素圆形自动取宽高最小值
imagePicker.setFocusHeight(800); //裁剪框的高度单位像素圆形自动取宽高最小值
imagePicker.setOutPutX(1000);//保存文件的宽度单位像素
imagePicker.setOutPutY(1000);//保存文件的高度单位像素
}
/**
* 初始化小米推送相关
*/
private void initMiPush() {
//初始化push推送服务
if (shouldInit()) {
MiPushClient.registerPush(this, MIPUSH_APP_ID, MIPUSH_APP_KEY);
}
LoggerInterface newLogger = new LoggerInterface() {
@Override
public void setTag(String tag) {
// ignore
}
@Override
public void log(String content, Throwable t) {
Log.d(TAG, content, t);
}
@Override
public void log(String content) {
Log.d(TAG, content);
}
};
Logger.setLogger(this, newLogger);
}
private boolean shouldInit() {
ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
String mainProcessName = getPackageName();
int myPid = Process.myPid();
for (RunningAppProcessInfo info : processInfos) {
if (info.pid == myPid && mainProcessName.equals(info.processName)) {
return true;
}
}
return false;
}
/**
* 获取Application Context
*/
public static Context getAppContext() {
return app != null ? app.getApplicationContext() : null;
}
public static String currentUserNick = "";
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}

View File

@ -0,0 +1,303 @@
package com.example.nanchen.aiyaschoolpush;
import com.example.nanchen.aiyaschoolpush.config.Consts;
import com.example.nanchen.aiyaschoolpush.model.PraiseModel;
import com.example.nanchen.aiyaschoolpush.model.User;
import com.example.nanchen.aiyaschoolpush.model.info.InfoModel;
import com.example.nanchen.aiyaschoolpush.model.info.UserModel;
import com.example.nanchen.aiyaschoolpush.net.okgo.JsonCallback;
import com.example.nanchen.aiyaschoolpush.net.okgo.LslResponse;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.StringCallback;
import com.lzy.okgo.request.PostRequest;
import java.io.File;
import java.util.HashMap;
import java.util.List;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.api
* @date 2016/11/09 11:19
*/
public class AppService {
private static final String TAG = "AppService";
private static AppService instance;
private User mCurrentUser;
private AppService(){
}
public static AppService getInstance(){
if (instance == null){
instance = new AppService();
}
return instance;
}
/**
* 退出登录时重置此类
*/
public static void resetInstance(){
instance = null;
}
/**
* 获取当前登录的用户
* @return 当前用户
*/
public User getCurrentUser(){
return mCurrentUser;
}
public void setCurrentUser(User user){
this.mCurrentUser = user;
}
/*************** 用户系统 Begin ******************/
/**
* 用户手机号验证是否已经注册
* @param mobile 手机号
* @param callback 回调
*/
public void isUsableMobileAsync(String mobile, JsonCallback<LslResponse<User>> callback){
String url = Consts.API_SERVICE_HOST+"/user/usable_mobile.php?mobile="+mobile;
OkGo.get(url).execute(callback);
}
/**
* 用户注册
* @param username 用户名
* @param password 密码
* @param nickname 昵称
* @param birthday 生日
* @param callback 回调
*/
public void registerAsync(String username,String password,String nickname,String birthday,String iconUrl,JsonCallback<LslResponse<User>> callback){
String url = Consts.API_SERVICE_HOST + "/user/register.php";
HashMap<String,String> postParams = new HashMap<>();
postParams.put("username",username);
postParams.put("password",password);
postParams.put("nickname",nickname);
postParams.put("birthday",birthday);
postParams.put("avatar",iconUrl);
OkGo.post(url).params(postParams).execute(callback);
}
/**
* 异步用户登录
* @param username 用户名
* @param password 用户密码
* @param callback 回调
*/
public void loginAsync(String username,String password,JsonCallback<LslResponse<User>> callback){
String url = Consts.API_SERVICE_HOST + "/user/login.php";
HashMap<String,String> postParams = new HashMap<>();
postParams.put("username",username);
postParams.put("password",password);
OkGo.post(url).params(postParams).execute(callback);
}
/**
* 异步用户修改密码
* @param username 用户名
* @param password 新密码
* @param callback 回调
*/
public void resetPwdAsync(String username,String password,JsonCallback<LslResponse<User>> callback){
String url = Consts.API_SERVICE_HOST + "/user/reset_pwd.php";
HashMap<String,String> postParams = new HashMap<>();
postParams.put("username",username);
postParams.put("password",password);
OkGo.post(url).params(postParams).execute(callback);
}
/**
* 异步用户头像上传
* @param file 文件
* @param callback 回调
*/
public void upLoadAvatarAsync(File file,JsonCallback<LslResponse<User>> callback){
String url = Consts.API_SERVICE_HOST + "/user/avatar.php";
OkGo.post(url).params("avatar",file,file.getName()).execute(callback);
}
/**
* 异步用户头像上传
* @param file 文件
* @param callback 回调 返回json字符串只是为了测试
*/
public void upLoadAvatarAsync(File file, StringCallback callback){
String url = Consts.API_SERVICE_HOST + "/user/avatar.php";
OkGo.post(url).params("avatar",file).execute(callback);
}
/**
* 更新用户头像信息
*
* @param username 用户名
* @param iconUrl 头像地址
* @param type 传递类型
* @param callback 回调
*/
public void updateAvatarUrlAsync(String username,String iconUrl,int type,JsonCallback<LslResponse<User>> callback){
String url = Consts.API_SERVICE_HOST+"/user/update_avatar.php?username="+username
+"&iconUrl="+iconUrl+"&type="+type;
OkGo.get(url).execute(callback);
}
/**
* 更新用户信息
* @param username 用户名
* @param action 更新项
* @param value 更新值
* @param callback 回调
*/
public void updateUserInfoAsync(String username,String action,String value,JsonCallback<LslResponse<Object>> callback){
String url = Consts.API_SERVICE_HOST+"/user/update_user.php?username="+username
+"&action="+action+"&value="+value;
OkGo.get(url).execute(callback);
}
/*************** 用户系统 End ******************/
/*************** 信息系統 Begin ******************/
/**
* 异步获取信息
* @param classId 班级id
* @param username 用户名 用于返回用户是否赞了主贴
* @param infoType 信息类型 1 公告 2 作业 3 动态
* @param start 信息从第几行开始提取
* @param count 信息数目 这里一次获取10条 需要更多获取联系后台
* @param callback 回调
*/
public void getNoticeAsync(int classId,String username, int infoType,int start, int count,int lastId, JsonCallback<LslResponse<List<InfoModel>>> callback){
String url = Consts.API_SERVICE_HOST+"/info/info_main.php?classId="+classId+
"&username="+username+"&infoType="+infoType+"&start="+start+"&count="+count+"&lastId="+lastId;
OkGo.get(url).execute(callback);
}
/**
* 异步获取发布信息的用户信息
* @param username 用户名
* @param callback 回调
*/
public void getUserInfoAsync(String username, JsonCallback<LslResponse<UserModel>> callback){
String url = Consts.API_SERVICE_HOST+"/info/get_user.php?username="+username;
OkGo.get(url).execute(callback);
}
/**
* 异步插入数据到评论
* @param mainId 信息表条目id
* @param username 发布人id
* @param content 发布内容
* @param reply 回复的人的id
* @param callback 回调
*/
public void insertCommentAsync(int mainId, String username, String content,
String reply, JsonCallback<LslResponse<Object>> callback){
String url = Consts.API_SERVICE_HOST+"/info/insert_comment.php";
HashMap<String,String> postParams = new HashMap<>();
postParams.put("mainId",mainId+"");
postParams.put("username",username);
postParams.put("content",content);
postParams.put("reply",reply);
OkGo.post(url).params(postParams).execute(callback);
}
/**
* 更新点赞的信息
* @param mainId 主贴的id
* @param username 用户名 - 用户只能删除自己发布的信息
* @param callback 回调接口
*/
public void updatePraiseAsync(int mainId, String username, JsonCallback<LslResponse<PraiseModel>> callback){
String url = Consts.API_SERVICE_HOST+"/info/praise.php?mainId="+mainId+"&username="+username;
OkGo.get(url).execute(callback);
}
/**
* 异步发送消息给服务器
* @param classId 班级id
* @param username 发布人用户名
* @param infoType 信息类型 1 公告 2 作业 3 动态
* @param content 发布内容
* @param picUrls 图片地址
* @param callback 回调
*/
public void addMainInfoAsync(int classId, String username, int infoType, String content, List<String> picUrls,boolean isVideo, JsonCallback<LslResponse<InfoModel>> callback){
String url = Consts.API_SERVICE_HOST+"/info/add_main.php";
HashMap<String,String> postParams = new HashMap<>();
postParams.put("classId",classId+"");
postParams.put("username",username);
postParams.put("infoType",infoType+"");
postParams.put("content",content);
postParams.put("picCount",picUrls.size()+"");
if (isVideo){
postParams.put("type","video");
postParams.put("picUrl0","/info/video/"+picUrls.get(0));
postParams.put("picUrl1","/info/pic/"+picUrls.get(1));
}else{
postParams.put("type","pic");
for (int i = 0; i < picUrls.size(); i++) {
postParams.put("picUrl"+i,"/info/pic/"+picUrls.get(i));
}
}
OkGo.post(url).params(postParams).execute(callback);
}
/**
* 异步提醒服务器推送相关消息给他人
*
*
* @param classId 班级id
* @param infoType 信息类型
* @param callback 回调
*/
public void sendMsgToOthersAsync(int classId,int infoType,JsonCallback<LslResponse<Object>> callback){
String url = Consts.API_SERVICE_HOST+"/android_example.php?classId="+classId+"&infoType="+infoType;
OkGo.get(url).execute(callback);
}
/**
* 附件上传
* @param files 文件集合
* @param callback 回调
*/
public void upLoadFileAsync(List<File> files,JsonCallback<LslResponse<User>> callback){
String url = Consts.API_SERVICE_HOST + "/info/attachment.php";
// OkGo.post(url).params("size",files.size()).addFileParams("files",files).execute(callback);
// String url = Consts.API_SERVICE_HOST + "/user/avatar.php";
PostRequest postRequest = OkGo.post(url);
for (int i = 0; i < files.size(); i++) {
postRequest.params("file"+i,files.get(i),files.get(i).getName());
}
postRequest.params("size",files.size());
postRequest.execute(callback);
}
/*************** 信息系統 End ******************/
}

View File

@ -0,0 +1,17 @@
package com.example.nanchen.aiyaschoolpush;
import android.content.Intent;
import android.graphics.drawable.Drawable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/09/28 14:44
*/
public class CropOption {
public CharSequence title;
public Drawable icon;
public Intent appIntent;
}

View File

@ -0,0 +1,34 @@
package com.example.nanchen.aiyaschoolpush;
import android.app.Activity;
import android.net.Uri;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.lzy.imagepicker.loader.ImageLoader;
import java.io.File;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/11/29 09:43
*/
public class GlideImageLoader implements ImageLoader {
@Override
public void displayImage(Activity activity, String path, ImageView imageView, int width, int height) {
Glide.with(activity) //配置上下文
.load(Uri.fromFile(new File(path))) //设置图片路径(fix #8,文件名包含%符号 无法识别和显示)
.error(R.mipmap.default_image) //设置错误图片
.placeholder(R.mipmap.default_image) //设置占位图片
.diskCacheStrategy(DiskCacheStrategy.ALL)//缓存全尺寸
.into(imageView);
}
@Override
public void clearMemoryCache() {
}
}

View File

@ -0,0 +1,30 @@
package com.example.nanchen.aiyaschoolpush;
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.lzy.ninegrid.NineGridView;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/11/29 09:43
*/
public class PicassoImageLoader implements NineGridView.ImageLoader {
@Override
public void onDisplayImage(Context context, ImageView imageView, String url) {
com.squareup.picasso.Picasso.with(context).load(url)//
.placeholder(R.drawable.ic_default_image)//
.error(R.drawable.ic_default_image)//
.into(imageView);
}
@Override
public Bitmap getCacheImage(String url) {
return null;
}
}

View File

@ -0,0 +1,312 @@
package com.example.nanchen.aiyaschoolpush;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.nanchen.aiyaschoolpush.config.AddConfig;
import com.example.nanchen.aiyaschoolpush.helper.event.CommunityEvent;
import com.example.nanchen.aiyaschoolpush.helper.event.HomeworkEvent;
import com.example.nanchen.aiyaschoolpush.helper.event.NoticeEvent;
import com.example.nanchen.aiyaschoolpush.model.User;
import com.example.nanchen.aiyaschoolpush.model.info.InfoModel;
import com.example.nanchen.aiyaschoolpush.model.info.InfoType;
import com.example.nanchen.aiyaschoolpush.net.okgo.JsonCallback;
import com.example.nanchen.aiyaschoolpush.net.okgo.LslResponse;
import com.example.nanchen.aiyaschoolpush.ui.activity.ActivityBase;
import com.example.nanchen.aiyaschoolpush.ui.view.TitleView;
import com.example.nanchen.aiyaschoolpush.ui.view.WavyLineView;
import com.example.nanchen.aiyaschoolpush.utils.ScreenUtil;
import com.example.nanchen.aiyaschoolpush.utils.UIUtil;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import mabeijianxi.camera.MediaRecorderActivity;
import okhttp3.Call;
import okhttp3.Response;
public class SendSmallVideoActivity extends ActivityBase implements View.OnClickListener {
private static final String TAG = "SendSmallVideoActivity";
private String videoUri;
private TextView tv_send;
private TextView tv_cancel;
private String videoScreenshot;
private ImageView iv_video_screenshot;
private EditText et_send_content;
private AlertDialog dialog;
private TitleView mTitleBar;
private int infoType;
private WavyLineView mWavyLine;
private EditText mEditText;
private List<File> mFiles;
private List<String> mSmallUrls;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.smallvideo_text_edit_activity);
initView();
initData();
initEvent();
}
private void initEvent() {
// tv_cancel.setOnClickListener(this);
// tv_send.setOnClickListener(this);
// et_send_content.setOnClickListener(this);
iv_video_screenshot.setOnClickListener(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mFiles != null){
mFiles.clear();
mFiles = null;
}
if (mSmallUrls != null){
mSmallUrls.clear();
mSmallUrls = null;
}
}
private void initData() {
Intent intent = getIntent();
videoUri = intent.getStringExtra(MediaRecorderActivity.VIDEO_URI);
videoScreenshot = intent.getStringExtra(MediaRecorderActivity.VIDEO_SCREENSHOT);
Bitmap bitmap = BitmapFactory.decodeFile(videoScreenshot);
iv_video_screenshot.setImageBitmap(bitmap);
mFiles = new ArrayList<>();
mSmallUrls = new ArrayList<>();
}
private void initView() {
setContentView(R.layout.activity_release);
// tv_cancel = (TextView) findViewById(R.id.tv_cancel);
// tv_send = (TextView) findViewById(R.id.tv_send);
// et_send_content = (EditText) findViewById(R.id.et_send_content);
iv_video_screenshot = (ImageView) findViewById(R.id.iv_video_screenshot);
iv_video_screenshot.setVisibility(View.VISIBLE);
// mTitleBar = (TitleView) findViewById(R.id.send_video_titleBar);
mTitleBar = (TitleView) findViewById(R.id.release_title);
mTitleBar.setLeftButtonAsFinish(this);
SharedPreferences sp = getSharedPreferences("send.tmp",MODE_PRIVATE);
String mFrom = sp.getString("infoType","");
String content = sp.getString("content","");
switch (mFrom) {
case AddConfig.NOTICE:
mTitleBar.setTitle("发布公告");
infoType = InfoType.NOTICE;
break;
case AddConfig.HOMEWORK:
mTitleBar.setTitle("发布作业");
infoType = InfoType.HOMEWORK;
break;
default:
mTitleBar.setTitle("发布动态");
infoType = InfoType.COMMUNITY;
break;
}
mTitleBar.changeRightButtonTextColor(getResources().getColor(R.color.white3));
mTitleBar.setRightButtonText(getResources().getString(R.string.send_back_right));
mTitleBar.setRightButtonTextSize(25);
mTitleBar.setFixRightButtonPadingTop();
mTitleBar.setRightButtonOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
uploadFile();
}
});
// 波浪线设置
mWavyLine = (WavyLineView) findViewById(R.id.release_wavyLine);
int initStrokeWidth = 1;
int initAmplitude = 5;
float initPeriod = (float) (2 * Math.PI / 60);
mWavyLine.setPeriod(initPeriod);
mWavyLine.setAmplitude(initAmplitude);
mWavyLine.setStrokeWidth(ScreenUtil.dp2px(initStrokeWidth));
mEditText = (EditText) findViewById(R.id.release_edit);
if (TextUtils.isEmpty(content) || content.equals("")){// 如果用户之前没有输入信息则不设置输入框
return;
}
mEditText.setText(content);
}
private void uploadFile() {
final String content = mEditText.getText().toString().trim();
if (TextUtils.isEmpty(content)) {
UIUtil.showToast("发布内容不能为空!");
return;
}
File file = new File(videoUri);
File file1 = new File(videoScreenshot);
mFiles.add(file);
mFiles.add(file1);
showLoading(this);
AppService.getInstance().upLoadFileAsync(mFiles,new JsonCallback<LslResponse<User>>() {
@Override
public void onSuccess(LslResponse<User> userLslResponse, Call call, Response response) {
if (userLslResponse.code == LslResponse.RESPONSE_OK) {
UIUtil.showToast("视频上传成功");
Log.e(TAG, "视频上传成功");
for (int i = 0; i < mFiles.size(); i++) {
Log.e(TAG, "onSuccess: "+i+":"+mFiles.get(i).getName() );
}
} else {
UIUtil.showToast("视频上传失败");
Log.e(TAG, "视频上传失败");
if (!SendSmallVideoActivity.this.isFinishing()) {
stopLoading();
return;
}
}
sendInfo();
}
});
}
private void sendInfo() {
final String content = mEditText.getText().toString().trim();
if (TextUtils.isEmpty(content)) {
UIUtil.showToast("发布内容不能为空!");
stopLoading();
return;
}
if(AppService.getInstance().getCurrentUser() == null){
UIUtil.showToast("未知错误,请重新登录后操作");
stopLoading();
return;
}
// mSmallUrls.add(videoScreenshot);
// mSmallUrls.add(videoUri);
for (int i = 0; i < mFiles.size(); i++) {
mSmallUrls.add(mFiles.get(i).getName());
}
final int classId = AppService.getInstance().getCurrentUser().classid;
String username = AppService.getInstance().getCurrentUser().username;
AppService.getInstance().addMainInfoAsync(classId, username, infoType, content, mSmallUrls, true,new JsonCallback<LslResponse<InfoModel>>() {
@Override
public void onSuccess(LslResponse<InfoModel> infoModelLslResponse, Call call, Response response) {
if (infoModelLslResponse.code == LslResponse.RESPONSE_OK) {
UIUtil.showToast("发布信息成功!");
Log.e(TAG, "onSuccess: data"+infoModelLslResponse.data.videoUrl.get(0).toString() );
// 只有公告和作业才发布推送
if (infoType == 1 || infoType == 2){
sendMsgToOthers(classId,infoType);
}
Log.e(TAG, infoType + "");
if (infoType == InfoType.NOTICE) {
EventBus.getDefault().post(new NoticeEvent(infoModelLslResponse.data));
Log.e(TAG, "通知发起");
} else if (infoType == InfoType.HOMEWORK) {
EventBus.getDefault().post(new HomeworkEvent(infoModelLslResponse.data));
Log.e(TAG, "作业发起");
} else {
EventBus.getDefault().post(new CommunityEvent(infoModelLslResponse.data));
Log.e(TAG, "社区发起");
}
if (!SendSmallVideoActivity.this.isFinishing()) {
stopLoading();
}
SendSmallVideoActivity.this.finish();
} else {
UIUtil.showToast("发布信息失败,请稍后再试!");
if (!SendSmallVideoActivity.this.isFinishing()) {
stopLoading();
}
}
}
});
}
private void sendMsgToOthers(int classId,int infoType) {
AppService.getInstance().sendMsgToOthersAsync(classId,infoType, new JsonCallback<LslResponse<Object>>() {
@Override
public void onSuccess(LslResponse<Object> objectLslResponse, Call call, Response response) {
Log.e(TAG,objectLslResponse.msg);
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
// case R.id.tv_cancel:
// hesitate();
// break;
// case R.id.tv_send:
// break;
case R.id.iv_video_screenshot:
startActivity(new Intent(this, VideoPlayerActivity.class).putExtra(
"path", videoUri));
break;
}
}
@Override
public void onBackPressed() {
hesitate();
}
private void hesitate() {
if (dialog == null) {
dialog = new AlertDialog.Builder(this)
.setTitle(R.string.hint)
.setMessage(R.string.record_camera_exit_dialog_message)
.setNegativeButton(
R.string.record_camera_cancel_dialog_yes,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
finish();
// FileUtils.deleteDir(getIntent().getStringExtra(MediaRecorderActivity.OUTPUT_DIRECTORY));
}
})
.setPositiveButton(R.string.record_camera_cancel_dialog_no,
null).setCancelable(false).show();
} else {
dialog.show();
}
}
}

View File

@ -0,0 +1,20 @@
package com.example.nanchen.aiyaschoolpush;
import com.example.nanchen.aiyaschoolpush.utils.UIUtil;
/**
* 自定义异常类
*
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/12/15 17:07
*/
public class VideoException extends RuntimeException {
public VideoException(String desc) {
UIUtil.showToast(desc);
}
}

View File

@ -0,0 +1,217 @@
package com.example.nanchen.aiyaschoolpush;
import android.annotation.TargetApi;
import android.app.Activity;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import com.example.nanchen.aiyaschoolpush.ui.activity.ActivityBase;
import mabeijianxi.camera.MediaRecorderBase;
import mabeijianxi.camera.util.DeviceUtils;
import mabeijianxi.camera.util.StringUtils;
import mabeijianxi.camera.views.SurfaceVideoView;
/**
* 通用单独播放界面
*
* @author tangjun
*/
public class VideoPlayerActivity extends ActivityBase implements
SurfaceVideoView.OnPlayStateListener, OnErrorListener,
OnPreparedListener, OnClickListener, OnCompletionListener,
OnInfoListener {
/**
* 播放控件
*/
private SurfaceVideoView mVideoView;
/**
* 暂停按钮
*/
private View mPlayerStatus;
private View mLoading;
/**
* 播放路径
*/
private String mPath;
/**
* 是否需要回复播放
*/
private boolean mNeedResume;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 防止锁屏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mPath = getIntent().getStringExtra("path");
if (StringUtils.isEmpty(mPath)) {
finish();
return;
}
setContentView(R.layout.activity_video_player);
mVideoView = (SurfaceVideoView) findViewById(R.id.videoview);
int screenWidth = getScreenWidth(this);
int videoHight = (int) (screenWidth / (MediaRecorderBase.SMALL_VIDEO_WIDTH / (MediaRecorderBase.SMALL_VIDEO_HEIGHT * 1.0f)));
mVideoView.getLayoutParams().height = videoHight;
mVideoView.requestLayout();
mPlayerStatus = findViewById(R.id.play_status);
mLoading = findViewById(R.id.loading);
mVideoView.setOnPreparedListener(this);
mVideoView.setOnPlayStateListener(this);
mVideoView.setOnErrorListener(this);
mVideoView.setOnClickListener(this);
mVideoView.setOnInfoListener(this);
mVideoView.setOnCompletionListener(this);
// mVideoView.getLayoutParams().height = DeviceUtils.getScreenWidth(this);
findViewById(R.id.root).setOnClickListener(this);
mVideoView.setVideoPath(mPath);
}
public int getScreenWidth(Activity context) {
DisplayMetrics mDisplayMetrics = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);
int W = mDisplayMetrics.widthPixels;
return W;
}
@Override
public void onResume() {
super.onResume();
if (mVideoView != null && mNeedResume) {
mNeedResume = false;
if (mVideoView.isRelease())
mVideoView.reOpen();
else
mVideoView.start();
}
}
@Override
public void onPause() {
super.onPause();
if (mVideoView != null) {
if (mVideoView.isPlaying()) {
mNeedResume = true;
mVideoView.pause();
}
}
}
@Override
protected void onDestroy() {
if (mVideoView != null) {
mVideoView.release();
mVideoView = null;
}
super.onDestroy();
}
@Override
public void onPrepared(MediaPlayer mp) {
mVideoView.setVolume(SurfaceVideoView.getSystemVolumn(this));
mVideoView.start();
// new Handler().postDelayed(new Runnable() {
//
// @SuppressWarnings("deprecation")
// @Override
// public void run() {
// if (DeviceUtils.hasJellyBean()) {
// mVideoView.setBackground(null);
// } else {
// mVideoView.setBackgroundDrawable(null);
// }
// }
// }, 300);
mLoading.setVisibility(View.GONE);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
switch (event.getKeyCode()) {// 跟随系统音量走
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
mVideoView.dispatchKeyEvent(this, event);
break;
}
return super.dispatchKeyEvent(event);
}
@Override
public void onStateChanged(boolean isPlaying) {
mPlayerStatus.setVisibility(isPlaying ? View.GONE : View.VISIBLE);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
if (!isFinishing()) {
// 播放失败
}
finish();
return false;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.videoview:
case R.id.root:
finish();
break;
}
}
@Override
public void onCompletion(MediaPlayer mp) {
if (!isFinishing())
mVideoView.reOpen();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
switch (what) {
case MediaPlayer.MEDIA_INFO_BAD_INTERLEAVING:
// 音频和视频数据不正确
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_START:
if (!isFinishing())
mVideoView.pause();
break;
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
if (!isFinishing())
mVideoView.start();
break;
case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
if (DeviceUtils.hasJellyBean()) {
mVideoView.setBackground(null);
} else {
mVideoView.setBackgroundDrawable(null);
}
break;
}
return false;
}
}

View File

@ -0,0 +1,59 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List;
/**
* 万能适配器对ListView 和GridView等
*
* @author nanchen
* @date 2016-7-29 14:17:28
*/
public abstract class CommonAdapter<T> extends BaseAdapter {
private Context context;
private List<T> list;
private LayoutInflater inflater;
private int itemLayoutId;
public CommonAdapter(Context context, List<T> list, int itemLayoutId) {
this.context = context;
this.list = list;
this.itemLayoutId = itemLayoutId;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return list == null ? 0 : list.size();
}
@Override
public T getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = getViewHolder(position,convertView,parent);
convert(holder,getItem(position));
return holder.getConvertView();
}
public abstract void convert(ViewHolder holder,T item);
private ViewHolder getViewHolder(int position,View convertView,ViewGroup parent){
return ViewHolder.get(context,convertView,parent,itemLayoutId,position);
}
}

View File

@ -0,0 +1,149 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* @author nanchen
* @fileName ischool
* @packageName com.idtechinfo.shouxiner.adapter.common
* @date 2016/09/02 15:47
*/
public abstract class CommonRecyclerAdapter<T> extends RecyclerView.Adapter<CommonRecyclerHolder> {
private Context context;//上下文
private List<T> list;//数据源
private LayoutInflater inflater;//布局器
private int itemLayoutId;//布局id
private boolean isScrolling;//是否在滚动
private OnItemClickListener listener;//点击事件监听器
private OnItemLongClickListener longClickListener;//长按监听器
private RecyclerView recyclerView;
//在RecyclerView提供数据的时候调用
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView = recyclerView;
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
this.recyclerView = null;
}
/**
* 定义一个点击事件接口回调
*/
public interface OnItemClickListener {
void onItemClick(RecyclerView parent, View view, int position);
}
public interface OnItemLongClickListener {
boolean onItemLongClick(RecyclerView parent, View view, int position);
}
/**
* 插入一项
*
* @param item
* @param position
*/
public void insert(T item, int position) {
list.add(position, item);
notifyItemInserted(position);
}
/**
* 删除一项
*
* @param position 删除位置
*/
public void delete(int position) {
list.remove(position);
notifyItemRemoved(position);
}
public CommonRecyclerAdapter(Context context, List<T> list, int itemLayoutId) {
this.context = context;
this.list = list;
this.itemLayoutId = itemLayoutId;
inflater = LayoutInflater.from(context);
// recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
// @Override
// public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
// super.onScrollStateChanged(recyclerView, newState);
// isScrolling = !(newState == RecyclerView.SCROLL_STATE_IDLE);
// if (!isScrolling) {
// notifyDataSetChanged();
// }
// }
// });
}
@Override
public CommonRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(itemLayoutId, parent, false);
return CommonRecyclerHolder.getRecyclerHolder(context, view);
}
@Override
public void onBindViewHolder(final CommonRecyclerHolder holder, int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null && view != null && recyclerView != null) {
int position = recyclerView.getChildAdapterPosition(view);
listener.onItemClick(recyclerView, view, position);
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
if (longClickListener != null && view != null && recyclerView != null) {
int position = recyclerView.getChildAdapterPosition(view);
longClickListener.onItemLongClick(recyclerView, view, position);
return true;
}
return false;
}
});
convert(holder, list.get(position), position, isScrolling);
}
@Override
public int getItemCount() {
return list == null ? 0 : list.size();
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
public void setOnItemLongClickListener(OnItemLongClickListener longClickListener) {
this.longClickListener = longClickListener;
}
/**
* 填充RecyclerView适配器的方法子类需要重写
*
* @param holder ViewHolder
* @param item 子项
* @param position 位置
* @param isScrolling 是否在滑动
*/
public abstract void convert(CommonRecyclerHolder holder, T item, int position, boolean isScrolling);
}

View File

@ -0,0 +1,143 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.nanchen.aiyaschoolpush.R;
import com.lzy.ninegrid.NineGridView;
import com.lzy.ninegrid.preview.NineGridViewClickAdapter;
import com.squareup.picasso.Picasso;
/**
* @author nanchen
* @fileName ischool
* @packageName com.idtechinfo.shouxiner.adapter.common
* @date 2016/09/02 15:41
*/
public class CommonRecyclerHolder extends RecyclerView.ViewHolder {
private SparseArray<View> views;
private Context context;
private CommonRecyclerHolder(Context context, View itemView) {
super(itemView);
this.context = context;
//指定一个初始为8
views = new SparseArray<>(8);
}
/**
* 取得一个RecyclerHolder对象
* @param context 上下文
* @param itemView 子项
* @return 返回一个RecyclerHolder对象
*/
public static CommonRecyclerHolder getRecyclerHolder(Context context, View itemView){
return new CommonRecyclerHolder(context,itemView);
}
public SparseArray<View> getViews(){
return this.views;
}
/**
* 通过view的id获取对应的控件如果没有则加入views中
* @param viewId 控件的id
* @return 返回一个控件
*/
@SuppressWarnings("unchecked")
public <T extends View> T getView(int viewId){
View view = views.get(viewId);
if (view == null ){
view = itemView.findViewById(viewId);
views.put(viewId,view);
}
return (T) view;
}
/**
* 设置字符串
*/
public CommonRecyclerHolder setText(int viewId, String text){
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public CommonRecyclerHolder setTextColor(int viewId,int color){
TextView textView = getView(viewId);
textView.setTextColor(color);
return this;
}
/**
* 设置图片
*/
public CommonRecyclerHolder setImageResource(int viewId, int drawableId){
ImageView iv = getView(viewId);
iv.setImageResource(drawableId);
return this;
}
/**
* 设置图片
*/
public CommonRecyclerHolder setImageBitmap(int viewId, Bitmap bitmap){
ImageView iv = getView(viewId);
iv.setImageBitmap(bitmap);
return this;
}
/**
* 设置图片
*/
public CommonRecyclerHolder setImageByUrl(int viewId, String url){
Picasso.with(context).load(url)
.placeholder(context.getResources().getDrawable(R.drawable.ic_default_image))
.error(context.getResources().getDrawable(R.drawable.ic_default_image))
.into((ImageView) getView(viewId));
return this;
}
public CommonRecyclerHolder setOnRecyclerItemClickListener(int viewId,OnClickListener listener){
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
/**
* 设置九宫格图片
* @param viewId id
* @param clickAdapter Adapter
*/
public CommonRecyclerHolder setNineGridAdapter(int viewId,NineGridViewClickAdapter clickAdapter){
NineGridView nineGridView = getView(viewId);
nineGridView.setAdapter(clickAdapter);
return this;
}
/**
* 设置一个控件是否可见
* @param viewId id
* @param visibility 可见性
* @return
*/
public CommonRecyclerHolder setVisibility(int viewId,int visibility){
View view = getView(viewId);
view.setVisibility(visibility);
return this;
}
public CommonRecyclerHolder setOnClckListener(int viewId,OnClickListener listener){
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
}

View File

@ -0,0 +1,51 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.nanchen.aiyaschoolpush.CropOption;
import com.example.nanchen.aiyaschoolpush.R;
import java.util.ArrayList;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.adapter
* @date 2016/09/28 15:26
*/
public class CropOptionAdapter extends ArrayAdapter<CropOption> {
private ArrayList<CropOption> mOptions;
private LayoutInflater mInflater;
public CropOptionAdapter(Context context, ArrayList<CropOption> options) {
super(context, R.layout.layout_crop_selector, options);
mOptions = options;
mInflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup group) {
if (convertView == null)
convertView = mInflater.inflate(R.layout.layout_crop_selector, null);
CropOption item = mOptions.get(position);
if (item != null) {
((ImageView) convertView.findViewById(R.id.iv_icon))
.setImageDrawable(item.icon);
((TextView) convertView.findViewById(R.id.tv_name))
.setText(item.title);
return convertView;
}
return null;
}
}

View File

@ -0,0 +1,125 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.adapter;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.nanchen.aiyaschoolpush.R;
import com.hyphenate.chat.EMGroup;
import java.util.List;
public class GroupAdapter extends ArrayAdapter<EMGroup> {
private LayoutInflater inflater;
private String newGroup;
private String addPublicGroup;
public GroupAdapter(Context context, int res, List<EMGroup> groups) {
super(context, res, groups);
this.inflater = LayoutInflater.from(context);
newGroup = context.getResources().getString(R.string.The_new_group_chat);
addPublicGroup = context.getResources().getString(R.string.add_public_group_chat);
}
@Override
public int getViewTypeCount() {
return 4;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return 0;
} else if (position == 1) {
return 1;
} else if (position == 2) {
return 2;
} else {
return 3;
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (getItemViewType(position) == 0) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.em_search_bar_with_padding, parent, false);
}
final EditText query = (EditText) convertView.findViewById(R.id.query);
final ImageButton clearSearch = (ImageButton) convertView.findViewById(R.id.search_clear);
query.addTextChangedListener(new TextWatcher() {
public void onTextChanged(CharSequence s, int start, int before, int count) {
getFilter().filter(s);
if (s.length() > 0) {
clearSearch.setVisibility(View.VISIBLE);
} else {
clearSearch.setVisibility(View.INVISIBLE);
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void afterTextChanged(Editable s) {
}
});
clearSearch.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
query.getText().clear();
}
});
} else if (getItemViewType(position) == 1) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.em_row_add_group, parent, false);
}
((ImageView) convertView.findViewById(R.id.avatar)).setImageResource(R.drawable.em_create_group);
((TextView) convertView.findViewById(R.id.name)).setText(newGroup);
} else if (getItemViewType(position) == 2) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.em_row_add_group, parent, false);
}
((ImageView) convertView.findViewById(R.id.avatar)).setImageResource(R.drawable.em_add_public_group);
((TextView) convertView.findViewById(R.id.name)).setText(addPublicGroup);
((TextView) convertView.findViewById(R.id.header)).setVisibility(View.VISIBLE);
} else {
if (convertView == null) {
convertView = inflater.inflate(R.layout.em_row_group, parent, false);
}
((TextView) convertView.findViewById(R.id.name)).setText(getItem(position - 3).getGroupName());
}
return convertView;
}
@Override
public int getCount() {
return super.getCount() + 3;
}
}

View File

@ -0,0 +1,48 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
/**
* 引导页Guide的适配器
*
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.adapter
* @date 2016/09/13 17:15
*/
public class GuidePagerAdapter extends PagerAdapter {
private Context context;
private List<View> views;
public GuidePagerAdapter(Context context, List<View> views) {
this.context = context;
this.views = views;
}
@Override
public int getCount() {
return views == null ? 0 : views.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(views.get(position));
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(views.get(position));
return views.get(position);
}
}

View File

@ -0,0 +1,112 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.app.Activity;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.example.nanchen.aiyaschoolpush.R;
import com.example.nanchen.aiyaschoolpush.ui.activity.ReleaseActivity;
import com.lzy.imagepicker.ImagePicker;
import com.lzy.imagepicker.bean.ImageItem;
import java.util.ArrayList;
import java.util.List;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/11/23 14:21
*/
public class ImagePickerAdapter extends RecyclerView.Adapter<ImagePickerAdapter.SelectedPicViewHolder> {
private int maxImgCount;
private Context mContext;
private List<ImageItem> mData;
private LayoutInflater mInflater;
private OnRecyclerViewItemClickListener listener;
private boolean isAdded; //是否额外添加了最后一个图片
public interface OnRecyclerViewItemClickListener {
void onItemClick(View view, int position);
}
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
this.listener = listener;
}
public void setImages(List<ImageItem> data) {
mData = new ArrayList<>(data);
if (getItemCount() < maxImgCount) {
mData.add(new ImageItem());
isAdded = true;
} else {
isAdded = false;
}
notifyDataSetChanged();
}
public List<ImageItem> getImages() {
//由于图片未选满时最后一张显示添加图片因此这个方法返回真正的已选图片
if (isAdded) return new ArrayList<>(mData.subList(0, mData.size() - 1));
else return mData;
}
public ImagePickerAdapter(Context mContext, List<ImageItem> data, int maxImgCount) {
this.mContext = mContext;
this.maxImgCount = maxImgCount;
this.mInflater = LayoutInflater.from(mContext);
setImages(data);
}
@Override
public SelectedPicViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new SelectedPicViewHolder(mInflater.inflate(R.layout.layout_item_image, parent, false));
}
@Override
public void onBindViewHolder(SelectedPicViewHolder holder, int position) {
holder.bind(position);
}
@Override
public int getItemCount() {
return mData.size();
}
public class SelectedPicViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView iv_img;
private int clickPosition;
public SelectedPicViewHolder(View itemView) {
super(itemView);
iv_img = (ImageView) itemView.findViewById(R.id.iv_img);
}
public void bind(int position) {
//设置条目的点击事件
itemView.setOnClickListener(this);
//根据条目位置设置图片
ImageItem item = mData.get(position);
if (isAdded && position == getItemCount() - 1) {
iv_img.setImageResource(R.drawable.selector_image_add);
clickPosition = ReleaseActivity.IMAGE_ITEM_ADD;
} else {
ImagePicker.getInstance().getImageLoader().displayImage((Activity) mContext, item.path, iv_img, 0, 0);
clickPosition = position;
}
}
@Override
public void onClick(View v) {
Log.e("ReleaseActivity","缩略宽:"+iv_img.getWidth()+",缩略高:"+iv_img.getHeight());
if (listener != null) listener.onItemClick(v, clickPosition);
}
}
}

View File

@ -0,0 +1,42 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.List;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.adapter
* @date 2016/10/08 10:11
*/
public class MyPagerAdapter extends FragmentPagerAdapter {
private List<Fragment> list;
private List<String> mName;
public MyPagerAdapter(FragmentManager fm,List<String> name,List<Fragment> list) {
super(fm);
this.list = list;
this.mName = name;
}
@Override
public Fragment getItem(int position) {
return list.get(position);
}
@Override
public int getCount() {
return list == null ? 0:list.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mName.get(position);
}
}

View File

@ -0,0 +1,289 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.adapter;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.nanchen.aiyaschoolpush.R;
import com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao;
import com.example.nanchen.aiyaschoolpush.im.InviteMesageStatus;
import com.example.nanchen.aiyaschoolpush.im.InviteMessage;
import com.hyphenate.chat.EMClient;
import java.util.List;
public class NewFriendsMsgAdapter extends ArrayAdapter<InviteMessage> {
private Context context;
private InviteMessgeDao messgeDao;
public NewFriendsMsgAdapter(Context context, int textViewResourceId, List<InviteMessage> objects) {
super(context, textViewResourceId, objects);
this.context = context;
messgeDao = new InviteMessgeDao(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = View.inflate(context, R.layout.layout_em_row_invite_msg, null);
holder.avator = (ImageView) convertView.findViewById(R.id.avatar);
holder.reason = (TextView) convertView.findViewById(R.id.message);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.agree = (Button) convertView.findViewById(R.id.agree);
holder.status = (Button) convertView.findViewById(R.id.user_state);
holder.groupContainer = (LinearLayout) convertView.findViewById(R.id.ll_group);
holder.groupname = (TextView) convertView.findViewById(R.id.tv_groupName);
// holder.time = (TextView) convertView.findViewById(R.id.time);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
String str1 = context.getResources().getString(R.string.Has_agreed_to_your_friend_request);
String str2 = context.getResources().getString(R.string.agree);
String str3 = context.getResources().getString(R.string.Request_to_add_you_as_a_friend);
String str4 = context.getResources().getString(R.string.Apply_to_the_group_of);
String str5 = context.getResources().getString(R.string.Has_agreed_to);
String str6 = context.getResources().getString(R.string.Has_refused_to);
String str7 = context.getResources().getString(R.string.refuse);
String str8 = context.getResources().getString(R.string.invite_join_group);
String str9 = context.getResources().getString(R.string.accept_join_group);
String str10 = context.getResources().getString(R.string.refuse_join_group);
final InviteMessage msg = getItem(position);
if (msg != null) {
holder.agree.setVisibility(View.INVISIBLE);
if(msg.getGroupId() != null){ // show group name
holder.groupContainer.setVisibility(View.VISIBLE);
holder.groupname.setText(msg.getGroupName());
} else{
holder.groupContainer.setVisibility(View.GONE);
}
holder.reason.setText(msg.getReason());
holder.name.setText(msg.getFrom());
// holder.time.setText(DateUtils.getTimestampString(new
// Date(msg.getTime())));
if (msg.getStatus() == InviteMesageStatus.BEAGREED) {
holder.status.setVisibility(View.INVISIBLE);
holder.reason.setText(str1);
} else if (msg.getStatus() == InviteMesageStatus.BEINVITEED || msg.getStatus() == InviteMesageStatus.BEAPPLYED ||
msg.getStatus() == InviteMesageStatus.GROUPINVITATION) {
holder.agree.setVisibility(View.VISIBLE);
holder.agree.setEnabled(true);
holder.agree.setBackgroundResource(android.R.drawable.btn_default);
holder.agree.setText(str2);
holder.status.setVisibility(View.VISIBLE);
holder.status.setEnabled(true);
holder.status.setBackgroundResource(android.R.drawable.btn_default);
holder.status.setText(str7);
if(msg.getStatus() == InviteMesageStatus.BEINVITEED){
if (msg.getReason() == null) {
// use default text
holder.reason.setText(str3);
}
}else if (msg.getStatus() == InviteMesageStatus.BEAPPLYED) { //application to join group
if (TextUtils.isEmpty(msg.getReason())) {
holder.reason.setText(str4 + msg.getGroupName());
}
} else if (msg.getStatus() == InviteMesageStatus.GROUPINVITATION) {
if (TextUtils.isEmpty(msg.getReason())) {
holder.reason.setText(str8 + msg.getGroupName());
}
}
// set click listener
holder.agree.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// accept invitation
acceptInvitation(holder.agree, holder.status, msg);
}
});
holder.status.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// decline invitation
refuseInvitation(holder.agree, holder.status, msg);
}
});
} else if (msg.getStatus() == InviteMesageStatus.AGREED) {
holder.status.setText(str5);
holder.status.setBackgroundDrawable(null);
holder.status.setEnabled(false);
} else if(msg.getStatus() == InviteMesageStatus.REFUSED){
holder.status.setText(str6);
holder.status.setBackgroundDrawable(null);
holder.status.setEnabled(false);
} else if(msg.getStatus() == InviteMesageStatus.GROUPINVITATION_ACCEPTED){
String str = msg.getGroupInviter() + str9 + msg.getGroupName();
holder.status.setText(str);
holder.status.setBackgroundDrawable(null);
holder.status.setEnabled(false);
} else if(msg.getStatus() == InviteMesageStatus.GROUPINVITATION_DECLINED){
String str = msg.getGroupInviter() + str10 + msg.getGroupName();
holder.status.setText(str);
holder.status.setBackgroundDrawable(null);
holder.status.setEnabled(false);
}
}
return convertView;
}
/**
* accept invitation
*
*/
private void acceptInvitation(final Button buttonAgree, final Button buttonRefuse, final InviteMessage msg) {
final ProgressDialog pd = new ProgressDialog(context);
String str1 = context.getResources().getString(R.string.Are_agree_with);
final String str2 = context.getResources().getString(R.string.Has_agreed_to);
final String str3 = context.getResources().getString(R.string.Agree_with_failure);
pd.setMessage(str1);
pd.setCanceledOnTouchOutside(false);
pd.show();
new Thread(new Runnable() {
public void run() {
// call api
try {
if (msg.getStatus() == InviteMesageStatus.BEINVITEED) {//accept be friends
EMClient.getInstance().contactManager().acceptInvitation(msg.getFrom());
} else if (msg.getStatus() == InviteMesageStatus.BEAPPLYED) { //accept application to join group
EMClient.getInstance().groupManager().acceptApplication(msg.getFrom(), msg.getGroupId());
} else if (msg.getStatus() == InviteMesageStatus.GROUPINVITATION) {
EMClient.getInstance().groupManager().acceptInvitation(msg.getGroupId(), msg.getGroupInviter());
}
msg.setStatus(InviteMesageStatus.AGREED);
// update database
ContentValues values = new ContentValues();
values.put(InviteMessgeDao.COLUMN_NAME_STATUS, msg.getStatus().ordinal());
messgeDao.updateMessage(msg.getId(), values);
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
pd.dismiss();
buttonAgree.setText(str2);
buttonAgree.setBackgroundDrawable(null);
buttonAgree.setEnabled(false);
buttonRefuse.setVisibility(View.INVISIBLE);
}
});
} catch (final Exception e) {
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
pd.dismiss();
Toast.makeText(context, str3 + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
}
}).start();
}
/**
* decline invitation
*
*/
private void refuseInvitation(final Button buttonAgree, final Button buttonRefuse, final InviteMessage msg) {
final ProgressDialog pd = new ProgressDialog(context);
String str1 = context.getResources().getString(R.string.Are_refuse_with);
final String str2 = context.getResources().getString(R.string.Has_refused_to);
final String str3 = context.getResources().getString(R.string.Refuse_with_failure);
pd.setMessage(str1);
pd.setCanceledOnTouchOutside(false);
pd.show();
new Thread(new Runnable() {
public void run() {
// call api
try {
if (msg.getStatus() == InviteMesageStatus.BEINVITEED) {//decline the invitation
EMClient.getInstance().contactManager().declineInvitation(msg.getFrom());
} else if (msg.getStatus() == InviteMesageStatus.BEAPPLYED) { //decline application to join group
EMClient.getInstance().groupManager().declineApplication(msg.getFrom(), msg.getGroupId(), "");
} else if (msg.getStatus() == InviteMesageStatus.GROUPINVITATION) {
EMClient.getInstance().groupManager().declineInvitation(msg.getGroupId(), msg.getGroupInviter(), "");
}
msg.setStatus(InviteMesageStatus.REFUSED);
// update database
ContentValues values = new ContentValues();
values.put(InviteMessgeDao.COLUMN_NAME_STATUS, msg.getStatus().ordinal());
messgeDao.updateMessage(msg.getId(), values);
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
pd.dismiss();
buttonRefuse.setText(str2);
buttonRefuse.setBackgroundDrawable(null);
buttonRefuse.setEnabled(false);
buttonAgree.setVisibility(View.INVISIBLE);
}
});
} catch (final Exception e) {
((Activity) context).runOnUiThread(new Runnable() {
@Override
public void run() {
pd.dismiss();
Toast.makeText(context, str3 + e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
}).start();
}
private static class ViewHolder {
ImageView avator;
TextView name;
TextView reason;
Button agree;
Button status;
LinearLayout groupContainer;
TextView groupname;
// TextView time;
}
}

View File

@ -0,0 +1,122 @@
package com.example.nanchen.aiyaschoolpush.adapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
/**
* 万能适配器的ViewHolder
*
* @author nanchen
* @date 2016-07-29 15:04:10
*/
public class ViewHolder {
//现在对于int作为键的官方推荐用SparseArray替代HashMap
private final SparseArray<View> views;
private View convertView;
private Context context;
private ViewHolder(Context context,ViewGroup parent,int itemLayoutId,int position) {
this.context = context;
this.views = new SparseArray<>();
this.convertView = LayoutInflater.from(context).inflate(itemLayoutId,parent,false);
convertView.setTag(this);
}
/**
* 拿到一个ViewHolder对象
*/
public static ViewHolder get(Context context,View convertView, ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
return new ViewHolder(context,parent, layoutId, position);
}
return (ViewHolder) convertView.getTag();
}
/**
* 通过控件的Id获取对于的控件如果没有则加入views
*/
public <T extends View> T getView(int viewId) {
View view = views.get(viewId);
if (view == null) {
view = convertView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
public View getConvertView() {
return convertView;
}
/**
* 设置字符串
*/
public ViewHolder setText(int viewId,String text){
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public ViewHolder setText(int viewId,CharSequence text){
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
/**
* 设置图片
*/
public ViewHolder setImageResource(int viewId,int drawableId){
ImageView iv = getView(viewId);
iv.setImageResource(drawableId);
return this;
}
/**
* 设置图片
*/
public ViewHolder setImageDrawable(int viewId, Drawable drawable){
ImageView iv = getView(viewId);
iv.setImageDrawable(drawable);
return this;
}
/**
* 设置图片
*/
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap){
ImageView iv = getView(viewId);
iv.setImageBitmap(bitmap);
return this;
}
/**
* 设置图片
*/
public ViewHolder setImageByUrl(int viewId,String url){
Picasso.with(context).load(url).into((ImageView) getView(viewId));
// ImageLoader.getInstance().init(ImageLoaderConfiguration.createDefault(context));
// ImageLoader.getInstance().displayImage(url, (ImageView) getView(viewId));
return this;
}
/**
* 设置文本颜色
*/
public ViewHolder setTextColor(int viewId,int color){
TextView textView = getView(viewId);
textView.setTextColor(color);
return this;
}
}

View File

@ -0,0 +1,80 @@
package com.example.nanchen.aiyaschoolpush.behavior;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.CoordinatorLayout.Behavior;
import android.support.design.widget.CoordinatorLayout.LayoutParams;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.behavior
* @date 2016/10/13 09:24
*/
public class FloatingActionButtonScrollBehavior extends FloatingActionButton.Behavior {
public FloatingActionButtonScrollBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final
FloatingActionButton child, final View directTargetChild, final View target, final int
nestedScrollAxes) {
// 确保是竖直判断的滚动
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll
(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final
FloatingActionButton child, final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) { // 往下滑
child.hide();
if (mOnStateChangedListener != null){
mOnStateChangedListener.onChanged(false);
}
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
child.show();
if (mOnStateChangedListener != null){
mOnStateChangedListener.onChanged(true);
}
}
}
/**
* 定义一个接口用于隐藏导航栏
*/
public interface OnStateChangedListener{
void onChanged(boolean isShow);
}
private OnStateChangedListener mOnStateChangedListener;
public void setOnStateChangedListener(OnStateChangedListener onStateChangedListener) {
mOnStateChangedListener = onStateChangedListener;
}
public static <V extends View> FloatingActionButtonScrollBehavior from(V view){
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)){
throw new IllegalArgumentException("这个View不是CoodinatorLayout的子View");
}
Behavior behavior = ((LayoutParams)params).getBehavior();
if (!(behavior instanceof FloatingActionButtonScrollBehavior)){
throw new IllegalArgumentException("这个View的Behavior不是FloatingActionButtonScrollBehavior");
}
return (FloatingActionButtonScrollBehavior) behavior;
}
}

View File

@ -0,0 +1,70 @@
package com.example.nanchen.aiyaschoolpush.behavior;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.CoordinatorLayout.Behavior;
import android.support.design.widget.CoordinatorLayout.LayoutParams;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.behavior
* @date 2016/10/13 09:23
*/
public class MyBehavior extends CoordinatorLayout.Behavior {
//写了这个构造方法才能在XML文件中直接指定
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
return true;//返回true代表我们关心这个滚动事件
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
if (dy < 0) {//向下滚动
ViewCompat.animate(child).scaleX(1).alpha(1).start();
if (mOnStateChangedListener != null){
mOnStateChangedListener.onChanged(true);
}
} else {//向上滚动
ViewCompat.animate(child).scaleX(0).alpha(0).start();
if (mOnStateChangedListener != null){
mOnStateChangedListener.onChanged(false);
}
}
}
/**
* 定义一个接口用于隐藏导航栏
*/
public interface OnStateChangedListener{
void onChanged(boolean isShow);
}
private OnStateChangedListener mOnStateChangedListener;
public void setOnStateChangedListener(OnStateChangedListener onStateChangedListener) {
mOnStateChangedListener = onStateChangedListener;
}
public static <V extends View> MyBehavior from(V view){
ViewGroup.LayoutParams params = view.getLayoutParams();
if (!(params instanceof CoordinatorLayout.LayoutParams)){
throw new IllegalArgumentException("这个View不是CoodinatorLayout的子View");
}
Behavior behavior = ((LayoutParams)params).getBehavior();
if (!(behavior instanceof MyBehavior)){
throw new IllegalArgumentException("这个View的Behavior不是MyBehavior");
}
return (MyBehavior) behavior;
}
}

View File

@ -0,0 +1,85 @@
package com.example.nanchen.aiyaschoolpush.behavior;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
/**
* Author: nanchen
* Email: liushilin520@foxmail.com
* Date: 2017-04-28 11:41
*/
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();
private boolean mIsAnimatingOut = false;
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
// Ensure we react to vertical scrolling
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View target, final int dxConsumed, final int dyConsumed,
final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && !this.mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {
// User scrolled down and the FAB is currently visible -> hide the FAB
animateOut(child);
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
// User scrolled up and the FAB is currently not visible -> show the FAB
animateIn(child);
}
}
// Same animation that FloatingActionButton.Behavior uses to hide the FAB when the AppBarLayout exits
private void animateOut(final FloatingActionButton button) {
ViewCompat.animate(button).translationY(button.getHeight() + getMarginBottom(button)).setInterpolator(INTERPOLATOR).withLayer()
.setListener(new ViewPropertyAnimatorListener() {
public void onAnimationStart(View view) {
ScrollAwareFABBehavior.this.mIsAnimatingOut = true;
}
public void onAnimationCancel(View view) {
ScrollAwareFABBehavior.this.mIsAnimatingOut = false;
}
public void onAnimationEnd(View view) {
ScrollAwareFABBehavior.this.mIsAnimatingOut = false;
view.setVisibility(View.GONE);
}
}).start();
}
// Same animation that FloatingActionButton.Behavior uses to show the FAB when the AppBarLayout enters
private void animateIn(FloatingActionButton button) {
button.setVisibility(View.VISIBLE);
ViewCompat.animate(button).translationY(0)
.setInterpolator(INTERPOLATOR).withLayer().setListener(null)
.start();
}
private int getMarginBottom(View v) {
int marginBottom = 0;
final ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
marginBottom = ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin;
}
return marginBottom;
}
}

View File

@ -0,0 +1,282 @@
package com.example.nanchen.aiyaschoolpush.behavior;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
import com.example.nanchen.aiyaschoolpush.R;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.behavior
* @date 2016/12/20 14:00
*/
public class SimpleViewBehavior extends CoordinatorLayout.Behavior<View> {
private static final int UNSPECIFIED_INT = Integer.MAX_VALUE;
private static final float UNSPECIFIED_FLOAT = Float.MAX_VALUE;
private static final int DEPEND_TYPE_HEIGHT = 0;
private static final int DEPEND_TYPE_WIDTH = 1;
private static final int DEPEND_TYPE_X = 2;
private static final int DEPEND_TYPE_Y = 3;
private int mDependViewId = 0; //默认没有依赖对象
private int mDependType = DEPEND_TYPE_Y; //默认按照y方向变化
private int mDependTargetX; //X方向的允许最大距离(影响动画percent)
private int mDependTargetY; //Y方向的允许最大距离(影响动画percent)
private int mDependTargetWidth; //依赖控件起始最大宽度(影响动画percent)
private int mDependTargetHeight; //依赖控件起始最大高度(影响动画percent)
private int targetX;
private int targetY;
private int targetWidth;
private int targetHeight;
private int targetBackgroundColor;
private float targetAlpha;
private float targetRotateX;
private float targetRotateY;
private int mAnimationId = 0; //自定义动画id(xml文件定义动画)
private int mDependStartX;
private int mDependStartY;
private int mDependStartWidth;
private int mDependStartHeight;
private int mStartX;
private int mStartY;
private int mStartWidth;
private int mStartHeight;
private int mStartBackgroundColor;
private float mStartAlpha;
private float mStartRotateX;
private float mStartRotateY;
private Animation mAnimation;
private boolean isPrepared;
public SimpleViewBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleViewBehavior);
mDependViewId = a.getResourceId(R.styleable.SimpleViewBehavior_svb_dependOn, mDependViewId);
mDependType = a.getInt(R.styleable.SimpleViewBehavior_svb_dependType, mDependType);
mDependTargetX = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetX, UNSPECIFIED_INT);
mDependTargetY = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetY, UNSPECIFIED_INT);
mDependTargetWidth = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetWidth, UNSPECIFIED_INT);
mDependTargetHeight = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_dependTargetHeight, UNSPECIFIED_INT);
targetX = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetX, UNSPECIFIED_INT);
targetY = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetY, UNSPECIFIED_INT);
targetWidth = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetWidth, UNSPECIFIED_INT);
targetHeight = a.getDimensionPixelOffset(R.styleable.SimpleViewBehavior_svb_targetHeight, UNSPECIFIED_INT);
targetBackgroundColor = a.getColor(R.styleable.SimpleViewBehavior_svb_targetBackgroundColor, UNSPECIFIED_INT);
targetAlpha = a.getFloat(R.styleable.SimpleViewBehavior_svb_targetAlpha, UNSPECIFIED_FLOAT);
targetRotateX = a.getFloat(R.styleable.SimpleViewBehavior_svb_targetRotateX, UNSPECIFIED_FLOAT);
targetRotateY = a.getFloat(R.styleable.SimpleViewBehavior_svb_targetRotateY, UNSPECIFIED_FLOAT);
mAnimationId = a.getResourceId(R.styleable.SimpleViewBehavior_svb_animation, mAnimationId);
a.recycle();
}
/** 初始化数据 */
private void prepare(CoordinatorLayout parent, View child, View dependency) {
mDependStartX = (int) dependency.getX();
mDependStartY = (int) dependency.getY();
mDependStartWidth = dependency.getWidth();
mDependStartHeight = dependency.getHeight();
mStartX = (int) child.getX();
mStartY = (int) child.getY();
mStartWidth = child.getWidth();
mStartHeight = child.getHeight();
mStartAlpha = child.getAlpha();
mStartRotateX = child.getRotationX();
mStartRotateY = child.getRotationY();
//特殊处理y方向变化
if (mDependTargetY == UNSPECIFIED_INT && dependency instanceof AppBarLayout) {
mDependTargetY = ((AppBarLayout) dependency).getTotalScrollRange();
}
// 背景颜色渐变
if (child.getBackground() instanceof ColorDrawable) mStartBackgroundColor = ((ColorDrawable) child.getBackground()).getColor();
// 自定义动画
if (mAnimationId != 0) {
mAnimation = AnimationUtils.loadAnimation(child.getContext(), mAnimationId);
mAnimation.initialize(child.getWidth(), child.getHeight(), parent.getWidth(), parent.getHeight());
}
// 兼容5.0以上的沉浸模式
if (Build.VERSION.SDK_INT > 16 && parent.getFitsSystemWindows() && targetY != UNSPECIFIED_INT) {
targetY += getStatusBarHeight(parent.getContext());
}
isPrepared = true;
}
/**
* child 是指应用behavior的View dependency 担任触发behavior的角色并与child进行互动
* layoutDependsOn方法在每次layout发生变化时都会调用我们需要在dependency控件发生变化时返回True
* 在我们的例子中是用户在屏幕上滑动时因为AppBarLayout发生了移动然后我们需要让child做出相应的反应
*/
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency.getId() == mDependViewId;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
// 该方法会在滑动的时候一直回调,但只需要初始化一次
if (!isPrepared) prepare(parent, child, dependency);
updateView(child, dependency);
return false;
}
/**
* 这个是CoordinatorLayout在进行measure的过程中利用Behavior对象对子view进行大小测量的一个方法
* 在这个方法内我们可以通过parent.getDependencies(child);这个方法获取到这个child依赖的view然后通过获取这个child依赖的view的大小来决定自身的大小
*/
@Override
public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
}
/**
* 这个方法是用来子view用来布局自身使用如果依赖其他view那么系统会首先调用
* public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency)
* 这个方法可以在这个回调中记录dependency的一些位置信息在onLayoutChild中利用保存下来的信息进行计算然后得到自身的具体位置
*/
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
boolean bool = super.onLayoutChild(parent, child, layoutDirection);
if (isPrepared) updateView(child, parent.getDependencies(child).get(0));
return bool;
}
public void updateView(View child, View dependency) {
float percent = 0;
float start = 0;
float current = 0;
float end = UNSPECIFIED_INT;
switch (mDependType) {
case DEPEND_TYPE_WIDTH:
start = mDependStartWidth;
current = dependency.getWidth();
end = mDependTargetWidth;
break;
case DEPEND_TYPE_HEIGHT:
start = mDependStartHeight;
current = dependency.getHeight();
end = mDependTargetHeight;
break;
case DEPEND_TYPE_X:
start = mDependStartX;
current = dependency.getX();
end = mDependTargetX;
break;
case DEPEND_TYPE_Y:
start = mDependStartY;
current = dependency.getY();
end = mDependTargetY;
break;
}
if (end != UNSPECIFIED_INT) {
percent = Math.abs(current - start) / Math.abs(end - start);
}
updateViewWithPercent(child, percent > 1 ? 1 : percent);
}
/** 更新View */
@SuppressWarnings("ResourceType")
public void updateViewWithPercent(View child, float percent) {
if (mAnimation == null) {
//如果没有自定义动画,那么使用属性动画
float newX = targetX == UNSPECIFIED_INT ? 0 : (targetX - mStartX) * percent;
float newY = targetY == UNSPECIFIED_INT ? 0 : (targetY - mStartY) * percent;
//缩放动画
if (targetWidth != UNSPECIFIED_INT || targetHeight != UNSPECIFIED_INT) {
child.setScaleX(scaleEvaluator(mStartWidth, targetWidth, percent));
child.setScaleY(scaleEvaluator(mStartHeight, targetHeight, percent));
float newWidth = floatEvaluator(mStartWidth, targetWidth, percent);
float newHeight = floatEvaluator(mStartWidth, targetWidth, percent);
newX -= (mStartWidth - newWidth) / 2;
newY -= (mStartHeight - newHeight) / 2;
}
//平移动画
child.setTranslationX(newX);
child.setTranslationY(newY);
//透明度变化
if (targetAlpha != UNSPECIFIED_FLOAT) child.setAlpha(floatEvaluator(mStartAlpha, targetAlpha, percent));
//背景渐变
if (targetBackgroundColor != UNSPECIFIED_INT && mStartBackgroundColor != 0) {
child.setBackgroundColor(argbEvaluator(mStartBackgroundColor, targetBackgroundColor, percent));
}
//旋转动画
if (targetRotateX != UNSPECIFIED_FLOAT) child.setRotationX(floatEvaluator(mStartRotateX, targetRotateX, percent));
if (targetRotateY != UNSPECIFIED_FLOAT) child.setRotationY(floatEvaluator(mStartRotateY, targetRotateY, percent));
} else {
mAnimation.setStartTime(0);
mAnimation.restrictDuration(100);
Transformation transformation = new Transformation();
mAnimation.getTransformation((long) (percent * 100), transformation);
BehaviorAnimation animation = new BehaviorAnimation(transformation);
child.startAnimation(animation);
}
child.requestLayout();
}
private static class BehaviorAnimation extends Animation {
private Transformation mTransformation;
public BehaviorAnimation(Transformation transformation) {
mTransformation = transformation;
setDuration(0);
setFillAfter(true);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
t.compose(mTransformation);
super.applyTransformation(interpolatedTime, t);
}
}
public static float floatEvaluator(float originalSize, float finalSize, float percent) {
return (finalSize - originalSize) * percent + originalSize;
}
public static float scaleEvaluator(float originalSize, float finalSize, float percent) {
float calcSize = (finalSize - originalSize) * percent + originalSize;
return calcSize / originalSize;
}
public static int argbEvaluator(int startColor, int endColor, float percent) {
int startA = (startColor >> 24) & 0xff;
int startR = (startColor >> 16) & 0xff;
int startG = (startColor >> 8) & 0xff;
int startB = startColor & 0xff;
int endA = (endColor >> 24) & 0xff;
int endR = (endColor >> 16) & 0xff;
int endG = (endColor >> 8) & 0xff;
int endB = endColor & 0xff;
return ((startA + (int) (percent * (endA - startA))) << 24) |
((startR + (int) (percent * (endR - startR))) << 16) |
((startG + (int) (percent * (endG - startG))) << 8) |
((startB + (int) (percent * (endB - startB))));
}
/** 获取状态栏的高度 */
private static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}

View File

@ -0,0 +1,14 @@
package com.example.nanchen.aiyaschoolpush.config;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.config
* @date 2016/10/13 10:34
*/
public class AddConfig {
public static final String NOTICE = "notice"; // 公告
public static final String HOMEWORK = "homework"; // 作业
public static final String COMMUNITY = "community"; // 社区
}

View File

@ -0,0 +1,31 @@
package com.example.nanchen.aiyaschoolpush.config;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.config
* @date 2016/11/09 11:34
*
* 系统常量定义类此类仅放常量定义不放任何逻辑代码
*
*/
public final class Consts {
/**
* 内网API接口主机名
*/
// public final static String API_SERVICE_HOST = "http://10.1.1.119:80/AiYaSchoolPush";
/**
* 外网API接口主机名
*/
public final static String API_SERVICE_HOST = "http://azhinj.ticp.io:10277/AiYaSchoolPush";
/**
* 用户是教师
*/
public final static int USER_TYPE_TEACHER = 2;
/**
* 用户是学生或家长
*/
public final static int USER_TYPE_STUDENT = 1;
}

View File

@ -0,0 +1,12 @@
package com.example.nanchen.aiyaschoolpush.config;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.config
* @date 2016/10/24 11:42
*/
public class WeatherConfig {
public static final String APP_KEY = "6601d195ad4e4e5eb21688df3d762fa7";
}

View File

@ -0,0 +1,117 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.example.nanchen.aiyaschoolpush.helper.DemoHelper;
public class DbOpenHelper extends SQLiteOpenHelper{
private static final int DATABASE_VERSION = 6;
private static DbOpenHelper instance;
private static final String USERNAME_TABLE_CREATE = "CREATE TABLE "
+ UserDao.TABLE_NAME + " ("
+ UserDao.COLUMN_NAME_NICK + " TEXT, "
+ UserDao.COLUMN_NAME_AVATAR + " TEXT, "
+ UserDao.COLUMN_NAME_ID + " TEXT PRIMARY KEY);";
private static final String INIVTE_MESSAGE_TABLE_CREATE = "CREATE TABLE "
+ InviteMessgeDao.TABLE_NAME + " ("
+ InviteMessgeDao.COLUMN_NAME_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ InviteMessgeDao.COLUMN_NAME_FROM + " TEXT, "
+ InviteMessgeDao.COLUMN_NAME_GROUP_ID + " TEXT, "
+ InviteMessgeDao.COLUMN_NAME_GROUP_Name + " TEXT, "
+ InviteMessgeDao.COLUMN_NAME_REASON + " TEXT, "
+ InviteMessgeDao.COLUMN_NAME_STATUS + " INTEGER, "
+ InviteMessgeDao.COLUMN_NAME_ISINVITEFROMME + " INTEGER, "
+ InviteMessgeDao.COLUMN_NAME_UNREAD_MSG_COUNT + " INTEGER, "
+ InviteMessgeDao.COLUMN_NAME_TIME + " TEXT, "
+ InviteMessgeDao.COLUMN_NAME_GROUPINVITER + " TEXT); ";
private static final String ROBOT_TABLE_CREATE = "CREATE TABLE "
+ UserDao.ROBOT_TABLE_NAME + " ("
+ UserDao.ROBOT_COLUMN_NAME_ID + " TEXT PRIMARY KEY, "
+ UserDao.ROBOT_COLUMN_NAME_NICK + " TEXT, "
+ UserDao.ROBOT_COLUMN_NAME_AVATAR + " TEXT);";
private static final String CREATE_PREF_TABLE = "CREATE TABLE "
+ UserDao.PREF_TABLE_NAME + " ("
+ UserDao.COLUMN_NAME_DISABLED_GROUPS + " TEXT, "
+ UserDao.COLUMN_NAME_DISABLED_IDS + " TEXT);";
private DbOpenHelper(Context context) {
super(context, getUserDatabaseName(), null, DATABASE_VERSION);
}
public static DbOpenHelper getInstance(Context context) {
if (instance == null) {
instance = new DbOpenHelper(context.getApplicationContext());
}
return instance;
}
private static String getUserDatabaseName() {
return DemoHelper.getInstance().getCurrentUserName() + "_demo.db";
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(USERNAME_TABLE_CREATE);
db.execSQL(INIVTE_MESSAGE_TABLE_CREATE);
db.execSQL(CREATE_PREF_TABLE);
db.execSQL(ROBOT_TABLE_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if(oldVersion < 2){
db.execSQL("ALTER TABLE "+ UserDao.TABLE_NAME +" ADD COLUMN "+
UserDao.COLUMN_NAME_AVATAR + " TEXT ;");
}
if(oldVersion < 3){
db.execSQL(CREATE_PREF_TABLE);
}
if(oldVersion < 4){
db.execSQL(ROBOT_TABLE_CREATE);
}
if(oldVersion < 5){
db.execSQL("ALTER TABLE " + InviteMessgeDao.TABLE_NAME + " ADD COLUMN " +
InviteMessgeDao.COLUMN_NAME_UNREAD_MSG_COUNT + " INTEGER ;");
}
if (oldVersion < 6) {
db.execSQL("ALTER TABLE " + InviteMessgeDao.TABLE_NAME + " ADD COLUMN " +
InviteMessgeDao.COLUMN_NAME_GROUPINVITER + " TEXT;");
}
}
public void closeDB() {
if (instance != null) {
try {
SQLiteDatabase db = instance.getWritableDatabase();
db.close();
} catch (Exception e) {
e.printStackTrace();
}
instance = null;
}
}
}

View File

@ -0,0 +1,309 @@
package com.example.nanchen.aiyaschoolpush.db;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.example.nanchen.aiyaschoolpush.im.Constant;
import com.example.nanchen.aiyaschoolpush.im.InviteMesageStatus;
import com.example.nanchen.aiyaschoolpush.im.InviteMessage;
import com.hyphenate.easeui.domain.EaseUser;
import com.hyphenate.easeui.utils.EaseCommonUtils;
import com.example.nanchen.aiyaschoolpush.App;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
public class DemoDBManager {
static private DemoDBManager dbMgr = new DemoDBManager();
private com.example.nanchen.aiyaschoolpush.db.DbOpenHelper dbHelper;
private DemoDBManager(){
dbHelper = com.example.nanchen.aiyaschoolpush.db.DbOpenHelper.getInstance(App.getInstance().getApplicationContext());
}
public static synchronized DemoDBManager getInstance(){
if(dbMgr == null){
dbMgr = new DemoDBManager();
}
return dbMgr;
}
/**
* save contact list
*
* @param contactList
*/
synchronized public void saveContactList(List<EaseUser> contactList) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
if (db.isOpen()) {
db.delete(com.example.nanchen.aiyaschoolpush.db.UserDao.TABLE_NAME, null, null);
for (EaseUser user : contactList) {
ContentValues values = new ContentValues();
values.put(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_ID, user.getUsername());
if(user.getNick() != null)
values.put(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_NICK, user.getNick());
if(user.getAvatar() != null)
values.put(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_AVATAR, user.getAvatar());
db.replace(com.example.nanchen.aiyaschoolpush.db.UserDao.TABLE_NAME, null, values);
}
}
}
/**
* get contact list
*
* @return
*/
synchronized public Map<String, EaseUser> getContactList() {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Map<String, EaseUser> users = new Hashtable<String, EaseUser>();
if (db.isOpen()) {
Cursor cursor = db.rawQuery("select * from " + com.example.nanchen.aiyaschoolpush.db.UserDao.TABLE_NAME /* + " desc" */, null);
while (cursor.moveToNext()) {
String username = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_ID));
String nick = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_NICK));
String avatar = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_AVATAR));
EaseUser user = new EaseUser(username);
user.setNick(nick);
user.setAvatar(avatar);
if (username.equals(Constant.NEW_FRIENDS_USERNAME) || username.equals(Constant.GROUP_USERNAME)
|| username.equals(Constant.CHAT_ROOM)|| username.equals(Constant.CHAT_ROBOT)) {
user.setInitialLetter("");
} else {
EaseCommonUtils.setUserInitialLetter(user);
}
users.put(username, user);
}
cursor.close();
}
return users;
}
/**
* delete a contact
* @param username
*/
synchronized public void deleteContact(String username){
SQLiteDatabase db = dbHelper.getWritableDatabase();
if(db.isOpen()){
db.delete(com.example.nanchen.aiyaschoolpush.db.UserDao.TABLE_NAME, com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_ID + " = ?", new String[]{username});
}
}
/**
* save a contact
* @param user
*/
synchronized public void saveContact(EaseUser user){
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_ID, user.getUsername());
if(user.getNick() != null)
values.put(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_NICK, user.getNick());
if(user.getAvatar() != null)
values.put(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_AVATAR, user.getAvatar());
if(db.isOpen()){
db.replace(com.example.nanchen.aiyaschoolpush.db.UserDao.TABLE_NAME, null, values);
}
}
public void setDisabledGroups(List<String> groups){
setList(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_DISABLED_GROUPS, groups);
}
public List<String> getDisabledGroups(){
return getList(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_DISABLED_GROUPS);
}
public void setDisabledIds(List<String> ids){
setList(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_DISABLED_IDS, ids);
}
public List<String> getDisabledIds(){
return getList(com.example.nanchen.aiyaschoolpush.db.UserDao.COLUMN_NAME_DISABLED_IDS);
}
synchronized private void setList(String column, List<String> strList){
StringBuilder strBuilder = new StringBuilder();
for(String hxid:strList){
strBuilder.append(hxid).append("$");
}
SQLiteDatabase db = dbHelper.getWritableDatabase();
if (db.isOpen()) {
ContentValues values = new ContentValues();
values.put(column, strBuilder.toString());
db.update(com.example.nanchen.aiyaschoolpush.db.UserDao.PREF_TABLE_NAME, values, null,null);
}
}
synchronized private List<String> getList(String column){
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select " + column + " from " + com.example.nanchen.aiyaschoolpush.db.UserDao.PREF_TABLE_NAME,null);
if (!cursor.moveToFirst()) {
cursor.close();
return null;
}
String strVal = cursor.getString(0);
if (strVal == null || strVal.equals("")) {
return null;
}
cursor.close();
String[] array = strVal.split("$");
if(array.length > 0){
List<String> list = new ArrayList<String>();
Collections.addAll(list, array);
return list;
}
return null;
}
/**
* save a message
* @param message
* @return return cursor of the message
*/
public synchronized Integer saveMessage(InviteMessage message){
SQLiteDatabase db = dbHelper.getWritableDatabase();
int id = -1;
if(db.isOpen()){
ContentValues values = new ContentValues();
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_FROM, message.getFrom());
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_GROUP_ID, message.getGroupId());
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_GROUP_Name, message.getGroupName());
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_REASON, message.getReason());
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_TIME, message.getTime());
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_STATUS, message.getStatus().ordinal());
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_GROUPINVITER, message.getGroupInviter());
db.insert(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.TABLE_NAME, null, values);
Cursor cursor = db.rawQuery("select last_insert_rowid() from " + com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.TABLE_NAME,null);
if(cursor.moveToFirst()){
id = cursor.getInt(0);
}
cursor.close();
}
return id;
}
/**
* update message
* @param msgId
* @param values
*/
synchronized public void updateMessage(int msgId,ContentValues values){
SQLiteDatabase db = dbHelper.getWritableDatabase();
if(db.isOpen()){
db.update(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.TABLE_NAME, values, com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_ID + " = ?", new String[]{String.valueOf(msgId)});
}
}
/**
* get messges
* @return
*/
synchronized public List<InviteMessage> getMessagesList(){
SQLiteDatabase db = dbHelper.getReadableDatabase();
List<InviteMessage> msgs = new ArrayList<InviteMessage>();
if(db.isOpen()){
Cursor cursor = db.rawQuery("select * from " + com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.TABLE_NAME + " desc",null);
while(cursor.moveToNext()){
InviteMessage msg = new InviteMessage();
int id = cursor.getInt(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_ID));
String from = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_FROM));
String groupid = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_GROUP_ID));
String groupname = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_GROUP_Name));
String reason = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_REASON));
long time = cursor.getLong(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_TIME));
int status = cursor.getInt(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_STATUS));
String groupInviter = cursor.getString(cursor.getColumnIndex(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_GROUPINVITER));
msg.setId(id);
msg.setFrom(from);
msg.setGroupId(groupid);
msg.setGroupName(groupname);
msg.setReason(reason);
msg.setTime(time);
msg.setGroupInviter(groupInviter);
if(status == InviteMesageStatus.BEINVITEED.ordinal())
msg.setStatus(InviteMesageStatus.BEINVITEED);
else if(status == InviteMesageStatus.BEAGREED.ordinal())
msg.setStatus(InviteMesageStatus.BEAGREED);
else if(status == InviteMesageStatus.BEREFUSED.ordinal())
msg.setStatus(InviteMesageStatus.BEREFUSED);
else if(status == InviteMesageStatus.AGREED.ordinal())
msg.setStatus(InviteMesageStatus.AGREED);
else if(status == InviteMesageStatus.REFUSED.ordinal())
msg.setStatus(InviteMesageStatus.REFUSED);
else if(status == InviteMesageStatus.BEAPPLYED.ordinal())
msg.setStatus(InviteMesageStatus.BEAPPLYED);
else if(status == InviteMesageStatus.GROUPINVITATION.ordinal())
msg.setStatus(InviteMesageStatus.GROUPINVITATION);
else if(status == InviteMesageStatus.GROUPINVITATION_ACCEPTED.ordinal())
msg.setStatus(InviteMesageStatus.GROUPINVITATION_ACCEPTED);
else if(status == InviteMesageStatus.GROUPINVITATION_DECLINED.ordinal())
msg.setStatus(InviteMesageStatus.GROUPINVITATION_DECLINED);
msgs.add(msg);
}
cursor.close();
}
return msgs;
}
/**
* delete invitation message
* @param from
*/
synchronized public void deleteMessage(String from){
SQLiteDatabase db = dbHelper.getWritableDatabase();
if(db.isOpen()){
db.delete(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.TABLE_NAME, com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_FROM + " = ?", new String[]{from});
}
}
synchronized int getUnreadNotifyCount(){
int count = 0;
SQLiteDatabase db = dbHelper.getReadableDatabase();
if(db.isOpen()){
Cursor cursor = db.rawQuery("select " + com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_UNREAD_MSG_COUNT + " from " + com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.TABLE_NAME, null);
if(cursor.moveToFirst()){
count = cursor.getInt(0);
}
cursor.close();
}
return count;
}
synchronized void setUnreadNotifyCount(int count){
SQLiteDatabase db = dbHelper.getWritableDatabase();
if(db.isOpen()){
ContentValues values = new ContentValues();
values.put(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.COLUMN_NAME_UNREAD_MSG_COUNT, count);
db.update(com.example.nanchen.aiyaschoolpush.db.InviteMessgeDao.TABLE_NAME, values, null,null);
}
}
synchronized public void closeDB(){
if(dbHelper != null){
dbHelper.closeDB();
}
dbMgr = null;
}
}

View File

@ -0,0 +1,80 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.db;
import android.content.ContentValues;
import android.content.Context;
import com.example.nanchen.aiyaschoolpush.im.InviteMessage;
import java.util.List;
public class InviteMessgeDao {
static final String TABLE_NAME = "new_friends_msgs";
static final String COLUMN_NAME_ID = "id";
static final String COLUMN_NAME_FROM = "username";
static final String COLUMN_NAME_GROUP_ID = "groupid";
static final String COLUMN_NAME_GROUP_Name = "groupname";
static final String COLUMN_NAME_TIME = "time";
static final String COLUMN_NAME_REASON = "reason";
public static final String COLUMN_NAME_STATUS = "status";
static final String COLUMN_NAME_ISINVITEFROMME = "isInviteFromMe";
static final String COLUMN_NAME_GROUPINVITER = "groupinviter";
static final String COLUMN_NAME_UNREAD_MSG_COUNT = "unreadMsgCount";
public InviteMessgeDao(Context context){
}
/**
* save message
* @param message
* @return return cursor of the message
*/
public Integer saveMessage(InviteMessage message){
return DemoDBManager.getInstance().saveMessage(message);
}
/**
* update message
* @param msgId
* @param values
*/
public void updateMessage(int msgId,ContentValues values){
DemoDBManager.getInstance().updateMessage(msgId, values);
}
/**
* get messges
* @return
*/
public List<InviteMessage> getMessagesList(){
return DemoDBManager.getInstance().getMessagesList();
}
public void deleteMessage(String from){
DemoDBManager.getInstance().deleteMessage(from);
}
public int getUnreadMessagesCount(){
return DemoDBManager.getInstance().getUnreadNotifyCount();
}
public void saveUnreadMessageCount(int count){
DemoDBManager.getInstance().setUnreadNotifyCount(count);
}
}

View File

@ -0,0 +1,93 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.db;
import android.content.Context;
import com.hyphenate.easeui.domain.EaseUser;
import java.util.List;
import java.util.Map;
public class UserDao {
public static final String TABLE_NAME = "uers";
public static final String COLUMN_NAME_ID = "username";
public static final String COLUMN_NAME_NICK = "nick";
public static final String COLUMN_NAME_AVATAR = "avatar";
public static final String PREF_TABLE_NAME = "pref";
public static final String COLUMN_NAME_DISABLED_GROUPS = "disabled_groups";
public static final String COLUMN_NAME_DISABLED_IDS = "disabled_ids";
public static final String ROBOT_TABLE_NAME = "robots";
public static final String ROBOT_COLUMN_NAME_ID = "username";
public static final String ROBOT_COLUMN_NAME_NICK = "nick";
public static final String ROBOT_COLUMN_NAME_AVATAR = "avatar";
public UserDao(Context context) {
}
/**
* save contact list
*
* @param contactList
*/
public void saveContactList(List<EaseUser> contactList) {
DemoDBManager.getInstance().saveContactList(contactList);
}
/**
* get contact list
*
* @return
*/
public Map<String, EaseUser> getContactList() {
return DemoDBManager.getInstance().getContactList();
}
/**
* delete a contact
* @param username
*/
public void deleteContact(String username){
DemoDBManager.getInstance().deleteContact(username);
}
/**
* save a contact
* @param user
*/
public void saveContact(EaseUser user){
DemoDBManager.getInstance().saveContact(user);
}
public void setDisabledGroups(List<String> groups){
DemoDBManager.getInstance().setDisabledGroups(groups);
}
public List<String> getDisabledGroups(){
return DemoDBManager.getInstance().getDisabledGroups();
}
public void setDisabledIds(List<String> ids){
DemoDBManager.getInstance().setDisabledIds(ids);
}
public List<String> getDisabledIds(){
return DemoDBManager.getInstance().getDisabledIds();
}
}

View File

@ -0,0 +1,47 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.helper;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.example.nanchen.aiyaschoolpush.ui.activity.VideoCallActivity;
import com.example.nanchen.aiyaschoolpush.ui.activity.VoiceCallActivity;
import com.hyphenate.util.EMLog;
public class CallReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
if(!DemoHelper.getInstance().isLoggedIn())
return;
//username
String from = intent.getStringExtra("from");
//call type
String type = intent.getStringExtra("type");
if("video".equals(type)){ //video call
context.startActivity(new Intent(context, VideoCallActivity.class).
putExtra("username", from).putExtra("isComingCall", true).
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}else{ //voice call
context.startActivity(new Intent(context, VoiceCallActivity.class).
putExtra("username", from).putExtra("isComingCall", true).
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
EMLog.d("CallReceiver", "app received a incoming call");
}
}

View File

@ -0,0 +1,19 @@
package com.example.nanchen.aiyaschoolpush.helper;
import java.io.Closeable;
/**
* 语法简化器
*
* */
public class CoderHelper {
public static void close(Closeable closable) {
if (closable != null) {
try {
closable.close();
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,39 @@
package com.example.nanchen.aiyaschoolpush.helper;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import com.example.nanchen.aiyaschoolpush.App;
/**
* 表示当前应用及系统的环境信息
*
* */
public class Environment2 {
/**
* 获取Application Context
* */
public static Context getAppContext() {
return App.getAppContext();
}
public static int getPackageVersionCode() {
try {
Context ctx = App.getAppContext();
return ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
return 1;
}
}
public static String getPackageVersionName() {
try {
Context ctx = App.getAppContext();
return ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0).versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
return "1.0";
}
}
}

View File

@ -0,0 +1,364 @@
package com.example.nanchen.aiyaschoolpush.helper;
import android.content.Context;
import android.media.ExifInterface;
import android.os.Environment;
import android.os.StatFs;
import com.example.nanchen.aiyaschoolpush.App;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 文件工具类
*
* @author Frank
* @email frank@mondol.info
* @create_date 2015-2
*/
public class FileHelper {
public static final String AUDIO = Environment.DIRECTORY_MUSIC;
public static final String IMAGE = Environment.DIRECTORY_PICTURES;
public static final String OTHER = Environment.DIRECTORY_PICTURES;
static boolean mExternalStorageAvailable = false;
static boolean mExternalStorageWriteable = false;
/**
* 检索当前系统是否包含扩展存储卡
*
* @return 0:没有存储卡|1:有只读存储卡|2:有可读写存储卡
*/
public static int hasExternalStorage() {
final String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
return 2;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return 1;
} else {
// 其它状态是错误的即不能读也不能写
return 0;
}
}
/**
* 获取扩展存储卡剩余空间
*/
public static long getAvailableExternalStorageSize() {
if (2 == hasExternalStorage()) {
StatFs localStatFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
long blockSize = localStatFs.getBlockSize();
long blockCount = localStatFs.getAvailableBlocks();
return blockSize * blockCount;
}
return 0;
}
/**
* 复制一个文件
*/
public static int copyFile(String fromFile, String toFile) {
try {
InputStream fosfrom = new FileInputStream(fromFile);
FileOutputStream fosto = new FileOutputStream(toFile);
byte[] bys = new byte[4096];
int c;
while ((c = fosfrom.read(bys)) > 0) {
fosto.write(bys, 0, c);
}
fosfrom.close();
fosto.close();
return 0;
} catch (Exception ex) {
return -1;
}
}
/**
* 获取files目录优先返回存储卡中的目录
*/
public static File getFilesDir() {
Context ctx = Environment2.getAppContext();
if (2 == hasExternalStorage()) {
return ctx.getExternalFilesDir(null);
} else {
return ctx.getFilesDir();
}
}
/**
* 获取缓存路径优先返回存储卡中的目录
*/
public static File getCacheDir() {
Context ctx = Environment2.getAppContext();
if (2 == hasExternalStorage()) {
return ctx.getExternalCacheDir();
} else {
return ctx.getCacheDir();
}
}
/**
* 返回指定目录中一个唯一的空文件
* <p/>
* 方法成功返回时文件已创建用后请删除
*
* @param fDir 文件目录 如果为null则会在扩展存储卡中的CacheDir中生成文件
* 如果扩展存储卡不存在则在应用CacheDir中生成文件
* @param suffix 文件后缀 null则使用.tmp
* @return 返回则返回null
*/
public static File createTempFile(File fDir, String suffix) {
Context context = Environment2.getAppContext();
if (fDir == null) {
if (2 == hasExternalStorage()) {
fDir = context.getExternalCacheDir();
} else {
fDir = context.getCacheDir();
}
}
SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHH");
String prefix = sdf.format(new Date());
try {
return File.createTempFile(prefix, suffix, fDir);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 获取一个空的临时文件
*
* @throws IOException
*/
public static File createTempFile(String suffix) {
return createTempFile(null, suffix);
}
/**
* 获取随机图片文件名
*
* @return
* @author Williams
*/
static public File getImageFilePath() {
String suffix = ".jpg";
File dir = getDirByType(IMAGE);
File file = getFile(suffix, dir);
return file;
}
/**
* 获取图片和音频外的文件名
*
* @return
* @author Williams
*/
static public File getOtherFilePath(String sufix) {
String suffix = "." + sufix;
File dir = getDirByType(OTHER);
File file = getFile(suffix, dir);
return file;
}
/**
* 获取音频随机文件名
*
* @return
* @author Williams
*/
static public File getAudioFilePath() {
String suffix = ".amr";
File dir = getDirByType(AUDIO);
File file = getFile(suffix, dir);
return file;
}
/**
* 获取音频随机文件名
*
* @return
* @author Williams
*/
public static File getFile(String suffix, File dir) {
if (dir == null) {
throw new NullPointerException("Dir can not be null!");
} else if (!dir.exists()) {
dir.mkdirs();
}
try {
return File.createTempFile("fx_temp_", suffix, dir);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* 获取图片文件扩展信息(方向)
*
* @return int 图片的旋转角度
* @author Williams
*/
public static int getExifOrientation(String filepath) {
int degree = 0;
ExifInterface exif = null;
try {
exif = new ExifInterface(filepath);
} catch (IOException ex) {
}
if (exif != null) {
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
if (orientation != -1) {
// We only recognize a subset of orientation tag values.
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;
}
}
}
return degree;
}
/**
* 检测外部存储卡状态
*
* @return
* @author Williams
*/
private static void checkStorageState() {
final String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// 外部存储卡可读写
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// 外部存储卡只读
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// 外部存储卡不可用
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
}
private static Context getContext() {
return App.getAppContext().getApplicationContext();
}
/**
* 获取音频随机文件
*
* @return
* @author Williams
*/
public static File getAudioDir() {
return getDirByType(AUDIO);
}
/**
* 获取图片随机文件
*
* @return
* @author Williams
*/
public static File getImageDir() {
return getDirByType(IMAGE);
}
/**
* @return
* @author Williams
*/
static public File getDirByType(String typeName) {
checkStorageState();
final Context context = getContext();
if (mExternalStorageWriteable) {
return context.getExternalFilesDir(typeName);
} else {
return context.getCacheDir();
}
}
/**
* @return
* @author WLF
*/
public static File getFileDir() {
checkStorageState();
final Context context = getContext();
if (mExternalStorageWriteable) {
return context.getExternalFilesDir(null);
} else {
return context.getCacheDir();
}
}
/**
* 检测SD卡是否可写
*
* @return
* @author WLF
*/
public static boolean isSdcardWriteable() {
checkStorageState();
return mExternalStorageWriteable;
}
/**
* 检测文件是否可用
*
* @return
* @author WLF
*/
public static boolean checkFile(File f) {
if (f != null && f.exists() && f.canRead() && (f.isDirectory() || (f.isFile() && f.length() > 0))) {
return true;
}
return false;
}
/**
* 检测文件是否可用
*
* @return
* @author WLF
*/
public static boolean checkFile(String path) {
if (StringHelper.isNotEmpty(path)) {
File f = new File(path);
if (f != null && f.exists() && f.canRead() && (f.isDirectory() || (f.isFile() && f.length() > 0)))
return true;
}
return false;
}
public static boolean tryDeleteFile(File file) {
try {
file.delete();
return true;
} catch (Throwable t) {
return false;
}
}
}

View File

@ -0,0 +1,39 @@
package com.example.nanchen.aiyaschoolpush.helper;
import android.content.Context;
import android.graphics.Typeface;
/**
* 统一 字体加载类
*
* 第一次加载在MainActivity 中调用
* 字体加载为相对耗时操作 所以做成单例
*/
public class FontCustomHelper {
private Typeface typeface;
private static final String FONT_URL = "fonts/icomoon.ttf";
private static class Holder{
private static FontCustomHelper helper = new FontCustomHelper();
}
public static FontCustomHelper getInstance(){
return Holder.helper;
}
public void init(Context mContext){
typeface = Typeface.createFromAsset(mContext.getAssets(), FONT_URL);
}
public Typeface getTypeface(Context mContext) {
if (null==typeface) {
init(mContext);
}
return typeface;
}
public void setTypeface(Typeface typeface) {
this.typeface = typeface;
}
}

View File

@ -0,0 +1,120 @@
package com.example.nanchen.aiyaschoolpush.helper;
import android.text.TextUtils;
import java.io.File;
import java.io.IOException;
/**
* 本地存储工具类
*
* @author Frank
* @email frank@mondol.info
* @create_date 2015年4月7日
*/
public class LocalStorageHelper {
private static final String FILE_TAG_IMAGE = "image";
private static final String FILE_TAG_AUDIO = "audio";
private static final String FILE_TAG_VIDEO = "video";
static File mFilesDir;
static File mUsersDir;
/**
* 创建一个空的临时文件用后请删除
*
* @param suffix
* 文件扩展名传null则使用.tmp
* @return null则失败
*/
public static File createTempFile(String suffix) {
return FileHelper.createTempFile(suffix);
}
/**
* 创建班级圈视频
*
* 返回文件路径不需要请删除
*/
public static File createGroupVideoFile(long groupId) {
return createUserTempFile("circle_" + groupId, FILE_TAG_VIDEO, ".mp4");
}
/**
* 创建一个空的讨论组图标文件
*/
public static File createDiscussGroupIconFile(String jid) {
return createUserTempFile("group_" + jid, null, ".jpg");
}
/**
* 创建一个空的用于聊天的图片文件
*
* @param userId
* 用户ID
*/
public static File createChatImageFile(long userId) {
return createUserTempFile("chat_" + userId, FILE_TAG_IMAGE, ".jpg");
}
/**
* 创建一个空的用于启动页广告图片文件
*/
public static File createAdImageFile() {
return createUserTempFile("start_ad", FILE_TAG_IMAGE, ".jpg");
}
/**
* 创建一个空的用于聊天的图片文件
*
* @param jid
* 讨论组ID
* @return
*/
public static File createChatImageFile(String jid) {
return createUserTempFile("chat_" + jid, FILE_TAG_IMAGE, ".jpg");
}
/**
* 创建一个空的用于聊天的语音文件
*
* @param userId
* @return
*/
public static File createChatAudioFile(long userId) {
return createUserTempFile("chat_" + userId, FILE_TAG_AUDIO, ".amr");
}
/**
* 创建一个空的用于聊天的语音文件
*
* @param jid
* @return
*/
public static File createChatAudioFile(String jid) {
return createUserTempFile("chat_" + jid, FILE_TAG_AUDIO, ".amr");
}
private static File createUserTempFile(String uidOrJid, String fileTag, String suffix) {
if (mFilesDir == null)
mFilesDir = FileHelper.getFilesDir();
if (mUsersDir == null) {
mUsersDir = new File(mFilesDir, "users");
mUsersDir.mkdir();
}
String userDir = TextUtils.isEmpty(uidOrJid) ? "empty" : StringHelper.getMD5String(uidOrJid);
File fDir = new File(mUsersDir, userDir);
fDir.mkdir();
if (fileTag != null) {
fDir = new File(fDir, fileTag);
fDir.mkdir();
}
try {
return File.createTempFile(fileTag != null ? fileTag : "file", suffix, fDir);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,225 @@
package com.example.nanchen.aiyaschoolpush.helper;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.alibaba.fastjson.JSONArray;
import com.example.nanchen.aiyaschoolpush.R;
import com.qiyukf.nimlib.sdk.NIMClient;
import com.qiyukf.nimlib.sdk.msg.MsgService;
import com.qiyukf.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.qiyukf.unicorn.api.ImageLoaderListener;
import com.qiyukf.unicorn.api.StatusBarNotificationConfig;
import com.qiyukf.unicorn.api.UICustomization;
import com.qiyukf.unicorn.api.Unicorn;
import com.qiyukf.unicorn.api.UnicornImageLoader;
import com.qiyukf.unicorn.api.YSFOptions;
import com.qiyukf.unicorn.api.YSFUserInfo;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.RequestCreator;
import com.squareup.picasso.Target;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashSet;
import java.util.Set;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.helper
* @date 2016/11/04 15:47
* <p>
* 七鱼云客服相关帮助类
*/
public class QiYuCloudServerHelper {
private static Context mContext;
private static YSFOptions mOptins;
private final static String APP_KEY = "2c170d44ffed6896868d56a13a09f63c";
/**
* 初始化七鱼云客服
*/
public static void initCloudServer(Application app) {
mContext = app;
Unicorn.init(app, APP_KEY, getServerOptions(), new PicassoImageLoader());
}
private static YSFOptions getServerOptions() {
if (mOptins == null) {
YSFOptions options = new YSFOptions();
// 通知栏提醒
options.statusBarNotificationConfig = new StatusBarNotificationConfig();
options.statusBarNotificationConfig.notificationSmallIconId = R.mipmap.icon_notify;
// UI定制 头像版本更新不再支持drawable://格式
UICustomization customization = new UICustomization();
// customization.msgBackgroundColor = mContext.getResources().getColor(R.color.main_bg_color);
// customization.tipsTextColor = mContext.getResources().getColor(R.color.gray3);
// customization.msgListViewDividerHeight = 40;
// customization.leftAvatar = "file://" + R.drawable.service;
// customization.tipsTextSize = 12;
// customization.msgItemBackgroundLeft = R.drawable.qiyu_message_item_selector;
// customization.msgItemBackgroundRight = R.drawable.qiyu_message_item_selector;
// customization.textMsgColorLeft = mContext.getResources().getColor(R.color.srs_text);
// customization.textMsgColorRight = mContext.getResources().getColor(R.color.srs_text);
// customization.titleBackgroundColor = mContext.getResources().getColor(R.color.main_bg_color1);
// customization.textMsgSize = 18;
// options.uiCustomization = customization;
// options.savePowerConfig = new SavePowerConfig();//省电策略
mOptins = options;
}
return mOptins;
}
/**
* 设置客服消息提醒的跳转入口
*/
private static void setNotifyActivity(Activity activity) {
mOptins.statusBarNotificationConfig.notificationEntrance = activity.getClass();
}
/**
* 启用通知
*
* @param activity 通知跳转入口
*/
public static void enableNotify(Activity activity) {
setNotifyActivity(activity);
NIMClient.getService(MsgService.class).setChattingAccount(MsgService.MSG_CHATTING_ACCOUNT_NONE, SessionTypeEnum.None);
}
/**
* 禁用通知
*/
public static void disableNotify() {
NIMClient.getService(MsgService.class).setChattingAccount(MsgService.MSG_CHATTING_ACCOUNT_ALL, SessionTypeEnum.None);
}
/**
* 设置用户信息
*
* @param login 当前用户是否处于登录状态
*/
public static void setUserInfo(boolean login) {
Unicorn.setUserInfo(getCurrentUserInfo(login));
}
/**
* 设置当前登录用户信息
*
* @param login 当前用户是否处于登录状态
* @return
*/
private static YSFUserInfo getCurrentUserInfo(boolean login) {
YSFUserInfo userInfo = new YSFUserInfo();
userInfo.data = userInfoData(login).toString();
if (login) {
userInfo.userId = DemoHelper.getInstance().getCurrentUserName();
}
return userInfo;
}
private static JSONArray userInfoData(boolean login) {
String userName = DemoHelper.getInstance().getCurrentUserName();
String platform = null;
JSONArray array = new JSONArray();
if (login){
array.add(userInfoDataItem("real_name", userName, false, -1, null, null));
}
array.add(userInfoDataItem("app_version", platform + "-" + Environment2.getPackageVersionName(), false, 0, "应用版本", null));
array.add(userInfoDataItem("system_version", android.os.Build.VERSION.RELEASE, false, 1, "系统版本", null));
array.add(userInfoDataItem("device_manufacturer", Build.MANUFACTURER, false, 2, "机型", null));
array.add(userInfoDataItem("device_model", Build.MODEL, false, 3, "模块", null));
return array;
}
private static JSONObject userInfoDataItem(String key, Object value, boolean hidden, int index, String label, String href) {
JSONObject item = null;
try {
item = new JSONObject();
item.put("key", key);
item.put("value", value);
if (hidden) {
item.put("hidden", true);
}
if (index >= 0) {
item.put("index", index);
}
if (!TextUtils.isEmpty(label)) {
item.put("label", label);
}
if (!TextUtils.isEmpty(href)) {
item.put("href", href);
}
} catch (JSONException e) {
e.printStackTrace();
}
return item;
}
private static class PicassoImageLoader implements UnicornImageLoader {
private final Set<Target> protectedFromGarbageCollectorTargets = new HashSet<>();
@Nullable
@Override
public Bitmap loadImageSync(String uri, int width, int height) {
return null;
}
@Override
public void loadImage(final String uri, final int width, final int height, final ImageLoaderListener listener) {
final Target mTarget = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
if (listener != null) {
listener.onLoadComplete(bitmap);
protectedFromGarbageCollectorTargets.remove(this);
}
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
if (listener != null) {
listener.onLoadFailed(null);
protectedFromGarbageCollectorTargets.remove(this);
}
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
((Activity)mContext).runOnUiThread(new Runnable() {
@Override
public void run() {
RequestCreator requestCreator = Picasso.with(mContext).load(uri).config(Bitmap.Config.RGB_565);
if (width > 0 && height > 0) {
requestCreator = requestCreator.resize(width, height);
}
protectedFromGarbageCollectorTargets.add(mTarget);
requestCreator.into(mTarget);
}
});
}
}
}

View File

@ -0,0 +1,194 @@
package com.example.nanchen.aiyaschoolpush.helper;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
/**
* 字符串相关的Helper方法
*
* */
public final class StringHelper {
public static final String EMPTY = "";
private final static Pattern pattern_isEmail = Pattern.compile("^[a-z0-9]+[\\w\\-\\.]*@[a-z0-9\\-]+(?:\\.[a-z0-9\\-]+)+$", Pattern.CASE_INSENSITIVE);
private final static Pattern pattern_isMobile = Pattern.compile("^1[3-9][0-9]{9}$");
private final static Pattern pattern_isPassword = Pattern.compile(".{6,30}");
public static boolean isNullOrEmpty(String str) {
return str == null || str.length() == 0;
}
public static boolean isNullOrWhiteSpace(String str) {
return str == null || str.length() == 0 || str.trim().length() == 0;
}
public static boolean isEmail(String str) {
return pattern_isEmail.matcher(str).matches();
}
/**
* 判断指定的字符串是否是手机号
* */
public static boolean isMobile(String str) {
return pattern_isMobile.matcher(str).matches();
}
/**
* 获取指定字符串中 {@code lastChar} 前面的部分 如果不包含{@code lastChar}则返回原{@code str}
* */
public static String getLeftStringByLastChar(String str, char lastChar) {
if (str == null)
return null;
int index = str.lastIndexOf(lastChar);
if (index == -1)
return str;
return str.substring(0, index);
}
/**
* 获取指定字符串中 {@code lastChar} 后面的部分 如果不包含{@code lastChar}则返回原{@code str}
* */
public static String getRightStringByLastChar(String str, char lastChar) {
if (str == null)
return null;
int index = str.lastIndexOf(lastChar);
if (index == -1)
return str;
return str.substring(index + 1);
}
public static String bytesToHexString(byte[] bytes) {
StringBuilder sbHex = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
if ((b & 0xFF) < 0x10)
sbHex.append("0");
sbHex.append(Integer.toHexString(b & 0xFF));
}
return sbHex.toString();
}
public static String getMD5String(String str) {
try {
return getMD5String(str, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
public static String getMD5String(String str, String charsetName) throws UnsupportedEncodingException {
try {
return getHashString("MD5", str, charsetName);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("当前环境不支持MD5算法");
}
}
public static String getSHA1String(String str) {
try {
return getSHA1String(str, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
public static String getSHA1String(String str, String charsetName) throws UnsupportedEncodingException {
try {
return getHashString("SHA-1", str, charsetName);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("当前环境不支持SHA-1算法");
}
}
public static String getCRC32String(String str) {
try {
return getCRC32String(str, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
public static String getCRC32String(String str, String charsetName) throws UnsupportedEncodingException {
try {
return getHashString("CRC32", str, charsetName);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("当前环境不支持CRC32算法");
}
}
private static String getHashString(String algorithm, String str, String charsetName) throws UnsupportedEncodingException, NoSuchAlgorithmException {
if ("CRC32".equals(algorithm)) {
CRC32 crc = new CRC32();
crc.update(str.getBytes(charsetName));
long lVal = crc.getValue();
return Long.toHexString(lVal).toLowerCase();
} else {
byte[] bysHash = MessageDigest.getInstance(algorithm).digest(str.getBytes(charsetName));
return bytesToHexString(bysHash);
}
}
public static boolean isValidUserName(String str) {
final int len = str.length();
boolean allNumber = true;
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
if ('0' > c || c > '9') {
allNumber = false;
break;
}
}
return allNumber;
}
public static boolean isValidPwd(String str) {
return pattern_isPassword.matcher(str).matches();
}
/**
* 判断字符串是否为空
*
*
* @param str
* @return
*
* @author WLF
*/
public static boolean isEmpty(String str) {
return str == null || str.length() == 0 || str.equalsIgnoreCase("null");
}
/**
* 判断字符串不为空
*
*
* @param str
* @return
*
* @author WLF
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
/**
*
*
* @param str
* @return
*
* @author WLF
*/
public static String trim(String str) {
return str == null ? EMPTY : str.trim();
}
}

View File

@ -0,0 +1,27 @@
package com.example.nanchen.aiyaschoolpush.helper.event;
import com.example.nanchen.aiyaschoolpush.model.info.InfoModel;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/11/23 13:46
*/
public class CommunityEvent {
private InfoModel mInfoModel;
public CommunityEvent(InfoModel infoModel) {
mInfoModel = infoModel;
}
public CommunityEvent(){}
public InfoModel getInfoModel() {
return mInfoModel;
}
public void setInfoModel(InfoModel infoModel) {
mInfoModel = infoModel;
}
}

View File

@ -0,0 +1,28 @@
package com.example.nanchen.aiyaschoolpush.helper.event;
import com.example.nanchen.aiyaschoolpush.model.info.InfoModel;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/11/22 13:47
*/
public class HomeworkEvent {
private InfoModel mInfoModel;
public HomeworkEvent(InfoModel infoModel){
this.mInfoModel = infoModel;
}
public HomeworkEvent(){}
public InfoModel getInfoModel() {
return mInfoModel;
}
public void setInfoModel(InfoModel infoModel) {
mInfoModel = infoModel;
}
}

View File

@ -0,0 +1,13 @@
package com.example.nanchen.aiyaschoolpush.helper.event;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.helper.event
* @date 2016/12/01 15:19
*/
public class NetStateEvent {
public NetStateEvent(){}
}

View File

@ -0,0 +1,41 @@
package com.example.nanchen.aiyaschoolpush.helper.event;
import com.example.nanchen.aiyaschoolpush.model.info.InfoModel;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush
* @date 2016/11/22 09:58
*/
public class NoticeEvent {
private InfoModel mInfoModel;
private int mCommentCount;
public NoticeEvent(InfoModel infoModel,int commentCount){
this(infoModel);
this.mCommentCount = commentCount;
}
public NoticeEvent(InfoModel infoModel){
this.mInfoModel = infoModel;
}
public NoticeEvent(){}
public int getCommentCount() {
return mCommentCount;
}
public void setCommentCount(int commentCount) {
mCommentCount = commentCount;
}
public InfoModel getInfoModel() {
return mInfoModel;
}
public void setInfoModel(InfoModel infoModel) {
mInfoModel = infoModel;
}
}

View File

@ -0,0 +1,14 @@
package com.example.nanchen.aiyaschoolpush.helper.event;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.event
* @date 2016/11/29 11:58
*/
public class UpdateUserEvent {
public UpdateUserEvent(){
}
}

View File

@ -0,0 +1,51 @@
package com.example.nanchen.aiyaschoolpush.helper.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.receiver
* @date 2016/11/11 11:40
*
* 头像改变的广播接收器防止用户在个人信息更改图片后主页面头像未更新的问题
*/
public class AvatarReceiver extends BroadcastReceiver {
private static final String TAG = "AvatarReceiver";
public static final String AVATAR_ACTION = "com.nanchen.android.AVATAR_ACTION";
private AvatarCallback mAvatarCallback;
public AvatarReceiver(AvatarCallback avatarCallback){
mAvatarCallback = avatarCallback;
}
public AvatarReceiver(){}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.e(TAG,action);
// 如果是正确的action
if (AVATAR_ACTION.equals(action)){
if (mAvatarCallback != null){
mAvatarCallback.onAvatarChanged();
}
}
}
public interface AvatarCallback{
/**
* 头像更改时调用
*/
void onAvatarChanged();
}
}

View File

@ -0,0 +1,113 @@
package com.example.nanchen.aiyaschoolpush.helper.receiver;
import android.content.Context;
import android.text.TextUtils;
import com.xiaomi.mipush.sdk.ErrorCode;
import com.xiaomi.mipush.sdk.MiPushClient;
import com.xiaomi.mipush.sdk.MiPushCommandMessage;
import com.xiaomi.mipush.sdk.MiPushMessage;
import com.xiaomi.mipush.sdk.PushMessageReceiver;
import java.util.List;
/**
* 小米推送消息接收器
*
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.receiver
* @date 2016/10/31 11:59
*/
public class MiMessageReceiver extends PushMessageReceiver {
private String mRegId;
private long mResultCode = -1;
private String mReason;
private String mCommand;
private String mMessage;
private String mTopic;
private String mAlias;
private String mUserAccount;
private String mStartTime;
private String mEndTime;
@Override
public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
mMessage = message.getContent();
if(!TextUtils.isEmpty(message.getTopic())) {
mTopic=message.getTopic();
} else if(!TextUtils.isEmpty(message.getAlias())) {
mAlias=message.getAlias();
} else if(!TextUtils.isEmpty(message.getUserAccount())) {
mUserAccount=message.getUserAccount();
}
}
@Override
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
mMessage = message.getContent();
if(!TextUtils.isEmpty(message.getTopic())) {
mTopic=message.getTopic();
} else if(!TextUtils.isEmpty(message.getAlias())) {
mAlias=message.getAlias();
} else if(!TextUtils.isEmpty(message.getUserAccount())) {
mUserAccount=message.getUserAccount();
}
}
@Override
public void onNotificationMessageArrived(Context context, MiPushMessage message) {
mMessage = message.getContent();
if(!TextUtils.isEmpty(message.getTopic())) {
mTopic=message.getTopic();
} else if(!TextUtils.isEmpty(message.getAlias())) {
mAlias=message.getAlias();
} else if(!TextUtils.isEmpty(message.getUserAccount())) {
mUserAccount=message.getUserAccount();
}
}
@Override
public void onCommandResult(Context context, MiPushCommandMessage message) {
String command = message.getCommand();
List<String> arguments = message.getCommandArguments();
String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);
String cmdArg2 = ((arguments != null && arguments.size() > 1) ? arguments.get(1) : null);
if (MiPushClient.COMMAND_REGISTER.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mRegId = cmdArg1;
}
} else if (MiPushClient.COMMAND_SET_ALIAS.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mAlias = cmdArg1;
}
} else if (MiPushClient.COMMAND_UNSET_ALIAS.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mAlias = cmdArg1;
}
} else if (MiPushClient.COMMAND_SUBSCRIBE_TOPIC.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mTopic = cmdArg1;
}
} else if (MiPushClient.COMMAND_UNSUBSCRIBE_TOPIC.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mTopic = cmdArg1;
}
} else if (MiPushClient.COMMAND_SET_ACCEPT_TIME.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mStartTime = cmdArg1;
mEndTime = cmdArg2;
}
}
}
@Override
public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
String command = message.getCommand();
List<String> arguments = message.getCommandArguments();
String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);
String cmdArg2 = ((arguments != null && arguments.size() > 1) ? arguments.get(1) : null);
if (MiPushClient.COMMAND_REGISTER.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mRegId = cmdArg1;
}
}
}
}

View File

@ -0,0 +1,29 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
import com.hyphenate.easeui.EaseConstant;
public class Constant extends EaseConstant{
public static final String NEW_FRIENDS_USERNAME = "item_new_friends";
public static final String GROUP_USERNAME = "item_groups";
public static final String CHAT_ROOM = "item_chatroom";
public static final String ACCOUNT_REMOVED = "account_removed";
public static final String ACCOUNT_CONFLICT = "conflict";
public static final String ACCOUNT_FORBIDDEN = "user_forbidden";
public static final String CHAT_ROBOT = "item_robots";
public static final String MESSAGE_ATTR_ROBOT_MSGTYPE = "msgtype";
public static final String ACTION_GROUP_CHANAGED = "action_group_changed";
public static final String ACTION_CONTACT_CHANAGED = "action_contact_changed";
}

View File

@ -0,0 +1,16 @@
package com.example.nanchen.aiyaschoolpush.im;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.im
* @date 2016/10/28 08:52
*/
public interface DataSyncListener {
/**
* sync complete
* @param success truedata sync successfulfalse: failed to sync data
*/
void onSyncComplete(boolean success);
}

View File

@ -0,0 +1,74 @@
package com.example.nanchen.aiyaschoolpush.im;
import com.example.nanchen.aiyaschoolpush.App;
import com.example.nanchen.aiyaschoolpush.R;
import com.hyphenate.easeui.domain.EaseEmojicon;
import com.hyphenate.easeui.domain.EaseEmojicon.Type;
import com.hyphenate.easeui.domain.EaseEmojiconGroupEntity;
import java.util.Arrays;
public class EmojiconExampleGroupData {
private static int[] icons = new int[]{
R.drawable.icon_002_cover,
R.drawable.icon_007_cover,
R.drawable.icon_010_cover,
R.drawable.icon_012_cover,
R.drawable.icon_013_cover,
R.drawable.icon_018_cover,
R.drawable.icon_019_cover,
R.drawable.icon_020_cover,
R.drawable.icon_021_cover,
R.drawable.icon_022_cover,
R.drawable.icon_024_cover,
R.drawable.icon_027_cover,
R.drawable.icon_029_cover,
R.drawable.icon_030_cover,
R.drawable.icon_035_cover,
R.drawable.icon_040_cover,
};
private static int[] bigIcons = new int[]{
R.drawable.icon_002,
R.drawable.icon_007,
R.drawable.icon_010,
R.drawable.icon_012,
R.drawable.icon_013,
R.drawable.icon_018,
R.drawable.icon_019,
R.drawable.icon_020,
R.drawable.icon_021,
R.drawable.icon_022,
R.drawable.icon_024,
R.drawable.icon_027,
R.drawable.icon_029,
R.drawable.icon_030,
R.drawable.icon_035,
R.drawable.icon_040,
};
private static final EaseEmojiconGroupEntity DATA = createData();
private static EaseEmojiconGroupEntity createData(){
EaseEmojiconGroupEntity emojiconGroupEntity = new EaseEmojiconGroupEntity();
EaseEmojicon[] datas = new EaseEmojicon[icons.length];
for(int i = 0; i < icons.length; i++){
datas[i] = new EaseEmojicon(icons[i], null, Type.BIG_EXPRESSION);
datas[i].setBigIcon(bigIcons[i]);
//you can replace this to any you want
datas[i].setName(App.getInstance().getApplicationContext().getString(R.string.emojicon_test_name)+ (i+1));
datas[i].setIdentityCode("em"+ (1000+i+1));
}
emojiconGroupEntity.setEmojiconList(Arrays.asList(datas));
emojiconGroupEntity.setIcon(R.drawable.ee_2);
emojiconGroupEntity.setType(Type.BIG_EXPRESSION);
return emojiconGroupEntity;
}
public static EaseEmojiconGroupEntity getData(){
return DATA;
}
}

View File

@ -0,0 +1,563 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.util.LruCache;
import java.io.File;
import java.lang.ref.SoftReference;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* This class memory caching of bitmaps in conjunction with the
* {@link ImageWorker} class and its subclasses. Use
* {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)}
* to get an instance of this class, although usually a cache should be added
* directly to an {@link ImageWorker} by calling
* {@link ImageWorker#addImageCache(FragmentManager, ImageCacheParams)}
* .
*/
public class ImageCache {
private static final String TAG = "ImageCache";
// Default memory cache size in kilobytes
private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 5; // 5MB
private static final int DEFAULT_COMPRESS_QUALITY = 70;
// Constants to easily toggle various caches
private static final boolean DEFAULT_MEM_CACHE_ENABLED = true;
private static final boolean DEFAULT_INIT_DISK_CACHE_ON_CREATE = false;
private LruCache<String, BitmapDrawable> mMemoryCache;
private Set<SoftReference<Bitmap>> mReusableBitmaps;
/**
* Create a new ImageCache object using the specified parameters. This
* should not be called directly by other classes, instead use
* {@link ImageCache#getInstance(FragmentManager, ImageCacheParams)}
* to fetch an ImageCache instance.
*
* @param cacheParams
* The cache parameters to use to initialize the cache
*/
private ImageCache(ImageCacheParams cacheParams) {
init(cacheParams);
}
/**
* Return an {@link ImageCache} instance. A {@link RetainFragment} is used
* to retain the ImageCache object across configuration changes such as a
* change in device orientation.
*
* @param fragmentManager
* The fragment manager to use when dealing with the retained
* fragment.
* @param cacheParams
* The cache parameters to use if the ImageCache needs
* instantiation.
* @return An existing retained ImageCache object or a new one if one did
* not exist
*/
public static ImageCache getInstance(FragmentManager fragmentManager,
ImageCacheParams cacheParams) {
// Search for, or create an instance of the non-UI RetainFragment
final RetainFragment mRetainFragment = findOrCreateRetainFragment(fragmentManager);
// See if we already have an ImageCache stored in RetainFragment
ImageCache imageCache = (ImageCache) mRetainFragment.getObject();
// No existing ImageCache, create one and store it in RetainFragment
if (imageCache == null) {
imageCache = new ImageCache(cacheParams);
mRetainFragment.setObject(imageCache);
}
return imageCache;
}
/**
* Initialize the cache, providing all parameters.
*
* @param cacheParams
* The cache parameters to initialize the cache
*/
private void init(ImageCacheParams cacheParams) {
ImageCacheParams mCacheParams = cacheParams;
// BEGIN_INCLUDE(init_memory_cache)
// Set up memory cache
if (mCacheParams.memoryCacheEnabled) {
// If we're running on Honeycomb or newer, create a set of reusable
// bitmaps that can be
// populated into the inBitmap field of BitmapFactory.Options. Note
// that the set is
// of SoftReferences which will actually not be very effective due
// to the garbage
// collector being aggressive clearing Soft/WeakReferences. A better
// approach
// would be to use a strongly references bitmaps, however this would
// require some
// balancing of memory usage between this set and the bitmap
// LruCache. It would also
// require knowledge of the expected size of the bitmaps. From
// Honeycomb to JellyBean
// the size would need to be precise, from KitKat onward the size
// would just need to
// be the upper bound (due to changes in how inBitmap can re-use
// bitmaps).
if (Utils.hasHoneycomb()) {
mReusableBitmaps = Collections
.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
}
mMemoryCache = new LruCache<String, BitmapDrawable>(
mCacheParams.memCacheSize) {
/**
* Notify the removed entry that is no longer being cached
*/
@Override
protected void entryRemoved(boolean evicted, String key,
BitmapDrawable oldValue, BitmapDrawable newValue) {
if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
// The removed entry is a recycling drawable, so notify
// it
// that it has been removed from the memory cache
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
} else {
// The removed entry is a standard BitmapDrawable
if (Utils.hasHoneycomb()) {
// We're running on Honeycomb or later, so add the
// bitmap
// to a SoftReference set for possible use with
// inBitmap later
mReusableBitmaps.add(new SoftReference<Bitmap>(
oldValue.getBitmap()));
}
}
}
/**
* Measure item size in kilobytes rather than units which is
* more practical for a bitmap cache
*/
@Override
protected int sizeOf(String key, BitmapDrawable value) {
final int bitmapSize = getBitmapSize(value) / 1024;
return bitmapSize == 0 ? 1 : bitmapSize;
}
};
}
}
/**
* Adds a bitmap to both memory and disk cache.
*
* @param data
* Unique identifier for the bitmap to store
* @param value
* The bitmap drawable to store
*/
public void addBitmapToCache(String data, BitmapDrawable value) {
// BEGIN_INCLUDE(add_bitmap_to_cache)
if (data == null || value == null) {
return;
}
// Add to memory cache
if (mMemoryCache != null) {
if (RecyclingBitmapDrawable.class.isInstance(value)) {
// The removed entry is a recycling drawable, so notify it
// that it has been added into the memory cache
((RecyclingBitmapDrawable) value).setIsCached(true);
}
mMemoryCache.put(data, value);
}
}
/**
* Get from memory cache.
*
* @param data
* Unique identifier for which item to get
* @return The bitmap drawable if found in cache, null otherwise
*/
public BitmapDrawable getBitmapFromMemCache(String data) {
// BEGIN_INCLUDE(get_bitmap_from_mem_cache)
BitmapDrawable memValue = null;
if (mMemoryCache != null) {
memValue = mMemoryCache.get(data);
}
// if (BuildConfig.DEBUG && memValue != null) {
// Log.d(TAG, "Memory cache hit");
// }
return memValue;
// END_INCLUDE(get_bitmap_from_mem_cache)
}
/**
* @param options
* - BitmapFactory.Options with out* options populated
* @return Bitmap that case be used for inBitmap
*/
protected Bitmap getBitmapFromReusableSet(BitmapFactory.Options options) {
// BEGIN_INCLUDE(get_bitmap_from_reusable_set)
Bitmap bitmap = null;
if (mReusableBitmaps != null && !mReusableBitmaps.isEmpty()) {
synchronized (mReusableBitmaps) {
final Iterator<SoftReference<Bitmap>> iterator = mReusableBitmaps
.iterator();
Bitmap item;
while (iterator.hasNext()) {
item = iterator.next().get();
if (null != item && item.isMutable()) {
// Check to see it the item can be used for inBitmap
if (canUseForInBitmap(item, options)) {
bitmap = item;
// Remove from reusable set so it can't be used
// again
iterator.remove();
break;
}
} else {
// Remove from the set if the reference has been
// cleared.
iterator.remove();
}
}
}
}
return bitmap;
// END_INCLUDE(get_bitmap_from_reusable_set)
}
/**
* Clears both the memory and disk cache associated with this ImageCache
* object. Note that this includes disk access so this should not be
* executed on the main/UI thread.
*/
public void clearCache() {
if (mMemoryCache != null) {
mMemoryCache.evictAll();
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "Memory cache cleared");
// }
}
}
/**
* A holder class that contains cache parameters.
*/
public static class ImageCacheParams {
public int memCacheSize = DEFAULT_MEM_CACHE_SIZE;
public int compressQuality = DEFAULT_COMPRESS_QUALITY;
public boolean memoryCacheEnabled = DEFAULT_MEM_CACHE_ENABLED;
public boolean initDiskCacheOnCreate = DEFAULT_INIT_DISK_CACHE_ON_CREATE;
/**
* Sets the memory cache size based on a percentage of the max available
* VM memory. Eg. setting percent to 0.2 would set the memory cache to
* one fifth of the available memory. Throws
* {@link IllegalArgumentException} if percent is < 0.01 or > .8.
* memCacheSize is stored in kilobytes instead of bytes as this will
* eventually be passed to construct a LruCache which takes an int in
* its constructor.
*
* This value should be chosen carefully based on a number of factors
* Refer to the corresponding Android Training class for more
* discussion: http://developer.android.com/training/displaying-bitmaps/
*
* @param percent
* Percent of available app memory to use to size memory
* cache
*/
public void setMemCacheSizePercent(float percent) {
if (percent < 0.01f || percent > 0.8f) {
throw new IllegalArgumentException(
"setMemCacheSizePercent - percent must be "
+ "between 0.01 and 0.8 (inclusive)");
}
memCacheSize = Math.round(percent
* Runtime.getRuntime().maxMemory() / 1024);
}
}
/**
* @param candidate
* - Bitmap to check
* @param targetOptions
* - Options that have the out* value populated
* @return true if <code>candidate</code> can be used for inBitmap re-use
* with <code>targetOptions</code>
*/
@TargetApi(19)
private static boolean canUseForInBitmap(Bitmap candidate,
BitmapFactory.Options targetOptions) {
// BEGIN_INCLUDE(can_use_for_inbitmap)
if (!Utils.hasKitKat()) {
// On earlier versions, the dimensions must match exactly and the
// inSampleSize must be 1
return candidate.getWidth() == targetOptions.outWidth
&& candidate.getHeight() == targetOptions.outHeight
&& targetOptions.inSampleSize == 1;
}
// From Android 4.4 (KitKat) onward we can re-use if the byte size of
// the new bitmap
// is smaller than the reusable bitmap candidate allocation byte count.
int width = targetOptions.outWidth / targetOptions.inSampleSize;
int height = targetOptions.outHeight / targetOptions.inSampleSize;
int byteCount = width * height
* getBytesPerPixel(candidate.getConfig());
return byteCount <= candidate.getByteCount();
// END_INCLUDE(can_use_for_inbitmap)
}
/**
* Return the byte usage per pixel of a bitmap based on its configuration.
*
* @param config
* The bitmap configuration.
* @return The byte usage per pixel.
*/
private static int getBytesPerPixel(Config config) {
if (config == Config.ARGB_8888) {
return 4;
} else if (config == Config.RGB_565) {
return 2;
} else if (config == Config.ARGB_4444) {
return 2;
} else if (config == Config.ALPHA_8) {
return 1;
}
return 1;
}
/**
* Get a usable cache directory (external if available, internal otherwise).
*
* @param context
* The context to use
* @param uniqueName
* A unique directory name to append to the cache dir
* @return The cache dir
*/
public static File getDiskCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use
// external cache dir
// otherwise use internal cache dir
final String cachePath = Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState()) || !isExternalStorageRemovable() ? getExternalCacheDir(
context).getPath()
: context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
/**
* A hashing method that changes a string (like a URL) into a hash suitable
* for using as a disk filename.
*/
public static String hashKeyForDisk(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}
private static String bytesToHexString(byte[] bytes) {
// http://stackoverflow.com/questions/332079
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
String hex = Integer.toHexString(0xFF & aByte);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
/**
* Get the size in bytes of a bitmap in a BitmapDrawable. Note that from
* Android 4.4 (KitKat) onward this returns the allocated memory size of the
* bitmap which can be larger than the actual bitmap data byte count (in the
* case it was re-used).
*
* @param value
* @return size in bytes
*/
@TargetApi(19)
public static int getBitmapSize(BitmapDrawable value) {
Bitmap bitmap = value.getBitmap();
// From KitKat onward use getAllocationByteCount() as allocated bytes
// can potentially be
// larger than bitmap byte count.
// if (Utils.hasKitKat()) {
// return bitmap.getAllocationByteCount();
// }
if (Utils.hasHoneycombMR1()) {
return bitmap.getByteCount();
}
// Pre HC-MR1
return bitmap.getRowBytes() * bitmap.getHeight();
}
/**
* Check if external storage is built-in or removable.
*
* @return True if external storage is removable (like an SD card), false
* otherwise.
*/
@TargetApi(VERSION_CODES.GINGERBREAD)
public static boolean isExternalStorageRemovable() {
if (Utils.hasGingerbread()) {
return Environment.isExternalStorageRemovable();
}
return true;
}
/**
* Get the external app cache directory.
*
* @param context
* The context to use
* @return The external cache dir
*/
@TargetApi(VERSION_CODES.FROYO)
public static File getExternalCacheDir(Context context) {
if (Utils.hasFroyo()) {
return context.getExternalCacheDir();
}
// Before Froyo we need to construct the external cache dir ourselves
final String cacheDir = "/Android/data/" + context.getPackageName()
+ "/cache/";
return new File(Environment.getExternalStorageDirectory().getPath()
+ cacheDir);
}
/**
* Locate an existing instance of this Fragment or if not found, create and
* add it using FragmentManager.
*
* @param fm
* The FragmentManager manager to use.
* @return The existing instance of the Fragment or the new instance if just
* created.
*/
private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
// BEGIN_INCLUDE(find_create_retain_fragment)
// Check to see if we have retained the worker fragment.
RetainFragment mRetainFragment = (RetainFragment) fm
.findFragmentByTag(TAG);
// If not retained (or first time running), we need to create and add
// it.
if (mRetainFragment == null) {
mRetainFragment = new RetainFragment();
fm.beginTransaction().add(mRetainFragment, TAG)
.commitAllowingStateLoss();
}
return mRetainFragment;
// END_INCLUDE(find_create_retain_fragment)
}
/**
* A simple non-UI Fragment that stores a single Object and is retained over
* configuration changes. It will be used to retain the ImageCache object.
*/
public static class RetainFragment extends Fragment {
private Object mObject;
/**
* Empty constructor as per the Fragment documentation
*/
public RetainFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Make sure this Fragment is retained over a configuration change
setRetainInstance(true);
}
/**
* Store a single object in this Fragment.
*
* @param object
* The object to store
*/
public void setObject(Object object) {
mObject = object;
}
/**
* Get the stored object.
*
* @return The stored object
*/
public Object getObject() {
return mObject;
}
}
}

View File

@ -0,0 +1,291 @@
package com.example.nanchen.aiyaschoolpush.im;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.os.Build;
import android.provider.MediaStore.Video.Thumbnails;
import java.io.FileDescriptor;
public class ImageResizer extends ImageWorker {
private static final String TAG = "ImageResizer";
protected int mImageWidth;
protected int mImageHeight;
/**
* Initialize providing a single target image size (used for both width and
* height);
*
* @param context
* @param imageWidth
* @param imageHeight
*/
public ImageResizer(Context context, int imageWidth, int imageHeight) {
super(context);
setImageSize(imageWidth, imageHeight);
}
/**
* Initialize providing a single target image size (used for both width and
* height);
*
* @param context
* @param imageSize
*/
public ImageResizer(Context context, int imageSize) {
super(context);
setImageSize(imageSize);
}
/**
* Set the target image width and height.
*
* @param width
* @param height
*/
public void setImageSize(int width, int height) {
mImageWidth = width;
mImageHeight = height;
}
/**
* Set the target image size (width and height will be the same).
*
* @param size
*/
public void setImageSize(int size) {
setImageSize(size, size);
}
/**
* The main processing method. This happens in a background task. In this
* case we are just sampling down the bitmap and returning it from a
* resource.
*
* @param resId
* @return
*/
private Bitmap processBitmap(int resId) {
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "processBitmap - " + resId);
// }
return decodeSampledBitmapFromResource(mResources, resId, mImageWidth,
mImageHeight, getImageCache());
}
@Override
protected Bitmap processBitmap(Object data) {
String filePath=String.valueOf(data);
return ThumbnailUtils.createVideoThumbnail(filePath, Thumbnails.MICRO_KIND);
}
/**
* Decode and sample down a bitmap from resources to the requested width and
* height.
*
* @param res
* The resources object containing the image data
* @param resId
* The resource id of the image data
* @param reqWidth
* The requested width of the resulting bitmap
* @param reqHeight
* The requested height of the resulting bitmap
* @param cache
* The ImageCache used to find candidate bitmaps for use with
* inBitmap
* @return A bitmap sampled down from the original with the same aspect
* ratio and dimensions that are equal to or greater than the
* requested width and height
*/
public static Bitmap decodeSampledBitmapFromResource(Resources res,
int resId, int reqWidth, int reqHeight, ImageCache cache) {
// BEGIN_INCLUDE (read_bitmap_dimensions)
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// END_INCLUDE (read_bitmap_dimensions)
// If we're running on Honeycomb or newer, try to use inBitmap
if (Utils.hasHoneycomb()) {
addInBitmapOptions(options, cache);
}
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
/**
* Decode and sample down a bitmap from a file to the requested width and
* height.
*
* @param filename
* The full path of the file to decode
* @param reqWidth
* The requested width of the resulting bitmap
* @param reqHeight
* The requested height of the resulting bitmap
* @param cache
* The ImageCache used to find candidate bitmaps for use with
* inBitmap
* @return A bitmap sampled down from the original with the same aspect
* ratio and dimensions that are equal to or greater than the
* requested width and height
*/
public static Bitmap decodeSampledBitmapFromFile(String filename,
int reqWidth, int reqHeight, ImageCache cache) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// If we're running on Honeycomb or newer, try to use inBitmap
if (Utils.hasHoneycomb()) {
addInBitmapOptions(options, cache);
}
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filename, options);
}
/**
* Decode and sample down a bitmap from a file input stream to the requested
* width and height.
*
* @param fileDescriptor
* The file descriptor to read from
* @param reqWidth
* The requested width of the resulting bitmap
* @param reqHeight
* The requested height of the resulting bitmap
* @param cache
* The ImageCache used to find candidate bitmaps for use with
* inBitmap
* @return A bitmap sampled down from the original with the same aspect
* ratio and dimensions that are equal to or greater than the
* requested width and height
*/
public static Bitmap decodeSampledBitmapFromDescriptor(
FileDescriptor fileDescriptor, int reqWidth, int reqHeight,
ImageCache cache) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
// If we're running on Honeycomb or newer, try to use inBitmap
if (Utils.hasHoneycomb()) {
addInBitmapOptions(options, cache);
}
return BitmapFactory
.decodeFileDescriptor(fileDescriptor, null, options);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private static void addInBitmapOptions(BitmapFactory.Options options,
ImageCache cache) {
// BEGIN_INCLUDE(add_bitmap_options)
// inBitmap only works with mutable bitmaps so force the decoder to
// return mutable bitmaps.
options.inMutable = true;
if (cache != null) {
// Try and find a bitmap to use for inBitmap
Bitmap inBitmap = cache.getBitmapFromReusableSet(options);
if (inBitmap != null) {
options.inBitmap = inBitmap;
}
}
// END_INCLUDE(add_bitmap_options)
}
/**
* Calculate an inSampleSize for use in a
* {@link BitmapFactory.Options} object when decoding
* bitmaps using the decode* methods from
* {@link BitmapFactory}. This implementation calculates
* the closest inSampleSize that is a power of 2 and will result in the
* final decoded bitmap having a width and height equal to or larger than
* the requested width and height.
*
* @param options
* An options object with out* params already populated (run
* through a decode* method with inJustDecodeBounds==true
* @param reqWidth
* The requested width of the resulting bitmap
* @param reqHeight
* The requested height of the resulting bitmap
* @return The value to be used for inSampleSize
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// BEGIN_INCLUDE (calculate_sample_size)
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
// This offers some additional logic in case the image has a strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might still
// end up being too large to fit comfortably in memory, so we should
// be more aggressive with sample down the image (=larger
// inSampleSize).
long totalPixels = width * height / inSampleSize;
// Anything more than 2x the requested pixels we'll sample down
// further
final long totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels > totalReqPixelsCap) {
inSampleSize *= 2;
totalPixels /= 2;
}
}
return inSampleSize;
// END_INCLUDE (calculate_sample_size)
}
}

View File

@ -0,0 +1,444 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.AsyncTask;
import android.support.v4.app.FragmentManager;
import android.widget.ImageView;
import com.example.nanchen.aiyaschoolpush.App;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* This class wraps up completing some arbitrary long running work when loading a bitmap to an
* ImageView. It handles things like using a memory and disk cache, running the work in a background
* thread and setting a placeholder image.
*/
public abstract class ImageWorker {
private static final String TAG = "ImageWorker";
private static final int FADE_IN_TIME = 200;
private ImageCache mImageCache;
private Bitmap mLoadingBitmap;
private boolean mFadeInBitmap = true;
private boolean mExitTasksEarly = false;
protected boolean mPauseWork = false;
private final Object mPauseWorkLock = new Object();
protected Resources mResources;
private static final int MESSAGE_CLEAR = 0;
private static final int MESSAGE_INIT_DISK_CACHE = 1;
private static final int MESSAGE_FLUSH = 2;
private static final int MESSAGE_CLOSE = 3;
public static final Executor DUAL_THREAD_EXECUTOR = Executors
.newFixedThreadPool(2);
protected ImageWorker(Context context) {
mResources = context.getResources();
}
/**
* Load an image specified by the data parameter into an ImageView (override
* {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and
* disk cache will be used if an {@link ImageCache} has been added using
* {@link ImageWorker#addImageCache(FragmentManager, ImageCache.ImageCacheParams)}. If the
* image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
* will be created to asynchronously load the bitmap.
*
* @param data The URL of the image to download.
* @param imageView The ImageView to bind the downloaded image to.
*/
public void loadImage(Object data, ImageView imageView) {
if (data == null) {
return;
}
BitmapDrawable value = null;
if (mImageCache != null) {
value = mImageCache.getBitmapFromMemCache(String.valueOf(data));
}
if (value != null) {
// Bitmap found in memory cache
imageView.setImageDrawable(value);
} else if (cancelPotentialWork(data, imageView)) {
//BEGIN_INCLUDE(execute_background_task)
final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView);
final AsyncDrawable asyncDrawable =
new AsyncDrawable(mResources, mLoadingBitmap, task);
imageView.setImageDrawable(asyncDrawable);
// NOTE: This uses a custom version of AsyncTask that has been pulled from the
// framework and slightly modified. Refer to the docs at the top of the class
// for more info on what was changed.
task.executeOnExecutor(DUAL_THREAD_EXECUTOR);
//END_INCLUDE(execute_background_task)
}
}
/**
* Set placeholder bitmap that shows when the the background thread is running.
*
* @param bitmap
*/
public void setLoadingImage(Bitmap bitmap) {
mLoadingBitmap = bitmap;
}
/**
* Set placeholder bitmap that shows when the the background thread is running.
*
* @param resId
*/
public void setLoadingImage(int resId) {
mLoadingBitmap = BitmapFactory.decodeResource(mResources, resId);
}
/**
* Adds an {@link ImageCache} to this {@link ImageWorker} to handle disk and memory bitmap
* caching.
* @param fragmentManager
* @param cacheParams The cache parameters to use for the image cache.
*/
public void addImageCache(FragmentManager fragmentManager,
ImageCache.ImageCacheParams cacheParams) {
ImageCache.ImageCacheParams mImageCacheParams = cacheParams;
mImageCache = ImageCache.getInstance(fragmentManager, mImageCacheParams);
new CacheAsyncTask().execute(MESSAGE_INIT_DISK_CACHE);
}
/**
* If set to true, the image will fade-in once it has been loaded by the background thread.
*/
public void setImageFadeIn(boolean fadeIn) {
mFadeInBitmap = fadeIn;
}
public void setExitTasksEarly(boolean exitTasksEarly) {
mExitTasksEarly = exitTasksEarly;
setPauseWork(false);
}
/**
* Subclasses should override this to define any processing or work that must happen to produce
* the final bitmap. This will be executed in a background thread and be long running. For
* example, you could resize a large bitmap here, or pull down an image from the network.
*
* @param data The data to identify which image to process, as provided by
* {@link ImageWorker#loadImage(Object, ImageView)}
* @return The processed bitmap
*/
protected abstract Bitmap processBitmap(Object data);
/**
* @return The {@link ImageCache} object currently being used by this ImageWorker.
*/
protected ImageCache getImageCache() {
return mImageCache;
}
/**
* Cancels any pending work attached to the provided ImageView.
* @param imageView
*/
public static void cancelWork(ImageView imageView) {
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
bitmapWorkerTask.cancel(true);
// if (BuildConfig.DEBUG) {
// final Object bitmapData = bitmapWorkerTask.mData;
// Log.d(TAG, "cancelWork - cancelled work for " + bitmapData);
// }
}
}
/**
* Returns true if the current work has been canceled or if there was no work in
* progress on this image view.
* Returns false if the work in progress deals with the same data. The work is not
* stopped in that case.
*/
public static boolean cancelPotentialWork(Object data, ImageView imageView) {
//BEGIN_INCLUDE(cancel_potential_work)
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (bitmapWorkerTask != null) {
final Object bitmapData = bitmapWorkerTask.mData;
if (bitmapData == null || !bitmapData.equals(data)) {
bitmapWorkerTask.cancel(true);
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "cancelPotentialWork - cancelled work for " + data);
// }
} else {
// The same work is already in progress.
return false;
}
}
return true;
//END_INCLUDE(cancel_potential_work)
}
/**
* @param imageView Any imageView
* @return Retrieve the currently active work task (if any) associated with this imageView.
* null if there is no such task.
*/
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
/**
* The actual AsyncTask that will asynchronously process the image.
*/
private class BitmapWorkerTask extends AsyncTask<Void, Void, BitmapDrawable> {
private Object mData;
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(Object data, ImageView imageView) {
mData = data;
imageViewReference = new WeakReference<ImageView>(imageView);
}
/**
* Background processing.
*/
@Override
protected BitmapDrawable doInBackground(Void... params) {
//BEGIN_INCLUDE(load_bitmap_in_background)
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "doInBackground - starting work");
// }
final String dataString = String.valueOf(mData);
Bitmap bitmap = null;
BitmapDrawable drawable = null;
// Wait here if work is paused and the task is not cancelled
synchronized (mPauseWorkLock) {
while (mPauseWork && !isCancelled()) {
try {
mPauseWorkLock.wait();
} catch (InterruptedException e) {}
}
}
// If the bitmap was not found in the cache and this task has not been cancelled by
// another thread and the ImageView that was originally bound to this task is still
// bound back to this task and our "exit early" flag is not set, then call the main
// process method (as implemented by a subclass)
if (bitmap == null && !isCancelled() && getAttachedImageView() != null
&& !mExitTasksEarly) {
bitmap = processBitmap(mData);
}
// If the bitmap was processed and the image cache is available, then add the processed
// bitmap to the cache for future use. Note we don't check if the task was cancelled
// here, if it was, and the thread is still running, we may as well add the processed
// bitmap to our cache as it might be used again in the future
if (bitmap != null) {
if (Utils.hasHoneycomb()) {
// Running on Honeycomb or newer, so wrap in a standard BitmapDrawable
drawable = new BitmapDrawable(mResources, bitmap);
} else {
// Running on Gingerbread or older, so wrap in a RecyclingBitmapDrawable
// which will recycle automagically
drawable = new RecyclingBitmapDrawable(mResources, bitmap);
}
if (mImageCache != null) {
mImageCache.addBitmapToCache(dataString, drawable);
}
}
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "doInBackground - finished work");
// }
return drawable;
//END_INCLUDE(load_bitmap_in_background)
}
/**
* Once the image is processed, associates it to the imageView
*/
@Override
protected void onPostExecute(BitmapDrawable value) {
//BEGIN_INCLUDE(complete_background_work)
// if cancel was called on this task or the "exit early" flag is set then we're done
if (isCancelled() || mExitTasksEarly) {
value = null;
}
final ImageView imageView = getAttachedImageView();
if (value != null && imageView != null) {
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "onPostExecute - setting bitmap");
// }
setImageDrawable(imageView, value);
}
//END_INCLUDE(complete_background_work)
}
@Override
protected void onCancelled(BitmapDrawable value) {
super.onCancelled(value);
synchronized (mPauseWorkLock) {
mPauseWorkLock.notifyAll();
}
}
/**
* Returns the ImageView associated with this task as long as the ImageView's task still
* points to this task as well. Returns null otherwise.
*/
private ImageView getAttachedImageView() {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask) {
return imageView;
}
return null;
}
}
/**
* A custom Drawable that will be attached to the imageView while the work is in progress.
* Contains a reference to the actual worker task, so that it can be stopped if a new binding is
* required, and makes sure that only the last started worker process can bind its result,
* independently of the finish order.
*/
private static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
/**
* Called when the processing is complete and the final drawable should be
* set on the ImageView.
*
* @param imageView
* @param drawable
*/
private void setImageDrawable(ImageView imageView, Drawable drawable) {
if (mFadeInBitmap) {
// Transition drawable with a transparent drawable and the final drawable
final TransitionDrawable td =
new TransitionDrawable(new Drawable[] {
new ColorDrawable(App.getAppContext().getResources().getColor(android.R.color.transparent)),
drawable
});
// Set background to loading bitmap
imageView.setBackgroundDrawable(
new BitmapDrawable(mResources, mLoadingBitmap));
imageView.setImageDrawable(td);
td.startTransition(FADE_IN_TIME);
} else {
imageView.setImageDrawable(drawable);
}
}
/**
* Pause any ongoing background work. This can be used as a temporary
* measure to improve performance. For example background work could
* be paused when a ListView or GridView is being scrolled using a
* {@link android.widget.AbsListView.OnScrollListener} to keep
* scrolling smooth.
* <p>
* If work is paused, be sure setPauseWork(false) is called again
* before your fragment or activity is destroyed (for example during
* {@link android.app.Activity#onPause()}), or there is a risk the
* background thread will never finish.
*/
public void setPauseWork(boolean pauseWork) {
synchronized (mPauseWorkLock) {
mPauseWork = pauseWork;
if (!mPauseWork) {
mPauseWorkLock.notifyAll();
}
}
}
protected class CacheAsyncTask extends AsyncTask<Object, Void, Void> {
@Override
protected Void doInBackground(Object... params) {
switch ((Integer)params[0]) {
case MESSAGE_CLEAR:
clearCacheInternal();
break;
}
return null;
}
}
protected void clearCacheInternal() {
if (mImageCache != null) {
mImageCache.clearCache();
}
}
public void clearCache() {
new CacheAsyncTask().execute(MESSAGE_CLEAR);
}
public void flushCache() {
new CacheAsyncTask().execute(MESSAGE_FLUSH);
}
public void closeCache() {
new CacheAsyncTask().execute(MESSAGE_CLOSE);
}
}

View File

@ -0,0 +1,34 @@
package com.example.nanchen.aiyaschoolpush.im;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.im
* @date 2016/10/28 09:05
*/
public enum InviteMesageStatus {
//==contact
/**being invited*/
BEINVITEED,
/**being refused*/
BEREFUSED,
/**remote user already agreed*/
BEAGREED,
//==group application
/**remote user apply to join*/
BEAPPLYED,
/**you have agreed to join*/
AGREED,
/**you refused the join request*/
REFUSED,
//==group invitation
/**received remote user's invitation**/
GROUPINVITATION,
/**remote user accept your invitation**/
GROUPINVITATION_ACCEPTED,
/**remote user declined your invitation**/
GROUPINVITATION_DECLINED
}

View File

@ -0,0 +1,102 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
public class InviteMessage {
private String from;
private long time;
private String reason;
private InviteMesageStatus status;
private String groupId;
private String groupName;
private String groupInviter;
private int id;
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public InviteMesageStatus getStatus() {
return status;
}
public void setStatus(InviteMesageStatus status) {
this.status = status;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId) {
this.groupId = groupId;
}
public String getGroupName() {
return groupName;
}
public void setGroupName(String groupName) {
this.groupName = groupName;
}
public void setGroupInviter(String inviter) {
groupInviter = inviter;
}
public String getGroupInviter() {
return groupInviter;
}
}

View File

@ -0,0 +1,229 @@
package com.example.nanchen.aiyaschoolpush.im;
import android.content.Context;
import com.example.nanchen.aiyaschoolpush.helper.DemoHelper;
import com.hyphenate.EMValueCallBack;
import com.hyphenate.chat.EMClient;
import com.hyphenate.easeui.domain.EaseUser;
import com.hyphenate.easeui.utils.EaseCommonUtils;
import com.hyphenate.util.EMLog;
import com.parse.FindCallback;
import com.parse.GetCallback;
import com.parse.Parse;
import com.parse.ParseException;
import com.parse.ParseFile;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.SaveCallback;
import java.util.ArrayList;
import java.util.List;
public class ParseManager {
private static final String TAG = ParseManager.class.getSimpleName();
private static final String ParseAppID = "UUL8TxlHwKj7ZXEUr2brF3ydOxirCXdIj9LscvJs";
private static final String ParseClientKey = "B1jH9bmxuYyTcpoFfpeVslhmLYsytWTxqYqKQhBJ";
// private static final String ParseAppID = "task";
// private static final String ParseClientKey = "123456789";
private static final String CONFIG_TABLE_NAME = "hxuser";
private static final String CONFIG_USERNAME = "username";
private static final String CONFIG_NICK = "nickname";
private static final String CONFIG_AVATAR = "avatar";
private static final String parseServer = "http://parse.easemob.com/parse/";
private static ParseManager instance = new ParseManager();
private ParseManager() {
}
public static ParseManager getInstance() {
return instance;
}
public void onInit(Context context) {
Context appContext = context.getApplicationContext();
Parse.enableLocalDatastore(appContext);
// Parse.initialize(context, ParseAppID, ParseClientKey);
Parse.initialize(new Parse.Configuration.Builder(appContext)
.applicationId(ParseAppID)
.server(parseServer)
.build());
}
public boolean updateParseNickName(final String nickname) {
String username = EMClient.getInstance().getCurrentUser();
ParseQuery<ParseObject> pQuery = ParseQuery.getQuery(CONFIG_TABLE_NAME);
pQuery.whereEqualTo(CONFIG_USERNAME, username);
ParseObject pUser = null;
try {
pUser = pQuery.getFirst();
if (pUser == null) {
return false;
}
pUser.put(CONFIG_NICK, nickname);
pUser.save();
return true;
} catch (ParseException e) {
if(e.getCode()==ParseException.OBJECT_NOT_FOUND){
pUser = new ParseObject(CONFIG_TABLE_NAME);
pUser.put(CONFIG_USERNAME, username);
pUser.put(CONFIG_NICK, nickname);
try {
pUser.save();
return true;
} catch (ParseException e1) {
e1.printStackTrace();
EMLog.e(TAG, "parse error " + e1.getMessage());
}
}
e.printStackTrace();
EMLog.e(TAG, "parse error " + e.getMessage());
} catch(Exception e) {
EMLog.e(TAG, "updateParseNickName error");
e.printStackTrace();
}
return false;
}
public void getContactInfos(List<String> usernames, final EMValueCallBack<List<EaseUser>> callback) {
ParseQuery<ParseObject> pQuery = ParseQuery.getQuery(CONFIG_TABLE_NAME);
pQuery.whereContainedIn(CONFIG_USERNAME, usernames);
pQuery.findInBackground(new FindCallback<ParseObject>() {
@Override
public void done(List<ParseObject> arg0, ParseException arg1) {
if (arg0 != null) {
List<EaseUser> mList = new ArrayList<EaseUser>();
for (ParseObject pObject : arg0) {
EaseUser user = new EaseUser(pObject.getString(CONFIG_USERNAME));
ParseFile parseFile = pObject.getParseFile(CONFIG_AVATAR);
if (parseFile != null) {
user.setAvatar(parseFile.getUrl());
}
user.setNick(pObject.getString(CONFIG_NICK));
EaseCommonUtils.setUserInitialLetter(user);
mList.add(user);
}
callback.onSuccess(mList);
} else {
callback.onError(arg1.getCode(), arg1.getMessage());
}
}
});
}
public void asyncGetCurrentUserInfo(final EMValueCallBack<EaseUser> callback){
final String username = EMClient.getInstance().getCurrentUser();
asyncGetUserInfo(username, new EMValueCallBack<EaseUser>() {
@Override
public void onSuccess(EaseUser value) {
callback.onSuccess(value);
}
@Override
public void onError(int error, String errorMsg) {
if (error == ParseException.OBJECT_NOT_FOUND) {
ParseObject pUser = new ParseObject(CONFIG_TABLE_NAME);
pUser.put(CONFIG_USERNAME, username);
pUser.saveInBackground(new SaveCallback() {
@Override
public void done(ParseException arg0) {
if(arg0==null){
callback.onSuccess(new EaseUser(username));
}
}
});
}else{
callback.onError(error, errorMsg);
}
}
});
}
public void asyncGetUserInfo(final String username,final EMValueCallBack<EaseUser> callback){
ParseQuery<ParseObject> pQuery = ParseQuery.getQuery(CONFIG_TABLE_NAME);
pQuery.whereEqualTo(CONFIG_USERNAME, username);
pQuery.getFirstInBackground(new GetCallback<ParseObject>() {
@Override
public void done(ParseObject pUser, ParseException e) {
if(pUser!=null){
String nick = pUser.getString(CONFIG_NICK);
ParseFile pFile = pUser.getParseFile(CONFIG_AVATAR);
if(callback!=null){
EaseUser user = DemoHelper.getInstance().getContactList().get(username);
if(user!=null){
user.setNick(nick);
if (pFile != null && pFile.getUrl() != null) {
user.setAvatar(pFile.getUrl());
}
}else{
user = new EaseUser(username);
user.setNick(nick);
if (pFile != null && pFile.getUrl() != null) {
user.setAvatar(pFile.getUrl());
}
}
callback.onSuccess(user);
}
}else{
if(callback!=null){
callback.onError(e.getCode(), e.getMessage());
}
}
}
});
}
public String uploadParseAvatar(byte[] data) {
String username = EMClient.getInstance().getCurrentUser();
ParseQuery<ParseObject> pQuery = ParseQuery.getQuery(CONFIG_TABLE_NAME);
pQuery.whereEqualTo(CONFIG_USERNAME, username);
ParseObject pUser = null;
try {
pUser = pQuery.getFirst();
if (pUser == null) {
pUser = new ParseObject(CONFIG_TABLE_NAME);
pUser.put(CONFIG_USERNAME, username);
}
ParseFile pFile = new ParseFile(data);
pUser.put(CONFIG_AVATAR, pFile);
pUser.save();
return pFile.getUrl();
} catch (ParseException e) {
if (e.getCode() == ParseException.OBJECT_NOT_FOUND) {
try {
pUser = new ParseObject(CONFIG_TABLE_NAME);
pUser.put(CONFIG_USERNAME, username);
ParseFile pFile = new ParseFile(data);
pUser.put(CONFIG_AVATAR, pFile);
pUser.save();
return pFile.getUrl();
} catch (ParseException e1) {
e1.printStackTrace();
EMLog.e(TAG, "parse error " + e1.getMessage());
}
} else {
e.printStackTrace();
EMLog.e(TAG, "parse error " + e.getMessage());
}
} catch(Exception e) {
EMLog.e(TAG, "uploadParseAvatar error");
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,25 @@
/**
* Copyright 2015 Anthony Restaino
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing
permissions and limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
/**
* Enum class to handle the different states
* of permissions since the PackageManager only
* has a granted and denied state.
*/
enum Permissions {
GRANTED,
NOT_FOUND,
DENIED
}

View File

@ -0,0 +1,381 @@
/**
* Copyright 2015 Anthony Restaino
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing
permissions and limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* A class to help you manage your permissions simply.
*/
public class PermissionsManager {
private static final String TAG = PermissionsManager.class.getSimpleName();
private final Set<String> mPendingRequests = new HashSet<String>(1);
private final Set<String> mPermissions = new HashSet<String>(1);
private final List<WeakReference<PermissionsResultAction>> mPendingActions = new ArrayList<WeakReference<PermissionsResultAction>>(1);
private static PermissionsManager mInstance = null;
public static PermissionsManager getInstance() {
if (mInstance == null) {
mInstance = new PermissionsManager();
}
return mInstance;
}
private PermissionsManager() {
initializePermissionsMap();
}
/**
* This method uses reflection to read all the permissions in the Manifest class.
* This is necessary because some permissions do not exist on older versions of Android,
* since they do not exist, they will be denied when you check whether you have permission
* which is problematic since a new permission is often added where there was no previous
* permission required. We initialize a Set of available permissions and check the set
* when checking if we have permission since we want to know when we are denied a permission
* because it doesn't exist yet.
*/
private synchronized void initializePermissionsMap() {
Field[] fields = Manifest.permission.class.getFields();
for (Field field : fields) {
String name = null;
try {
name = (String) field.get("");
} catch (IllegalAccessException e) {
Log.e(TAG, "Could not access field", e);
}
mPermissions.add(name);
}
}
/**
* This method retrieves all the permissions declared in the application's manifest.
* It returns a non null array of permisions that can be declared.
*
* @param activity the Activity necessary to check what permissions we have.
* @return a non null array of permissions that are declared in the application manifest.
*/
@NonNull
private synchronized String[] getManifestPermissions(@NonNull final Activity activity) {
PackageInfo packageInfo = null;
List<String> list = new ArrayList<String>(1);
try {
Log.d(TAG, activity.getPackageName());
packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "A problem occurred when retrieving permissions", e);
}
if (packageInfo != null) {
String[] permissions = packageInfo.requestedPermissions;
if (permissions != null) {
for (String perm : permissions) {
Log.d(TAG, "Manifest contained permission: " + perm);
list.add(perm);
}
}
}
return list.toArray(new String[list.size()]);
}
/**
* This method adds the {@link PermissionsResultAction} to the current list
* of pending actions that will be completed when the permissions are
* received. The list of permissions passed to this method are registered
* in the PermissionsResultAction object so that it will be notified of changes
* made to these permissions.
*
* @param permissions the required permissions for the action to be executed.
* @param action the action to add to the current list of pending actions.
*/
private synchronized void addPendingAction(@NonNull String[] permissions,
@Nullable PermissionsResultAction action) {
if (action == null) {
return;
}
action.registerPermissions(permissions);
mPendingActions.add(new WeakReference<PermissionsResultAction>(action));
}
/**
* This method removes a pending action from the list of pending actions.
* It is used for cases where the permission has already been granted, so
* you immediately wish to remove the pending action from the queue and
* execute the action.
*
* @param action the action to remove
*/
private synchronized void removePendingAction(@Nullable PermissionsResultAction action) {
for (Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();
iterator.hasNext(); ) {
WeakReference<PermissionsResultAction> weakRef = iterator.next();
if (weakRef.get() == action || weakRef.get() == null) {
iterator.remove();
}
}
}
/**
* This static method can be used to check whether or not you have a specific permission.
* It is basically a less verbose method of using {@link ActivityCompat#checkSelfPermission(Context, String)}
* and will simply return a boolean whether or not you have the permission. If you pass
* in a null Context object, it will return false as otherwise it cannot check the permission.
* However, the Activity parameter is nullable so that you can pass in a reference that you
* are not always sure will be valid or not (e.g. getActivity() from Fragment).
*
* @param context the Context necessary to check the permission
* @param permission the permission to check
* @return true if you have been granted the permission, false otherwise
*/
@SuppressWarnings("unused")
public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {
return context != null && (ActivityCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission));
}
/**
* This static method can be used to check whether or not you have several specific permissions.
* It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)}
* for each permission and will simply return a boolean whether or not you have all the permissions.
* If you pass in a null Context object, it will return false as otherwise it cannot check the
* permission. However, the Activity parameter is nullable so that you can pass in a reference
* that you are not always sure will be valid or not (e.g. getActivity() from Fragment).
*
* @param context the Context necessary to check the permission
* @param permissions the permissions to check
* @return true if you have been granted all the permissions, false otherwise
*/
@SuppressWarnings("unused")
public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) {
if (context == null) {
return false;
}
boolean hasAllPermissions = true;
for (String perm : permissions) {
hasAllPermissions &= hasPermission(context, perm);
}
return hasAllPermissions;
}
/**
* This method will request all the permissions declared in your application manifest
* for the specified {@link PermissionsResultAction}. The purpose of this method is to enable
* all permissions to be requested at one shot. The PermissionsResultAction is used to notify
* you of the user allowing or denying each permission. The Activity and PermissionsResultAction
* parameters are both annotated Nullable, but this method will not work if the Activity
* is null. It is only annotated Nullable as a courtesy to prevent crashes in the case
* that you call this from a Fragment where {@link Fragment#getActivity()} could yield
* null. Additionally, you will not receive any notification of permissions being granted
* if you provide a null PermissionsResultAction.
*
* @param activity the Activity necessary to request and check permissions.
* @param action the PermissionsResultAction used to notify you of permissions being accepted.
*/
@SuppressWarnings("unused")
public synchronized void requestAllManifestPermissionsIfNecessary(final @Nullable Activity activity,
final @Nullable PermissionsResultAction action) {
if (activity == null) {
return;
}
String[] perms = getManifestPermissions(activity);
requestPermissionsIfNecessaryForResult(activity, perms, action);
}
/**
* This method should be used to execute a {@link PermissionsResultAction} for the array
* of permissions passed to this method. This method will request the permissions if
* they need to be requested (i.e. we don't have permission yet) and will add the
* PermissionsResultAction to the queue to be notified of permissions being granted or
* denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
* The Activity variable is nullable, but if it is null, the method will fail to execute.
* This is only nullable as a courtesy for Fragments where getActivity() may yeild null
* if the Fragment is not currently added to its parent Activity.
*
* @param activity the activity necessary to request the permissions.
* @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
* @param action the PermissionsResultAction to notify when the permissions are granted or denied.
*/
@SuppressWarnings("unused")
public synchronized void requestPermissionsIfNecessaryForResult(@Nullable Activity activity,
@NonNull String[] permissions,
@Nullable PermissionsResultAction action) {
if (activity == null) {
return;
}
addPendingAction(permissions, action);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
doPermissionWorkBeforeAndroidM(activity, permissions, action);
} else {
List<String> permList = getPermissionsListToRequest(activity, permissions, action);
if (permList.isEmpty()) {
//if there is no permission to request, there is no reason to keep the action int the list
removePendingAction(action);
} else {
String[] permsToRequest = permList.toArray(new String[permList.size()]);
mPendingRequests.addAll(permList);
ActivityCompat.requestPermissions(activity, permsToRequest, 1);
}
}
}
/**
* This method should be used to execute a {@link PermissionsResultAction} for the array
* of permissions passed to this method. This method will request the permissions if
* they need to be requested (i.e. we don't have permission yet) and will add the
* PermissionsResultAction to the queue to be notified of permissions being granted or
* denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
* The Fragment variable is used, but if {@link Fragment#getActivity()} returns null, this method
* will fail to work as the activity reference is necessary to check for permissions.
*
* @param fragment the fragment necessary to request the permissions.
* @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
* @param action the PermissionsResultAction to notify when the permissions are granted or denied.
*/
@SuppressWarnings("unused")
public synchronized void requestPermissionsIfNecessaryForResult(@NonNull Fragment fragment,
@NonNull String[] permissions,
@Nullable PermissionsResultAction action) {
Activity activity = fragment.getActivity();
if (activity == null) {
return;
}
addPendingAction(permissions, action);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
doPermissionWorkBeforeAndroidM(activity, permissions, action);
} else {
List<String> permList = getPermissionsListToRequest(activity, permissions, action);
if (permList.isEmpty()) {
//if there is no permission to request, there is no reason to keep the action int the list
removePendingAction(action);
} else {
String[] permsToRequest = permList.toArray(new String[permList.size()]);
mPendingRequests.addAll(permList);
fragment.requestPermissions(permsToRequest, 1);
}
}
}
/**
* This method notifies the PermissionsManager that the permissions have change. If you are making
* the permissions requests using an Activity, then this method should be called from the
* Activity callback onRequestPermissionsResult() with the variables passed to that method. If
* you are passing a Fragment to make the permissions request, then you should call this in
* the {@link Fragment#onRequestPermissionsResult(int, String[], int[])} method.
* It will notify all the pending PermissionsResultAction objects currently
* in the queue, and will remove the permissions request from the list of pending requests.
*
* @param permissions the permissions that have changed.
* @param results the values for each permission.
*/
@SuppressWarnings("unused")
public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {
int size = permissions.length;
if (results.length < size) {
size = results.length;
}
Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();
while (iterator.hasNext()) {
PermissionsResultAction action = iterator.next().get();
for (int n = 0; n < size; n++) {
if (action == null || action.onResult(permissions[n], results[n])) {
iterator.remove();
break;
}
}
}
for (int n = 0; n < size; n++) {
mPendingRequests.remove(permissions[n]);
}
}
/**
* When request permissions on devices before Android M (Android 6.0, API Level 23)
* Do the granted or denied work directly according to the permission status
*
* @param activity the activity to check permissions
* @param permissions the permissions names
* @param action the callback work object, containing what we what to do after
* permission check
*/
private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity,
@NonNull String[] permissions,
@Nullable PermissionsResultAction action) {
for (String perm : permissions) {
if (action != null) {
if (!mPermissions.contains(perm)) {
action.onResult(perm, Permissions.NOT_FOUND);
} else if (ActivityCompat.checkSelfPermission(activity, perm)
!= PackageManager.PERMISSION_GRANTED) {
action.onResult(perm, Permissions.DENIED);
} else {
action.onResult(perm, Permissions.GRANTED);
}
}
}
}
/**
* Filter the permissions list:
* If a permission is not granted, add it to the result list
* if a permission is granted, do the granted work, do not add it to the result list
*
* @param activity the activity to check permissions
* @param permissions all the permissions names
* @param action the callback work object, containing what we what to do after
* permission check
* @return a list of permissions names that are not granted yet
*/
@NonNull
private List<String> getPermissionsListToRequest(@NonNull Activity activity,
@NonNull String[] permissions,
@Nullable PermissionsResultAction action) {
List<String> permList = new ArrayList<String>(permissions.length);
for (String perm : permissions) {
if (!mPermissions.contains(perm)) {
if (action != null) {
action.onResult(perm, Permissions.NOT_FOUND);
}
} else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
if (!mPendingRequests.contains(perm)) {
permList.add(perm);
}
} else {
if (action != null) {
action.onResult(perm, Permissions.GRANTED);
}
}
}
return permList;
}
}

View File

@ -0,0 +1,181 @@
/**
* Copyright 2015 Anthony Restaino
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
either express or implied. See the License for the specific language governing
permissions and limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.util.Log;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* This abstract class should be used to create an if/else action that the PermissionsManager
* can execute when the permissions you request are granted or denied. Simple use involves
* creating an anonymous instance of it and passing that instance to the
* requestPermissionsIfNecessaryForResult method. The result will be sent back to you as
* either onGranted (all permissions have been granted), or onDenied (a required permission
* has been denied). Ideally you put your functionality in the onGranted method and notify
* the user what won't work in the onDenied method.
*/
public abstract class PermissionsResultAction {
private static final String TAG = PermissionsResultAction.class.getSimpleName();
private final Set<String> mPermissions = new HashSet<String>(1);
private Looper mLooper = Looper.getMainLooper();
/**
* Default Constructor
*/
public PermissionsResultAction() {}
/**
* Alternate Constructor. Pass the looper you wish the PermissionsResultAction
* callbacks to be executed on if it is not the current Looper. For instance,
* if you are making a permissions request from a background thread but wish the
* callback to be on the UI thread, use this constructor to specify the UI Looper.
*
* @param looper the looper that the callbacks will be called using.
*/
@SuppressWarnings("unused")
public PermissionsResultAction(@NonNull Looper looper) {mLooper = looper;}
/**
* This method is called when ALL permissions that have been
* requested have been granted by the user. In this method
* you should put all your permissions sensitive code that can
* only be executed with the required permissions.
*/
public abstract void onGranted();
/**
* This method is called when a permission has been denied by
* the user. It provides you with the permission that was denied
* and will be executed on the Looper you pass to the constructor
* of this class, or the Looper that this object was created on.
*
* @param permission the permission that was denied.
*/
public abstract void onDenied(String permission);
/**
* This method is used to determine if a permission not
* being present on the current Android platform should
* affect whether the PermissionsResultAction should continue
* listening for events. By default, it returns true and will
* simply ignore the permission that did not exist. Usually this will
* work fine since most new permissions are introduced to
* restrict what was previously allowed without permission.
* If that is not the case for your particular permission you
* request, override this method and return false to result in the
* Action being denied.
*
* @param permission the permission that doesn't exist on this
* Android version
* @return return true if the PermissionsResultAction should
* ignore the lack of the permission and proceed with exection
* or false if the PermissionsResultAction should treat the
* absence of the permission on the API level as a denial.
*/
@SuppressWarnings({"WeakerAccess", "SameReturnValue"})
public synchronized boolean shouldIgnorePermissionNotFound(String permission) {
Log.d(TAG, "Permission not found: " + permission);
return true;
}
@SuppressWarnings("WeakerAccess")
@CallSuper
protected synchronized final boolean onResult(final @NonNull String permission, int result) {
if (result == PackageManager.PERMISSION_GRANTED) {
return onResult(permission, Permissions.GRANTED);
} else {
return onResult(permission, Permissions.DENIED);
}
}
/**
* This method is called when a particular permission has changed.
* This method will be called for all permissions, so this method determines
* if the permission affects the state or not and whether it can proceed with
* calling onGranted or if onDenied should be called.
*
* @param permission the permission that changed.
* @param result the result for that permission.
* @return this method returns true if its primary action has been completed
* and it should be removed from the data structure holding a reference to it.
*/
@SuppressWarnings("WeakerAccess")
@CallSuper
protected synchronized final boolean onResult(final @NonNull String permission, Permissions result) {
mPermissions.remove(permission);
if (result == Permissions.GRANTED) {
if (mPermissions.isEmpty()) {
new Handler(mLooper).post(new Runnable() {
@Override
public void run() {
onGranted();
}
});
return true;
}
} else if (result == Permissions.DENIED) {
new Handler(mLooper).post(new Runnable() {
@Override
public void run() {
onDenied(permission);
}
});
return true;
} else if (result == Permissions.NOT_FOUND) {
if (shouldIgnorePermissionNotFound(permission)) {
if (mPermissions.isEmpty()) {
new Handler(mLooper).post(new Runnable() {
@Override
public void run() {
onGranted();
}
});
return true;
}
} else {
new Handler(mLooper).post(new Runnable() {
@Override
public void run() {
onDenied(permission);
}
});
return true;
}
}
return false;
}
/**
* This method registers the PermissionsResultAction object for the specified permissions
* so that it will know which permissions to look for changes to. The PermissionsResultAction
* will then know to look out for changes to these permissions.
*
* @param perms the permissions to listen for
*/
@SuppressWarnings("WeakerAccess")
@CallSuper
protected synchronized final void registerPermissions(@NonNull String[] perms) {
Collections.addAll(mPermissions, perms);
}
}

View File

@ -0,0 +1,266 @@
/**
* Copyright (C) 2016 Hyphenate Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.nanchen.aiyaschoolpush.im;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
public class PreferenceManager {
/**
* name of preference
*/
public static final String PREFERENCE_NAME = "saveInfo";
private static SharedPreferences mSharedPreferences;
private static PreferenceManager mPreferencemManager;
private static SharedPreferences.Editor editor;
private String SHARED_KEY_SETTING_NOTIFICATION = "shared_key_setting_notification";
private String SHARED_KEY_SETTING_SOUND = "shared_key_setting_sound";
private String SHARED_KEY_SETTING_VIBRATE = "shared_key_setting_vibrate";
private String SHARED_KEY_SETTING_SPEAKER = "shared_key_setting_speaker";
private static String SHARED_KEY_SETTING_CHATROOM_OWNER_LEAVE = "shared_key_setting_chatroom_owner_leave";
private static String SHARED_KEY_SETTING_DELETE_MESSAGES_WHEN_EXIT_GROUP = "shared_key_setting_delete_messages_when_exit_group";
private static String SHARED_KEY_SETTING_AUTO_ACCEPT_GROUP_INVITATION = "shared_key_setting_auto_accept_group_invitation";
private static String SHARED_KEY_SETTING_ADAPTIVE_VIDEO_ENCODE = "shared_key_setting_adaptive_video_encode";
private static String SHARED_KEY_SETTING_OFFLINE_PUSH_CALL = "shared_key_setting_offline_push_call";
private static String SHARED_KEY_SETTING_GROUPS_SYNCED = "SHARED_KEY_SETTING_GROUPS_SYNCED";
private static String SHARED_KEY_SETTING_CONTACT_SYNCED = "SHARED_KEY_SETTING_CONTACT_SYNCED";
private static String SHARED_KEY_SETTING_BALCKLIST_SYNCED = "SHARED_KEY_SETTING_BALCKLIST_SYNCED";
private static String SHARED_KEY_CURRENTUSER_USERNAME = "SHARED_KEY_CURRENTUSER_USERNAME";
private static String SHARED_KEY_CURRENTUSER_NICK = "SHARED_KEY_CURRENTUSER_NICK";
private static String SHARED_KEY_CURRENTUSER_AVATAR = "SHARED_KEY_CURRENTUSER_AVATAR";
private static String SHARED_KEY_REST_SERVER = "SHARED_KEY_REST_SERVER";
private static String SHARED_KEY_IM_SERVER = "SHARED_KEY_IM_SERVER";
private static String SHARED_KEY_ENABLE_CUSTOM_SERVER = "SHARED_KEY_ENABLE_CUSTOM_SERVER";
private static String SHARED_KEY_ENABLE_CUSTOM_APPKEY = "SHARED_KEY_ENABLE_CUSTOM_APPKEY";
private static String SHARED_KEY_CUSTOM_APPKEY = "SHARED_KEY_CUSTOM_APPKEY";
@SuppressLint("CommitPrefEdits")
private PreferenceManager(Context cxt) {
mSharedPreferences = cxt.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
editor = mSharedPreferences.edit();
}
public static synchronized void init(Context cxt){
if(mPreferencemManager == null){
mPreferencemManager = new PreferenceManager(cxt);
}
}
/**
* get instance of PreferenceManager
*
* @param
* @return
*/
public synchronized static PreferenceManager getInstance() {
if (mPreferencemManager == null) {
throw new RuntimeException("please init first!");
}
return mPreferencemManager;
}
public void setSettingMsgNotification(boolean paramBoolean) {
editor.putBoolean(SHARED_KEY_SETTING_NOTIFICATION, paramBoolean);
editor.apply();
}
public boolean getSettingMsgNotification() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_NOTIFICATION, true);
}
public void setSettingMsgSound(boolean paramBoolean) {
editor.putBoolean(SHARED_KEY_SETTING_SOUND, paramBoolean);
editor.apply();
}
public boolean getSettingMsgSound() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_SOUND, true);
}
public void setSettingMsgVibrate(boolean paramBoolean) {
editor.putBoolean(SHARED_KEY_SETTING_VIBRATE, paramBoolean);
editor.apply();
}
public boolean getSettingMsgVibrate() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_VIBRATE, true);
}
public void setSettingMsgSpeaker(boolean paramBoolean) {
editor.putBoolean(SHARED_KEY_SETTING_SPEAKER, paramBoolean);
editor.apply();
}
public boolean getSettingMsgSpeaker() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_SPEAKER, true);
}
public void setSettingAllowChatroomOwnerLeave(boolean value) {
editor.putBoolean(SHARED_KEY_SETTING_CHATROOM_OWNER_LEAVE, value);
editor.apply();
}
public boolean getSettingAllowChatroomOwnerLeave() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_CHATROOM_OWNER_LEAVE, true);
}
public void setDeleteMessagesAsExitGroup(boolean value){
editor.putBoolean(SHARED_KEY_SETTING_DELETE_MESSAGES_WHEN_EXIT_GROUP, value);
editor.apply();
}
public boolean isDeleteMessagesAsExitGroup() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_DELETE_MESSAGES_WHEN_EXIT_GROUP, true);
}
public void setAutoAcceptGroupInvitation(boolean value) {
editor.putBoolean(SHARED_KEY_SETTING_AUTO_ACCEPT_GROUP_INVITATION, value);
editor.commit();
}
public boolean isAutoAcceptGroupInvitation() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_AUTO_ACCEPT_GROUP_INVITATION, true);
}
public void setAdaptiveVideoEncode(boolean value) {
editor.putBoolean(SHARED_KEY_SETTING_ADAPTIVE_VIDEO_ENCODE, value);
editor.apply();
}
public boolean isAdaptiveVideoEncode() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_ADAPTIVE_VIDEO_ENCODE, false);
}
public void setPushCall(boolean value) {
editor.putBoolean(SHARED_KEY_SETTING_OFFLINE_PUSH_CALL, value);
editor.apply();
}
public boolean isPushCall() {
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_OFFLINE_PUSH_CALL, false);
}
public void setGroupsSynced(boolean synced){
editor.putBoolean(SHARED_KEY_SETTING_GROUPS_SYNCED, synced);
editor.apply();
}
public boolean isGroupsSynced(){
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_GROUPS_SYNCED, false);
}
public void setContactSynced(boolean synced){
editor.putBoolean(SHARED_KEY_SETTING_CONTACT_SYNCED, synced);
editor.apply();
}
public boolean isContactSynced(){
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_CONTACT_SYNCED, false);
}
public void setBlacklistSynced(boolean synced){
editor.putBoolean(SHARED_KEY_SETTING_BALCKLIST_SYNCED, synced);
editor.apply();
}
public boolean isBacklistSynced(){
return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_BALCKLIST_SYNCED, false);
}
public void setCurrentUserNick(String nick) {
editor.putString(SHARED_KEY_CURRENTUSER_NICK, nick);
editor.apply();
}
public void setCurrentUserAvatar(String avatar) {
editor.putString(SHARED_KEY_CURRENTUSER_AVATAR, avatar);
editor.apply();
}
public String getCurrentUserNick() {
return mSharedPreferences.getString(SHARED_KEY_CURRENTUSER_NICK, null);
}
public String getCurrentUserAvatar() {
return mSharedPreferences.getString(SHARED_KEY_CURRENTUSER_AVATAR, null);
}
public void setCurrentUserName(String username){
editor.putString(SHARED_KEY_CURRENTUSER_USERNAME, username);
editor.apply();
}
public String getCurrentUsername(){
return mSharedPreferences.getString(SHARED_KEY_CURRENTUSER_USERNAME, null);
}
public void setRestServer(String restServer){
editor.putString(SHARED_KEY_REST_SERVER, restServer).commit();
editor.commit();
}
public String getRestServer(){
return mSharedPreferences.getString(SHARED_KEY_REST_SERVER, null);
}
public void setIMServer(String imServer){
editor.putString(SHARED_KEY_IM_SERVER, imServer);
editor.commit();
}
public String getIMServer(){
return mSharedPreferences.getString(SHARED_KEY_IM_SERVER, null);
}
public void enableCustomServer(boolean enable){
editor.putBoolean(SHARED_KEY_ENABLE_CUSTOM_SERVER, enable);
editor.apply();
}
public boolean isCustomServerEnable(){
return mSharedPreferences.getBoolean(SHARED_KEY_ENABLE_CUSTOM_SERVER, false);
}
public void enableCustomAppkey(boolean enable) {
editor.putBoolean(SHARED_KEY_ENABLE_CUSTOM_APPKEY, enable);
editor.apply();
}
public boolean isCustomAppkeyEnabled() {
return mSharedPreferences.getBoolean(SHARED_KEY_ENABLE_CUSTOM_APPKEY, false);
}
public String getCustomAppkey() {
return mSharedPreferences.getString(SHARED_KEY_CUSTOM_APPKEY, "");
}
public void setCustomAppkey(String appkey) {
editor.putString(SHARED_KEY_CUSTOM_APPKEY, appkey);
editor.apply();
}
public void removeCurrentUserInfo() {
editor.remove(SHARED_KEY_CURRENTUSER_NICK);
editor.remove(SHARED_KEY_CURRENTUSER_AVATAR);
editor.apply();
}
}

View File

@ -0,0 +1,86 @@
package com.example.nanchen.aiyaschoolpush.im;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
public class RecyclingBitmapDrawable extends BitmapDrawable {
static final String TAG = "CountingBitmapDrawable";
private int mCacheRefCount = 0;
private int mDisplayRefCount = 0;
private boolean mHasBeenDisplayed;
public RecyclingBitmapDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
/**
* Notify the drawable that the displayed state has changed. Internally a
* count is kept so that the drawable knows when it is no longer being
* displayed.
*
* @param isDisplayed
* - Whether the drawable is being displayed or not
*/
public void setIsDisplayed(boolean isDisplayed) {
// BEGIN_INCLUDE(set_is_displayed)
synchronized (this) {
if (isDisplayed) {
mDisplayRefCount++;
mHasBeenDisplayed = true;
} else {
mDisplayRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
// END_INCLUDE(set_is_displayed)
}
/**
* Notify the drawable that the cache state has changed. Internally a count
* is kept so that the drawable knows when it is no longer being cached.
*
* @param isCached
* - Whether the drawable is being cached or not
*/
public void setIsCached(boolean isCached) {
// BEGIN_INCLUDE(set_is_cached)
synchronized (this) {
if (isCached) {
mCacheRefCount++;
} else {
mCacheRefCount--;
}
}
// Check to see if recycle() can be called
checkState();
// END_INCLUDE(set_is_cached)
}
private synchronized void checkState() {
// BEGIN_INCLUDE(check_state)
// If the drawable cache and display ref counts = 0, and this drawable
// has been displayed, then recycle
if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
&& hasValidBitmap()) {
// if (BuildConfig.DEBUG) {
// Log.d(TAG, "No longer being used or cached so recycling. "
// + toString());
// }
getBitmap().recycle();
}
// END_INCLUDE(check_state)
}
private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
}
}

View File

@ -0,0 +1,180 @@
package com.example.nanchen.aiyaschoolpush.im;
import android.content.Context;
import com.example.nanchen.aiyaschoolpush.helper.DemoHelper;
import com.hyphenate.EMValueCallBack;
import com.hyphenate.chat.EMClient;
import com.hyphenate.easeui.domain.EaseUser;
import java.util.ArrayList;
import java.util.List;
public class UserProfileManager {
/**
* application context
*/
protected Context appContext = null;
/**
* init flag: test if the sdk has been inited before, we don't need to init
* again
*/
private boolean sdkInited = false;
/**
* HuanXin sync contact nick and avatar listener
*/
private List<DataSyncListener> syncContactInfosListeners;
private boolean isSyncingContactInfosWithServer = false;
private EaseUser currentUser;
public UserProfileManager() {
}
public synchronized boolean init(Context context) {
if (sdkInited) {
return true;
}
ParseManager.getInstance().onInit(context);
syncContactInfosListeners = new ArrayList<DataSyncListener>();
sdkInited = true;
return true;
}
public void addSyncContactInfoListener(DataSyncListener listener) {
if (listener == null) {
return;
}
if (!syncContactInfosListeners.contains(listener)) {
syncContactInfosListeners.add(listener);
}
}
public void removeSyncContactInfoListener(DataSyncListener listener) {
if (listener == null) {
return;
}
if (syncContactInfosListeners.contains(listener)) {
syncContactInfosListeners.remove(listener);
}
}
public void asyncFetchContactInfosFromServer(List<String> usernames, final EMValueCallBack<List<EaseUser>> callback) {
if (isSyncingContactInfosWithServer) {
return;
}
isSyncingContactInfosWithServer = true;
ParseManager.getInstance().getContactInfos(usernames, new EMValueCallBack<List<EaseUser>>() {
@Override
public void onSuccess(List<EaseUser> value) {
isSyncingContactInfosWithServer = false;
// in case that logout already before server returns,we should
// return immediately
if (!DemoHelper.getInstance().isLoggedIn()) {
return;
}
if (callback != null) {
callback.onSuccess(value);
}
}
@Override
public void onError(int error, String errorMsg) {
isSyncingContactInfosWithServer = false;
if (callback != null) {
callback.onError(error, errorMsg);
}
}
});
}
public void notifyContactInfosSyncListener(boolean success) {
for (DataSyncListener listener : syncContactInfosListeners) {
listener.onSyncComplete(success);
}
}
public boolean isSyncingContactInfoWithServer() {
return isSyncingContactInfosWithServer;
}
public synchronized void reset() {
isSyncingContactInfosWithServer = false;
currentUser = null;
PreferenceManager.getInstance().removeCurrentUserInfo();
}
public synchronized EaseUser getCurrentUserInfo() {
if (currentUser == null) {
String username = EMClient.getInstance().getCurrentUser();
currentUser = new EaseUser(username);
String nick = getCurrentUserNick();
currentUser.setNick((nick != null) ? nick : username);
currentUser.setAvatar(getCurrentUserAvatar());
}
return currentUser;
}
public boolean updateCurrentUserNickName(final String nickname) {
boolean isSuccess = ParseManager.getInstance().updateParseNickName(nickname);
if (isSuccess) {
setCurrentUserNick(nickname);
}
return isSuccess;
}
public String uploadUserAvatar(byte[] data) {
String avatarUrl = ParseManager.getInstance().uploadParseAvatar(data);
if (avatarUrl != null) {
setCurrentUserAvatar(avatarUrl);
}
return avatarUrl;
}
public void asyncGetCurrentUserInfo() {
ParseManager.getInstance().asyncGetCurrentUserInfo(new EMValueCallBack<EaseUser>() {
@Override
public void onSuccess(EaseUser value) {
if(value != null){
setCurrentUserNick(value.getNick());
setCurrentUserAvatar(value.getAvatar());
}
}
@Override
public void onError(int error, String errorMsg) {
}
});
}
public void asyncGetUserInfo(final String username,final EMValueCallBack<EaseUser> callback){
ParseManager.getInstance().asyncGetUserInfo(username, callback);
}
private void setCurrentUserNick(String nickname) {
getCurrentUserInfo().setNick(nickname);
PreferenceManager.getInstance().setCurrentUserNick(nickname);
}
private void setCurrentUserAvatar(String avatar) {
getCurrentUserInfo().setAvatar(avatar);
PreferenceManager.getInstance().setCurrentUserAvatar(avatar);
}
private String getCurrentUserNick() {
return PreferenceManager.getInstance().getCurrentUserNick();
}
private String getCurrentUserAvatar() {
return PreferenceManager.getInstance().getCurrentUserAvatar();
}
}

View File

@ -0,0 +1,95 @@
package com.example.nanchen.aiyaschoolpush.im;
import android.annotation.SuppressLint;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.StrictMode;
import com.example.nanchen.aiyaschoolpush.ui.activity.ImageGridActivity;
import java.util.Comparator;
import java.util.List;
public class Utils {
private Utils() {
}
@SuppressLint("NewApi")
public static void enableStrictMode() {
if(Utils.hasGingerbread())
{
StrictMode.ThreadPolicy.Builder threadPolicyBuilder =
new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog();
StrictMode.VmPolicy.Builder vmPolicyBuilder =
new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog();
if (Utils.hasHoneycomb()) {
threadPolicyBuilder.penaltyFlashScreen();
vmPolicyBuilder
.setClassInstanceLimit(ImageGridActivity.class, 1);
}
StrictMode.setThreadPolicy(threadPolicyBuilder.build());
StrictMode.setVmPolicy(vmPolicyBuilder.build());
}
}
public static boolean hasFroyo() {
return Build.VERSION.SDK_INT >= VERSION_CODES.FROYO;
}
public static boolean hasGingerbread() {
return Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD;
}
public static boolean hasHoneycomb() {
return Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
}
public static boolean hasHoneycombMR1() {
return Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1;
}
public static boolean hasJellyBean() {
return Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
}
public static boolean hasKitKat() {
return Build.VERSION.SDK_INT >= 19;
}
public static List<Size> getResolutionList(Camera camera)
{
Parameters parameters = camera.getParameters();
return parameters.getSupportedPreviewSizes();
}
public static class ResolutionComparator implements Comparator<Size>{
@Override
public int compare(Size lhs, Size rhs) {
if(lhs.height!=rhs.height)
return lhs.height-rhs.height;
else
return lhs.width-rhs.width;
}
}
}

View File

@ -0,0 +1,9 @@
package com.example.nanchen.aiyaschoolpush.im;
public class VideoEntity {
public int ID;
public String title;
public String filePath;
public int size;
public int duration;
}

View File

@ -0,0 +1,36 @@
package com.example.nanchen.aiyaschoolpush.model;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/10/10 14:43
*/
public class ActivityModel {
private String title;
private int id;
public ActivityModel(int id,String title) {
this.id = id;
this.title = title;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}

View File

@ -0,0 +1,51 @@
package com.example.nanchen.aiyaschoolpush.model;
import java.io.Serializable;
import java.util.List;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/10/26 10:48
*
* 社区话题附件
*/
public class Attach implements IJsonModel,Serializable {
public static final int ATTACH_TYPE_IMAGE = 0;
public static final int ATTACH_TYPE_VIDEO = 1;
public List<Image> mImages;
public List<Video> mVideos;
private static class Image implements Serializable{
public String url;
/**
* 图片宽度
*/
public int width;
/**
* 图片高度
*/
public int height;
}
public static class Video implements Serializable {
public String url;
public String name;
/**
* 媒体时长
*/
public int duration;
/**
* 视频宽度
*/
public int width;
/**
* 视频高度
*/
public int height;
}
}

View File

@ -0,0 +1,37 @@
package com.example.nanchen.aiyaschoolpush.model;
import java.io.Serializable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/10/24 16:16
*/
public class CommentModel implements IJsonModel,Serializable {
/**
* 评论ID
*/
public long id;
/**
* 评论发起者
*/
public User sender;
/**
* 要回复的用户信息非回复类型为null
*/
public User replyto;
/**
* 评论内容
*/
public String content;
/**
* 评论时间戳
*/
public long createTs;
}

View File

@ -0,0 +1,58 @@
package com.example.nanchen.aiyaschoolpush.model;
import java.util.ArrayList;
import java.util.List;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/12/08 09:08
*/
public class CourseDao {
public static List<CourseModel>[] getCourseData() {
List<CourseModel> courseModels[] = new ArrayList[7];
for (int i = 0; i < courseModels.length; i++) {
courseModels[i] = new ArrayList<>();
}
List<CourseModel> models_1 = new ArrayList<>();
models_1.add(new CourseModel(0, "C语言", 1, 2, 1, "A401", (int) (Math.random() * 10)));
models_1.add(new CourseModel(1, "Ruby", 3, 3, 1, "A453", (int) (Math.random() * 10)));
models_1.add(new CourseModel(1, "PHP", 6, 3, 1, "A483", (int) (Math.random() * 10)));
courseModels[0].addAll(models_1);
List<CourseModel> models_2 = new ArrayList<>();
models_2.add(new CourseModel(2, "Swift", 2, 2, 2, "A222", (int) (Math.random() * 10)));
models_2.add(new CourseModel(3, "JavaScript", 6, 2, 2, "A777", (int) (Math.random() * 10)));
courseModels[1].addAll(models_2);
List<CourseModel> models_3 = new ArrayList<>();
models_3.add(new CourseModel(2, "Python", 1, 2, 3, "A342", (int) (Math.random() * 10)));
models_3.add(new CourseModel(3, "Visual Basic .NET", 5, 2, 3, "A737", (int) (Math.random() * 10)));
courseModels[2].addAll(models_3);
List<CourseModel> models_4 = new ArrayList<>();
models_4.add(new CourseModel(4, "C#", 1, 3, 4, "A666", (int) (Math.random() * 10)));
models_4.add(new CourseModel(5, "R语言", 5, 2, 4, "A888", (int) (Math.random() * 10)));
models_4.add(new CourseModel(5, "Java", 9, 2, 4, "A828", (int) (Math.random() * 10)));
courseModels[3].addAll(models_4);
List<CourseModel> models_5 = new ArrayList<>();
models_5.add(new CourseModel(6, "Android", 1, 2, 5, "A466", (int) (Math.random() * 10)));
models_5.add(new CourseModel(7, "Groovy", 3, 2, 5, "A434", (int) (Math.random() * 10)));
models_5.add(new CourseModel(8, "Objective-C", 6, 2, 5, "A411", (int) (Math.random() * 10)));
courseModels[4].addAll(models_5);
List<CourseModel> models_6 = new ArrayList<>();
models_6.add(new CourseModel(9, "C++", 1, 2, 6, "A422", (int) (Math.random() * 10)));
models_6.add(new CourseModel(10, "SQL", 5, 3, 6, "A402", (int) (Math.random() * 10)));
courseModels[5].addAll(models_6);
return courseModels;
}
}

View File

@ -0,0 +1,87 @@
package com.example.nanchen.aiyaschoolpush.model;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/12/08 09:08
*/
public class CourseModel {
private int id; //课程ID
private String courseName;
private int section; //从第几节课开始
private int sectionSpan; //跨几节课
private int week; //周几
private String classRoom; //教室
private int courseFlag; //课程背景颜色
public CourseModel() {
}
public CourseModel(int id, String courseName, int section, int sectionSpan,int week,String classRoom,int courseFlag) {
this.id = id;
this.courseName = courseName;
this.section = section;
this.sectionSpan = sectionSpan;
this.week = week;
this.classRoom = classRoom;
this.courseFlag = courseFlag;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public int getSection() {
return section;
}
public void setSection(int section) {
this.section = section;
}
public int getSectionSpan() {
return sectionSpan;
}
public void setSectionSpan(int sectionSpan) {
this.sectionSpan = sectionSpan;
}
public int getCourseFlag() {
return courseFlag;
}
public void setCourseFlag(int courseFlag) {
this.courseFlag = courseFlag;
}
public int getWeek() {
return week;
}
public void setWeek(int week) {
this.week = week;
}
public String getClassRoom() {
return classRoom;
}
public void setClassRoom(String classRoom) {
this.classRoom = classRoom;
}
}

View File

@ -0,0 +1,290 @@
package com.example.nanchen.aiyaschoolpush.model;
import android.content.Context;
import com.example.nanchen.aiyaschoolpush.db.UserDao;
import com.example.nanchen.aiyaschoolpush.im.PreferenceManager;
import com.hyphenate.easeui.domain.EaseUser;
import com.hyphenate.easeui.model.EaseAtMessageHelper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DemoModel {
UserDao dao = null;
protected Context context = null;
protected Map<Key,Object> valueCache = new HashMap<Key,Object>();
public DemoModel(Context ctx){
context = ctx;
PreferenceManager.init(context);
}
public boolean saveContactList(List<EaseUser> contactList) {
UserDao dao = new UserDao(context);
dao.saveContactList(contactList);
return true;
}
public Map<String, EaseUser> getContactList() {
UserDao dao = new UserDao(context);
return dao.getContactList();
}
public void saveContact(EaseUser user){
UserDao dao = new UserDao(context);
dao.saveContact(user);
}
/**
* save current username
* @param username
*/
public void setCurrentUserName(String username){
PreferenceManager.getInstance().setCurrentUserName(username);
}
public String getCurrentUsernName(){
return PreferenceManager.getInstance().getCurrentUsername();
}
public void setSettingMsgNotification(boolean paramBoolean) {
PreferenceManager.getInstance().setSettingMsgNotification(paramBoolean);
valueCache.put(Key.VibrateAndPlayToneOn, paramBoolean);
}
public boolean getSettingMsgNotification() {
Object val = valueCache.get(Key.VibrateAndPlayToneOn);
if(val == null){
val = PreferenceManager.getInstance().getSettingMsgNotification();
valueCache.put(Key.VibrateAndPlayToneOn, val);
}
return (Boolean) (val != null?val:true);
}
public void setSettingMsgSound(boolean paramBoolean) {
PreferenceManager.getInstance().setSettingMsgSound(paramBoolean);
valueCache.put(Key.PlayToneOn, paramBoolean);
}
public boolean getSettingMsgSound() {
Object val = valueCache.get(Key.PlayToneOn);
if(val == null){
val = PreferenceManager.getInstance().getSettingMsgSound();
valueCache.put(Key.PlayToneOn, val);
}
return (Boolean) (val != null?val:true);
}
public void setSettingMsgVibrate(boolean paramBoolean) {
PreferenceManager.getInstance().setSettingMsgVibrate(paramBoolean);
valueCache.put(Key.VibrateOn, paramBoolean);
}
public boolean getSettingMsgVibrate() {
Object val = valueCache.get(Key.VibrateOn);
if(val == null){
val = PreferenceManager.getInstance().getSettingMsgVibrate();
valueCache.put(Key.VibrateOn, val);
}
return (Boolean) (val != null?val:true);
}
public void setSettingMsgSpeaker(boolean paramBoolean) {
PreferenceManager.getInstance().setSettingMsgSpeaker(paramBoolean);
valueCache.put(Key.SpakerOn, paramBoolean);
}
public boolean getSettingMsgSpeaker() {
Object val = valueCache.get(Key.SpakerOn);
if(val == null){
val = PreferenceManager.getInstance().getSettingMsgSpeaker();
valueCache.put(Key.SpakerOn, val);
}
return (Boolean) (val != null?val:true);
}
public void setDisabledGroups(List<String> groups){
if(dao == null){
dao = new UserDao(context);
}
List<String> list = new ArrayList<String>();
list.addAll(groups);
for(int i = 0; i < list.size(); i++){
if(EaseAtMessageHelper.get().getAtMeGroups().contains(list.get(i))){
list.remove(i);
i--;
}
}
dao.setDisabledGroups(list);
valueCache.put(Key.DisabledGroups, list);
}
public List<String> getDisabledGroups(){
Object val = valueCache.get(Key.DisabledGroups);
if(dao == null){
dao = new UserDao(context);
}
if(val == null){
val = dao.getDisabledGroups();
valueCache.put(Key.DisabledGroups, val);
}
//noinspection unchecked
return (List<String>) val;
}
public void setDisabledIds(List<String> ids){
if(dao == null){
dao = new UserDao(context);
}
dao.setDisabledIds(ids);
valueCache.put(Key.DisabledIds, ids);
}
public List<String> getDisabledIds(){
Object val = valueCache.get(Key.DisabledIds);
if(dao == null){
dao = new UserDao(context);
}
if(val == null){
val = dao.getDisabledIds();
valueCache.put(Key.DisabledIds, val);
}
//noinspection unchecked
return (List<String>) val;
}
public void setGroupsSynced(boolean synced){
PreferenceManager.getInstance().setGroupsSynced(synced);
}
public boolean isGroupsSynced(){
return PreferenceManager.getInstance().isGroupsSynced();
}
public void setContactSynced(boolean synced){
PreferenceManager.getInstance().setContactSynced(synced);
}
public boolean isContactSynced(){
return PreferenceManager.getInstance().isContactSynced();
}
public void setBlacklistSynced(boolean synced){
PreferenceManager.getInstance().setBlacklistSynced(synced);
}
public boolean isBacklistSynced(){
return PreferenceManager.getInstance().isBacklistSynced();
}
public void allowChatroomOwnerLeave(boolean value){
PreferenceManager.getInstance().setSettingAllowChatroomOwnerLeave(value);
}
public boolean isChatroomOwnerLeaveAllowed(){
return PreferenceManager.getInstance().getSettingAllowChatroomOwnerLeave();
}
public void setDeleteMessagesAsExitGroup(boolean value) {
PreferenceManager.getInstance().setDeleteMessagesAsExitGroup(value);
}
public boolean isDeleteMessagesAsExitGroup() {
return PreferenceManager.getInstance().isDeleteMessagesAsExitGroup();
}
public void setAutoAcceptGroupInvitation(boolean value) {
PreferenceManager.getInstance().setAutoAcceptGroupInvitation(value);
}
public boolean isAutoAcceptGroupInvitation() {
return PreferenceManager.getInstance().isAutoAcceptGroupInvitation();
}
public void setAdaptiveVideoEncode(boolean value) {
PreferenceManager.getInstance().setAdaptiveVideoEncode(value);
}
public boolean isAdaptiveVideoEncode() {
return PreferenceManager.getInstance().isAdaptiveVideoEncode();
}
public void setPushCall(boolean value) {
PreferenceManager.getInstance().setPushCall(value);
}
public boolean isPushCall() {
return PreferenceManager.getInstance().isPushCall();
}
public void setRestServer(String restServer){
PreferenceManager.getInstance().setRestServer(restServer);
}
public String getRestServer(){
return PreferenceManager.getInstance().getRestServer();
}
public void setIMServer(String imServer){
PreferenceManager.getInstance().setIMServer(imServer);
}
public String getIMServer(){
return PreferenceManager.getInstance().getIMServer();
}
public void enableCustomServer(boolean enable){
PreferenceManager.getInstance().enableCustomServer(enable);
}
public boolean isCustomServerEnable(){
return PreferenceManager.getInstance().isCustomServerEnable();
}
public void enableCustomAppkey(boolean enable) {
PreferenceManager.getInstance().enableCustomAppkey(enable);
}
public boolean isCustomAppkeyEnabled() {
return PreferenceManager.getInstance().isCustomAppkeyEnabled();
}
public void setCustomAppkey(String appkey) {
PreferenceManager.getInstance().setCustomAppkey(appkey);
}
public String getCutomAppkey() {
return PreferenceManager.getInstance().getCustomAppkey();
}
enum Key{
VibrateAndPlayToneOn,
VibrateOn,
PlayToneOn,
SpakerOn,
DisabledGroups,
DisabledIds
}
}

View File

@ -0,0 +1,19 @@
package com.example.nanchen.aiyaschoolpush.model;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/10/08 11:54
*/
public class HomeworkModel {
public long id; // 通知id
public String content; // 内容
public User user; // 发送人
public long time; // 发送时间
public boolean isIPraised; // 我是否赞了该通知
public int praiseCount; // 总赞数
public int commentCount; //评论数
public int tag; // 发送类型 1公告 2作业
}

View File

@ -0,0 +1,13 @@
package com.example.nanchen.aiyaschoolpush.model;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.json
* @date 2016/10/24 14:49
*
* 这是一个空接口代表实现此接口的类才是Json Model
*/
public interface IJsonModel {
}

View File

@ -0,0 +1,27 @@
package com.example.nanchen.aiyaschoolpush.model;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.api
* @date 2016/11/09 11:22
*/
public class LoginUser extends User{
private static LoginUser loginUser;
private LoginUser(){
}
public static LoginUser getInstance(){
if (loginUser == null){
loginUser = new LoginUser();
}
return loginUser;
}
}

View File

@ -0,0 +1,36 @@
package com.example.nanchen.aiyaschoolpush.model;
import java.io.Serializable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/10/08 11:54
*/
public class NoticeModel implements IJsonModel,Serializable{
public long id; // 通知id
public String content; // 内容
public User user; // 发送人
public long time; // 发送时间
public boolean isIPraised; // 我是否赞了该通知
public int praiseCount; // 总赞数
public int commentCount; //评论数
public int tag; // 发送类型 1公告 2作业 3动态
@Override
public String toString() {
return "NoticeModel{" +
"id=" + id +
", content='" + content + '\'' +
", user=" + user +
", time=" + time +
", isIPraised=" + isIPraised +
", praiseCount=" + praiseCount +
", commentCount=" + commentCount +
", tag=" + tag +
'}';
}
}

View File

@ -0,0 +1,15 @@
package com.example.nanchen.aiyaschoolpush.model;
import java.io.Serializable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/11/18 15:19
*/
public class PraiseModel implements IJsonModel,Serializable {
public boolean isInsert;
public int praiseCount;
}

View File

@ -0,0 +1,57 @@
package com.example.nanchen.aiyaschoolpush.model;
import java.io.Serializable;
import java.util.List;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/10/24 14:48
*/
public class Topic implements IJsonModel,Serializable {
/**
* 话题id
*/
public long id;
/**
* 话题内容
*/
public String content;
/**
* 话题作者
*/
public User author;
/**
* 话题发起时间戳
*/
public long createTs;
/**
* 图片Url
*/
public List<String> imgUrls;
/**
* 我是否赞了该话题
*/
public boolean isIPraised;
/**
* 总赞数
*/
public int praiseCount;
/**
* 总评论数
*/
public int commentCount;
}

View File

@ -0,0 +1,74 @@
package com.example.nanchen.aiyaschoolpush.model;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model
* @date 2016/10/08 11:55
*/
public class User implements IJsonModel,Serializable{
/**
* 用户名
*/
public String username;
/**
* 用户密码
*/
public String password;
/**
* 用户头像地址
*/
@SerializedName("avatar")
public String icon;
/**
* 生日
*/
public String birthday;
/**
* 昵称
*/
public String nickname;
/**
* 用户类型 1 教师 2 家长 3 管理员 4 游客
*/
public int type;
/**
* 班级id
*/
public int classid;
/**
* 家庭地址
*/
public String address;
/**
* 孩子名字
*/
@SerializedName("child_name")
public String childName;
/**
* 孩子头像地址
*/
@SerializedName("child_avatar")
public String childAvatar;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", icon='" + icon + '\'' +
", birthday='" + birthday + '\'' +
", nickname='" + nickname + '\'' +
", type=" + type +
", classid=" + classid +
", address='" + address + '\'' +
", childName='" + childName + '\'' +
", childAvatar='" + childAvatar + '\'' +
'}';
}
}

View File

@ -0,0 +1,43 @@
package com.example.nanchen.aiyaschoolpush.model.info;
import com.example.nanchen.aiyaschoolpush.model.IJsonModel;
import java.io.Serializable;
/**
* 评论信息
*
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model.info
* @date 2016/11/16 16:01
*/
public class CommentInfoModel implements IJsonModel,Serializable {
/**
* 信息id
*/
public int infoid;
public int mainid;//条目主贴id
public String username;//用户名
public long time;//评论时间
public String reply;// 回复的人用户名
public String content;// 评论内容
public UserModel commentUser;//评论人信息
public UserModel replyUser;// 回复的人的信息
@Override
public String toString() {
return "CommentInfoModel{" +
"infoid=" + infoid +
", mainid=" + mainid +
", username='" + username + '\'' +
", time=" + time +
", reply='" + reply + '\'' +
", content='" + content + '\'' +
", commentUser=" + commentUser +
", replyUser=" + replyUser +
'}';
}
}

View File

@ -0,0 +1,13 @@
package com.example.nanchen.aiyaschoolpush.model.info;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model.info
* @date 2016/11/16 14:29
*/
public class InfoKind {
public static final int COMMENT = 1;
public static final int PRAISE = 2;
}

View File

@ -0,0 +1,75 @@
package com.example.nanchen.aiyaschoolpush.model.info;
import com.example.nanchen.aiyaschoolpush.model.IJsonModel;
import java.io.Serializable;
import java.util.List;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model.info
* @date 2016/11/16 14:03
*/
public class InfoModel implements Serializable,IJsonModel{
/**
* 主贴id
*/
public int mainid;
/**
* 班级id,可见性
*/
public int classid;
/**
* 用户名用于获取用户资料信息
*/
public String username;
/**
* 发起时间戳
*/
public long time;
/**
* 信息类型1为公告2为作业3为动态
*/
public int infotype;
/**
* 信息内容
*/
public String content;
/**
* 发布人信息
*/
public UserModel user;
/**
* 赞的数目
*/
public int praiseCount;
/**
* 评论的数目
*/
public int commentCount;
/**
* 我是否赞了该条信息
*/
public boolean isIPraised;
/**
* 评论信息
*/
public List<CommentInfoModel> commentInfo;
/**
* 最后获取的一条信息的id
*/
public int lastid;
/**
* 社区图片Url组
*/
public List<PicModel> picUrls;
/**
* 社区视频Url
*/
public List<VideoModel> videoUrl;
}

View File

@ -0,0 +1,14 @@
package com.example.nanchen.aiyaschoolpush.model.info;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.fragment
* @date 2016/11/16 14:28
*/
public class InfoType {
public static final int NOTICE = 1;
public static final int HOMEWORK = 2;
public static final int COMMUNITY = 3;
}

View File

@ -0,0 +1,24 @@
package com.example.nanchen.aiyaschoolpush.model.info;
import com.example.nanchen.aiyaschoolpush.model.IJsonModel;
import java.io.Serializable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model.info
* @date 2016/11/23 10:51
*/
public class PicModel implements Serializable,IJsonModel{
/**
* 图片id
*/
public int picid;
/**
* 图片地址
*/
public String imageUrl;
}

View File

@ -0,0 +1,18 @@
package com.example.nanchen.aiyaschoolpush.model.info;
import com.example.nanchen.aiyaschoolpush.model.IJsonModel;
import java.io.Serializable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model.info
* @date 2016/11/16 14:49
*/
public class UserModel implements IJsonModel,Serializable {
public String username;
public String avatar;
public String nickname;
}

View File

@ -0,0 +1,27 @@
package com.example.nanchen.aiyaschoolpush.model.info;
import com.example.nanchen.aiyaschoolpush.model.IJsonModel;
import java.io.Serializable;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.model.info
* @date 2016/12/15 15:54
*/
public class VideoModel implements Serializable,IJsonModel {
public int picid; //缩略图id
public int videoid; //视频id
public String videoUrl; //视频地址
@Override
public String toString() {
return "VideoModel{" +
"picid=" + picid +
", videoid=" + videoid +
", videoUrl='" + videoUrl + '\'' +
'}';
}
}

View File

@ -0,0 +1,149 @@
package com.example.nanchen.aiyaschoolpush.net;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import com.example.nanchen.aiyaschoolpush.helper.event.NetStateEvent;
import com.gitonway.lee.niftymodaldialogeffects.lib.NiftyDialogBuilder;
import org.greenrobot.eventbus.EventBus;
/**
* @author nanchen
* @fileName AiYaSchoolPush
* @packageName com.example.nanchen.aiyaschoolpush.net
* @date 2016/12/01 13:55
*/
public class NetworkStateService extends Service {
// 系统网络连接相关的操作管理类.
private ConnectivityManager mManager;
// 网络状态信息的实例
private NetworkInfo mInfo;
/**
* 当前处于的网络
* 0 null
* 1 : 2G/3G/4G
* 2 wifi
*/
public static int networkStatus;
/**
* An action name
*/
public static final String NETWORKSTATE = "com.text.android.network.state";
/**
* 广播实例
*/
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// The action of this intent or null if none is specified.
// action是行动的意思也许是我水平问题无法理解为什么叫行动我一直理解为标识现在理解为意图
String action = intent.getAction(); //当前接受到的广播的标识(行动/意图)
// 当当前接受到的广播的标识(意图)为网络状态的标识时做相应判断
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
// 获取网络连接管理器
mManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
// 获取当前网络状态信息
mInfo = mManager.getActiveNetworkInfo();
if (mInfo != null && mInfo.isAvailable()) {
//当NetworkInfo不为空且是可用的情况下获取当前网络的Type状态
//根据NetworkInfo.getTypeName()判断当前网络
String name = mInfo.getTypeName();
//更改NetworkStateService的静态变量之后只要在Activity中进行判断就好了
if (name.equals("WIFI")) {
networkStatus = 2;
} else {
networkStatus = 1;
}
} else {
// NetworkInfo为空或者是不可用的情况下
networkStatus = 0;
// UIUtil.showToast("当前网络不可用,请检查网络连接!");
EventBus.getDefault().post(new NetStateEvent());
Intent it = new Intent();
it.putExtra("networkStatus", networkStatus);
it.setAction(NETWORKSTATE);
// sendBroadcast(it); //发送无网络广播给注册了当前服务广播的Activity
/**
* 这里推荐使用本地广播的方式发送:
* LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
*/
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
}
}
};
private NiftyDialogBuilder mDialogBuilder;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
//注册网络状态的广播绑定到mReceiver
IntentFilter mFilter = new IntentFilter();
mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(mReceiver, mFilter);
}
@Override
public void onDestroy() {
super.onDestroy();
//注销接收
unregisterReceiver(mReceiver);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
/**
* 判断网络是否可用
*/
public static boolean isNetworkAvailable(Context context) {
// 获取网络连接管理器
ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// 获取当前网络状态信息
NetworkInfo[] info = mgr.getAllNetworkInfo();
if (info != null) {
for (int i = 0; i < info.length; i++) {
if (info[i].getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
return false;
}
}

Some files were not shown because too many files have changed in this diff Show More