├── .gradle ├── 4.6 │ ├── fileChanges │ │ └── last-build.bin │ ├── fileHashes │ │ ├── fileHashes.bin │ │ ├── fileHashes.lock │ │ └── resourceHashesCache.bin │ └── taskHistory │ │ ├── taskHistory.bin │ │ └── taskHistory.lock ├── buildOutputCleanup │ ├── buildOutputCleanup.lock │ ├── cache.properties │ └── outputFiles.bin └── vcsWorkingDirs │ └── gc.properties ├── InstantUpload.iml ├── LICENSE ├── Main.md ├── README.md ├── build.gradle ├── docs ├── AddVoiceAndText.md ├── For_Personal_Camera.md ├── Geo.md ├── OtherTransport.md ├── Simple_and_High_Adaptation.md ├── api.md ├── iOSSupport.md ├── tracker.md └── yed │ ├── whats_instand_upload.graphml │ └── whats_instand_upload.png ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.jar ├── gradlew ├── gradlew.bat ├── library ├── .gitignore ├── build.gradle ├── library.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── mtp │ │ └── rainx │ │ └── cn │ │ └── mtpcontroller │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── cn │ │ │ └── rainx │ │ │ ├── exif │ │ │ ├── ExifUtils.java │ │ │ └── GPS.java │ │ │ ├── ptp │ │ │ ├── db │ │ │ │ ├── SyncDevice.java │ │ │ │ ├── SyncDeviceManager.java │ │ │ │ └── Uuid.java │ │ │ ├── detect │ │ │ │ └── CameraDetector.java │ │ │ ├── interfaces │ │ │ │ ├── FileAddedListener.java │ │ │ │ ├── FileDownloadedListener.java │ │ │ │ └── FileTransferListener.java │ │ │ ├── params │ │ │ │ └── SyncParams.java │ │ │ └── usbcamera │ │ │ │ ├── BaselineInitiator.java │ │ │ │ ├── Buffer.java │ │ │ │ ├── Command.java │ │ │ │ ├── Container.java │ │ │ │ ├── Data.java │ │ │ │ ├── DeviceInfo.java │ │ │ │ ├── DevicePropDesc.java │ │ │ │ ├── DevicePropValue.java │ │ │ │ ├── Event.java │ │ │ │ ├── FileSendData.java │ │ │ │ ├── InitiatorFactory.java │ │ │ │ ├── KodakExtension.java │ │ │ │ ├── NameFactory.java │ │ │ │ ├── ObjectInfo.java │ │ │ │ ├── OutputStreamData.java │ │ │ │ ├── PTPBusyException.java │ │ │ │ ├── PTPException.java │ │ │ │ ├── PTPOpenSessionException.java │ │ │ │ ├── PTPUnsupportedException.java │ │ │ │ ├── ParamVector.java │ │ │ │ ├── Response.java │ │ │ │ ├── Session.java │ │ │ │ ├── StorageInfo.java │ │ │ │ ├── eos │ │ │ │ ├── EosEvent.java │ │ │ │ ├── EosEventConstants.java │ │ │ │ ├── EosEventFormat.java │ │ │ │ ├── EosEventParser.java │ │ │ │ └── EosInitiator.java │ │ │ │ ├── nikon │ │ │ │ ├── NikonEvent.java │ │ │ │ ├── NikonEventConstants.java │ │ │ │ ├── NikonEventFormat.java │ │ │ │ ├── NikonEventParser.java │ │ │ │ └── NikonInitiator.java │ │ │ │ └── sony │ │ │ │ ├── SonyDevicePropDesc.java │ │ │ │ ├── SonyExtDeviceInfo.java │ │ │ │ └── SonyInitiator.java │ │ │ └── tracker │ │ │ ├── BaseInfo.java │ │ │ └── IuTracker.java │ └── res │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── device_filter.xml │ └── test │ └── java │ └── mtp │ └── rainx │ └── cn │ └── mtpcontroller │ └── ExampleUnitTest.java ├── local.properties ├── mtp_controller.iml ├── mtpcontrollerdemo ├── .gitignore ├── build.gradle ├── mtpcontrollerdemo.iml ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── cn │ │ └── rainx │ │ └── demo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── cn │ │ │ └── rainx │ │ │ └── demo │ │ │ └── ControllerActivity.java │ └── res │ │ ├── layout │ │ └── activity_controller.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── cn │ └── rainx │ └── demo │ └── ExampleUnitTest.java └── settings.gradle /.gradle/4.6/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/4.6/fileHashes/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/4.6/fileHashes/fileHashes.bin -------------------------------------------------------------------------------- /.gradle/4.6/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/4.6/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /.gradle/4.6/fileHashes/resourceHashesCache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/4.6/fileHashes/resourceHashesCache.bin -------------------------------------------------------------------------------- /.gradle/4.6/taskHistory/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/4.6/taskHistory/taskHistory.bin -------------------------------------------------------------------------------- /.gradle/4.6/taskHistory/taskHistory.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/4.6/taskHistory/taskHistory.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Mon Nov 25 15:18:52 CST 2019 2 | gradle.version=4.6 3 | -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /.gradle/vcsWorkingDirs/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/.gradle/vcsWorkingDirs/gc.properties -------------------------------------------------------------------------------- /InstantUpload.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Main.md: -------------------------------------------------------------------------------- 1 | # Instant Upload 数码相机即时上传api 2 | 3 | ## 什么是即时即时上传api 4 | 5 | 即时上传api可以通过让手机通过USB (android OTG方式) 数据线连接数码相机设备,在数码相机有新增文件操作的时候,即时上传内容到手机中(usb host) 6 | 7 | ![即时上传的结构](docs/yed/whats_instand_upload.png) 8 | 9 | 对于协议的情况,请参考 10 | 11 | [说明文档](README.md) 12 | 13 | ## 一些调研主题 14 | 15 | - [为家用单反服务](docs/For_Personal_Camera.md) 16 | - [简单适配度高](docs/Simple_and_High_Adaptation.md) 17 | - 爱云动的业务封装 18 | - 后端的存储,云盘等... 19 | - 业务场景,双向打通 20 | - [数据采集,地理信息的同步](docs/Geo.md) 21 | - [传输方式不限于USB线](docs/OtherTransport.md) 22 | - 相机的特殊操作 23 | - [在照片上加语音,加信息](docs/AddVoiceAndText.md) 24 | - 爱运动App和摄端App的整合 25 | - [如何让苹果iOS连接相机](docs/iOSSupport.md) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Instant Upload 爱云动即时上传解决方案 2 | 3 | ## 关于即时上传系统 4 | 5 | 开发缘起是爱云动公司需要开发即时上传模块的需求,作为其影响处理程序的模块之一。所谓即时上传是针对数码相机 (或有拍照功能的智能手机等设备),我们可以将其与手机(安卓系统)连接,在拍照之后即时的将照片传输到手机的功能 (当然后续有可能还会上传到云端等,whatever,我们当前只关注相机到手机的传输部分)。 6 | 7 | 我们的模块将实现如下的功能: 8 | 9 | - 识别相机对手机的连接,拔出等操作,并作出响应 10 | - 对相机型号进行识别 11 | - 针对相机进行记录,保存其文件历史。 12 | - 实现文件自动同步到手机中。 13 | - 支持通过手机增强图片的exif信息,增加地理位置和同步时间信息等。 14 | - 识别相机不同的sd卡,并建立记录? ... 15 | 16 | ## 目前可以使用的同步方式 17 | 18 | ### USB mass storage device class 方式 19 | 20 | 简称后USB MSC 或者 USC, 可以称作大容量存储设备,很多相机都支持的一种文件传输方式,基于USB数据线传输,这种方式,通常手机使用OTG线与相机连接,手机作为usb host, 相机作为usb device. 21 | 22 | 简单来说,就是将相机变为类似u盘一样的存储设备和手机交互的方式,在这种方式下,(一般来说)存储设备会被独占,所以无法同时进行拍照操作,适合离线的批量导入的时候使用。 23 | 24 | 协议的具体知识参见文档: 25 | 26 | (下面连接 **需要翻墙访问** ) 27 | 28 | or 29 | 30 | 31 | 32 | 目前新版本的安卓设备在接入OTG线的时候一般会自动识别出MSC设备并映射到文件系统的某个载入点中, 针对这个特性,程序应该可以实现批量文件的导入和管理。 33 | 34 | ### USB PTP协议方式 35 | 36 | 和上述方式类似,也是基于OTG连接的USB数据线进行传输的方式,也是我们的模块主要实现的方式,这个方式的好处是PTP协议是专门针对相机的相片传输的协议,并且在传输过程中不会锁定存储设备,也就是同时还可以进行拍照等操作。 37 | 38 | 改实现方式我们会在后面重点说明 39 | 40 | ### WIFI传输方式 41 | 42 | 很多新型相机,如Sony实现了通过WIFI和手机连接传输的方式,并提供了对应的sdk, 43 | 44 | 45 | 46 | 无线版本的远程控制接口一般基于http和json,与普通的服务器端程序接口类似。开发起来比较容易。 47 | 48 | 缺点是,在实际使用中,需要手机连接相机提供的wifi,可能会对手机的网络连接有一定影响。如果要实现同步到云端等功能时,难度会增加。 49 | 50 | ### 其它端口 51 | 52 | #### 蓝牙 53 | 54 | 蓝牙的传输速度难以满足需求 55 | 56 | #### hdmi 57 | 58 | 一般用来传输预览信息,一般是单向传输,无法对相机进行控制。 59 | 60 | ## 关于PTP协议 61 | 62 | > PTP是英语"图片传输协议(picture transfer protocol)"的缩写。PTP是最早由柯达公司与微软协商制定的一种标准,符合这种标准的图像设备在接入Windows XP系统之后可以更好地被系统和应用程序所共享,尤其在网络传输方面,系统可以直接访问这些设备用于建立网络相册时图片的上传、网上聊天时图片的传送等。 63 | 64 | PTP协议被广泛使用在相机厂商用于图片和视频的传输,但是不同的厂商根据协议的基础格式进行了不同的定制和裁剪,所以不同厂商的PTP协议并不兼容。 65 | 66 | 除了相机厂商定制的PTP协议,微软后来为了适应除了图像类文件传入之外的其它媒体文件传入,对PTP协议进行了扩充,推出了MTP协议,支持更多的文件传入,并扩充了指令集,增强了DRM版权保护等的支持,广泛应用于各类媒体产品中,如手机,MP3等设备。 67 | 68 | PTP本身没有约定文件传输使用的物理介质和底层传输协议,目前USB, 蓝牙, TCP/IP等都可以作为底层传输介质存在。 69 | 70 | 由于速度等约束,目前使用最多的传输介质还是通过USB数据线比较多。 71 | 72 | 关于PTP最初协议的说明,可以参考下面文档 73 | 74 | 75 | 76 | 关于MTP协议的定义,参考下面文档 77 | 78 | 79 | 80 | 关于PTP/IP协议的实现,参考这个链接 81 | 82 | 83 | 84 | ## 我们目前实现的同步方式 85 | 86 | 目前我们使用基于USB的PTP协议作为图片文件的传输方式,由于不同厂商对PTP协议的支持并不相同,所以我们需要对不同的厂商的PTP协议部分分别实现。 后面我们会提到实现的细节。首先我们先对USB协议和PTP的基础概念做一个介绍。 87 | 88 | ### USB的协议 89 | 90 | USB是通用穿行总线的缩写, USB数据协议是一个不对等协议,传输的双方分别是Host(主机)和 Device或Accessory或Function(设备或者附件), 数据指令的请求总是从Host发起的,不过Device可以使用INTERRUPT传输机制来对HOST发起通知。 91 | 92 | ![http://hiphotos.baidu.com/aggio/pic/item/4742fbd6aea9a130a08bb77e.jpg?_=3591452](USB设备) 93 | 94 | 为了对USB设备不同功能的复用,USB设备将自己的功能作为一个configuration上报给HOST, 一个USB可以有多套configuration, 对应多个 Interface, 每个Interface对应多个Endpoint, 每个Endpoint对应不同的传输方向, 可以有 Bulk In , Bulk Out, Interrupt In 等(相机一般对应上面的3个端点) Endpoint一般有四种传输方式 ( ) 95 | 96 | - Control Transfers 97 | - Interrupt Transfers 98 | - Isochronous Transfers 99 | - Bulk Transfers 100 | 101 | PTP一般对应Bulk In , Bulk Out, Interrupt In三个端点。 其中的In 和 Out的方向是以Host作为视角确认的方向,比如Bulk In 就是从Device拉取数据到Host的传输。 102 | 103 | ### PTP over USB 104 | 105 | PTP/USB 是在USB协议技术上, 指令了一组指令集,负责相机和Host的交互,早期的Host的角色一般由个人电脑来承担,现在,安卓手机等设备也可以做为Host设备,当然,手机早期也可以作为Device存在的,所以可以把两台手机通过OTG数据线链接起来,这个时候一台作为Host,一台作为Device。 106 | 107 | 在PTP的定义里面,Host被定义为Initiator, Device被定义为Responder,在PTP协议里面,有下面几个比较重要的概念 108 | 109 | Operation : Operation可以理解为一次指令的抽象,所有的Operation 都通过 Initiator 发起,会经过 Operation -> Data Phase -> Response 的一个组合完成一个事物(Transaction), 其中 Operation 必定由 Initiator 发出到 Responder, Response是Responder -> Initiator 。 Data 的方向既可以是 Initiator -> Responder 也可以是 Responder -> Initiator。 依赖与 Operation 的类型,Host可以定义 Session ,用来为一组 Operation 定义一个统一的上下文环境,除了 Operation 之外, Device 也可以(一般通过 Interrupt Endpoint)发送 Event 给Host,用来传递事件。另外,相机可以通过特定的指令来设置,获取相机的属性(Property) 110 | 111 | Object : PTP协议里面,定义每一个(照片,影像)文件为一个Object, 每一个Object可以对应到一个图片,每一个Object都可以用一个32位的(无符号)整型作为句柄,可以称作 Object Handle。 在PTP协议里规定,在同一个Session里面,同一个文件的Object Handle必须相同,但是一般来说,几乎所有的实现(我目前看到的),即使在不同的Session的情况下,同一个文件的Object Handle也是相同的。 112 | 113 | ### 我们的实现 114 | 115 | 针对上面的描述和PTP常用的指令,我们实现了下面2钟主要的方式用来进行图片的同步 116 | 117 | #### 事件模式 Event 118 | 119 | 事件模式基于PTP协议的ObjectAdded事件(通过Interrupt Endpoint),在ObjectAdded事件中获取到Object Handle, 然后使用 GetObject 指令获取文件。 120 | 121 | ObjectAdded 模式有如下几个问题 122 | 123 | 1. 部分相机,如Canon并不使用Interrupt Endpoint,而是使用CheckEvent指令来模拟获取ObjectAdded事件,这种方式需要利用到Bulk in 和 Bulk out端点轮询来实现,和文件传输会有冲突。 124 | 2. 还是Canon相机,在文件传输过程中,如果文件传输过程中有新的事件并且新的事件比较多,有可能在CheckEvent的时候会丢失一些事件,导致在拍照频繁的时候同步文件会有部分丢失。 125 | 3. Event模式只能处理插入USB线并开启Session之后的所有新增文件,对离线增加的文件无法同步。 126 | 127 | #### 列表轮询模式 PollList 128 | 129 | 列表轮询模式通过轮询调用GetObjectHandles指令,获取Storage上的Object列表,检查两次获取之间Object列表的变化,识别新增的文件。并对新增的文件调用GetObject指令进行下载。这个方式的优点是相对稳定和可靠,可以识别出USB没有插入的时候的文件变化并同步。不过有如下的缺点。 130 | 131 | 1. PollList使用轮询的方式,如果设计轮询的时间过长,会影像发现新文件增加的时间。 132 | 2. PTP协议并没有说明不同Session下同一个文件的Object Handle必须相同,所以这个可能会是一个隐患(虽然暂时没有发现这个现象)。 133 | 3. 部分相机,如Sony的某些型号不支持在拍照时同时调用GetObjectHandles指令 134 | 135 | ## 我们系统中的参数定义 136 | 137 | 我们的系统里面,定义了若干参数,参数的选项定义在 cn.rainx.ptp.params.SyncParams 下, 参数通过定义BaseIntiator下的若干组getter/setter来获取/设置。 138 | 139 | 下面解释一下常用的设定 140 | 141 | ### setFileNameRule,设定文件名称的命名方式 142 | 143 | - FILE_NAME_RULE_HANDLE_ID : 设定为Object Handle 144 | - FILE_NAME_RULE_OBJECT_NAME : 设定为Object的名称,就是相机SD里卡保存的文件名,依赖于多发送一个GetObjectInfo指令。 145 | 146 | ### setSyncTriggerMode, 设定同步出发模式 147 | 148 | - SYNC_TRIGGER_MODE_EVENT : 事件模式。 149 | - SYNC_TRIGGER_MODE_POLL_LIST : 设置为列表轮询模式。 150 | 151 | ### setSyncMode, 设置同步模式 152 | 153 | - SYNC_MODE_SYNC_ALL : 同步所有内容 154 | - SYNC_MODE_SYNC_NEW_ADDED : 同步新增内容 155 | 156 | ### setSyncRecordMode, 设置同步记录模式 157 | 158 | - SYNC_RECORD_MODE_REMEMBER: 记住上次的同步点,如果设置次选项,我们会在设备插入的时候,针对设备信息建立数据表中的记录,以便于下次插入数据线的时候,可以继续上次的同步点进行新的内容的同步。 159 | - SYNC_RECORD_MODE_FORGET: 不记住上次的同步点 160 | 161 | 对于 SYNC_MODE 和 SYNC_RECORD_MODE 公有四种组合 162 | 163 | SYNC_MODE \ SYNC_RECORD_MODE | SYNC_RECORD_MODE_REMEMBER | SYNC_RECORD_MODE_FORGET: 164 | ---------------------------- | ------------------------- | ------------------------ 165 | SYNC_MODE_SYNC_ALL | polllist | polllist 166 | SYNC_MODE_SYNC_NEW_ADDED | polllist | polllist/event 167 | 168 | 其中 EVENT 模式只能支持SYNC_MODE_SYNC_NEW_ADDED + SYNC_RECORD_MODE_FORGET 的组合, POLLLIST模式支持所有四种模式 169 | 170 | 下面分别介绍一下下面几种组合的行为: 171 | 172 | 模式 | 解释 173 | ---------------------------------------------------- | ----------------------------------------------------------------------------------------- 174 | SYNC_RECORD_MODE_FORGET + SYNC_MODE_SYNC_NEW_ADDED | 每次连接只同步插入USB数据线之后的新增文件 175 | SYNC_RECORD_MODE_FORGET + SYNC_MODE_SYNC_ALL | 每次连接都同步所有文件 176 | SYNC_RECORD_MODE_REMEMBER + SYNC_MODE_SYNC_NEW_ADDED | 在第一次插入相机之后开始同步文件,不同步第一次插入相机之前的文件,在拔出USB线再次连接之后,同步上次同步完成的文件之后新增的所有文件(无论图片是否是在插入USB线的时候拍摄的) 177 | SYNC_RECORD_MODE_REMEMBER + SYNC_MODE_SYNC_ALL | 第一次插入相机之后开始同步相机内的所有文件,在拔出USB线再次连接之后,同步上次同步完成的文件之后新增的所有文件(无论图片是否是在插入USB线的时候拍摄的) 178 | 179 | ## 如何针对不同的相机进行调试 180 | 181 | 不同的相机对指令的支持程度不一致,导致针对不同的相机,实现方式不同。目前的开源项目中, gphoto对相机设备的支持是比较全面的,它内部使用了libptp2来连接不同的设备,因为厂商的PTP协议一般是不对外开放的,我们一般可以通过查看gphoto的实现方式来理解不同相机的调用指令。 182 | 183 | gphoto是一个命令行工具,可以在*nix类型的系统执行,并提供了一组指令来实现相机的常用功能,比如 184 | 185 | ``` 186 | gphoto2 --wait-event 187 | ``` 188 | 189 | 用来监听相机的事件,如果遇到ObjectAdded事件,它会自动进行下载。 190 | 191 | 我们可以通过 `--debug`指令来查看一些详细的调试信息。 `--debug-logfile` 将它输出到日志文件中, 如: 192 | 193 | ``` 194 | gphoto2 --wait-event --debug --debug-logfile=/tmp/5d_wait-event-cap.log 195 | ``` 196 | 197 | 对于gphoto2不支持的相机,如果其有官方的软件可以进行交互的话,我们可以使用一些USB嗅探工具来监听USB数据包来分析其交互。 198 | 199 | ## 未来的优化展望 200 | 201 | 1. 在程序库的组织上,目前的实现是基于之前开源的一个半成品的基础上继续开发的,有一个问题是BaseIntiator里面的代码过多,需要将部分功能抽离出来,如将 EventPoll 抽离出来。 202 | 2. 对于Canon的Event模式,之前使用的方式会发现在GetObject获取较大文件的时候,有可能会丢部分事件,可以考虑使用GetObjectPartial将一个文件的获取拆成很多小的请求,通过分配时间片的方式,将GetObjectPartial 和CheckEvent请求轮番发送,保证在下载文件的同时持续可以获取Event. 203 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } 6 | jcenter() 7 | google() 8 | maven { 9 | url 'https://maven.google.com' 10 | } 11 | // jcenter() 12 | } 13 | dependencies { 14 | classpath 'com.android.tools.build:gradle:3.2.1' 15 | 16 | // NOTE: Do not place your application dependencies here; they belong 17 | // in the individual module build.gradle files 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } 24 | google() 25 | maven { 26 | url 'https://maven.google.com' 27 | } 28 | jcenter() 29 | //jcenter() 30 | } 31 | } 32 | 33 | task clean(type: Delete) { 34 | delete rootProject.buildDir 35 | } 36 | -------------------------------------------------------------------------------- /docs/AddVoiceAndText.md: -------------------------------------------------------------------------------- 1 | # 在照片上加语音,加信息 2 | 3 | ## 概述 4 | 5 | 添加语音和文本和即时上传模块的耦合并不紧密,可以在照片从相机同步到手机之后的任意时刻开启,本质上你可以单独的保存声音和文本的信息,只要在之后对这些信息和同步到云端的信息进行关联即可。 6 | 7 | ## 流程 8 | 9 | 这里分为两种流程,需要分别进行处理,主要的区别在于,如果相片还没上传成功,这个时候,还没有云端的photo id ,则如果需要进行关联,需要现在本地保存照片以及声音等信息的关联关系,然后可以选择同时将照片和附加信息上传到服务器端(下图1展示的情况),也可以等待照片上传完成之后,拿到photo id, 再次上传附件信息,类似(图2) 10 | 11 | ### 照片只上传到手机端,还未上传到云端的情况 12 | 13 | 图1 14 | 15 | ``` 16 | +------------------------------------------------+ 17 | | Camera | 18 | +----------------------+-------------------------+ 19 | | 20 | | transport 21 | +----------------------v-------------------------+ 22 | | Photo in Android Device | 23 | +----------------------+-------------------------+ 24 | | 25 | | Record Voice 26 | | 27 | +----------------------v--------------------------+ 28 | |Local DB to store relation between photo and voice etc... 29 | +----------------------+--------------------------+ 30 | | 31 | | Upload both photo and Voice to cloud 32 | +----------------------v-------------------------+ 33 | | Cloud space | 34 | +------------------------------------------------+ 35 | ``` 36 | 37 | ### 照片已经上传到云端的情况 38 | 39 | 图2 40 | 41 | ``` 42 | +------------------------------------------------+ 43 | | Camera | 44 | +----------------------+-------------------------+ 45 | | 46 | | transport 47 | +----------------------v-------------------------+ 48 | | Photo in Android Device | 49 | +--+--------+----------------+-------------------+ 50 | | ^ | 51 | | | | record Voice etc.. 52 | | upload | +-------v-------------------+ 53 | | | | | 54 | | | +-------+-------------------+ 55 | |Get photo ID | 56 | | | | upload Voice and Photo ID 57 | | | | 58 | +--v--------+----------v-----v-------------------+ 59 | | Cloud space | 60 | +------------------------------------------------+ 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/For_Personal_Camera.md: -------------------------------------------------------------------------------- 1 | # 为家用单反服务 2 | 3 | 为了支持家用级别的单反,需要实现如下的特征 4 | 5 | - 需要更广泛的支持PTP的各个变种,家用单反的型号众多,对PTP协议的支持也不尽相同,一般来说,较新的相机对协议的支持较好,但还有众多的型号采用自己的PTP协议变种,需要对这类协议进行支持 6 | - 需要一个自动识别相机型号并采用对应协议的机制,这个可以根据vendor id 和 device id ,结合一个自己的产品数据库来实现。 7 | - 需要支持除PTP协议之外的其它协议,如MSC, WI-FI,蓝牙,红外等... 8 | -------------------------------------------------------------------------------- /docs/Geo.md: -------------------------------------------------------------------------------- 1 | # 数据采集,地理信息的同步 2 | 3 | ## 如何向文件写入地理位置信息 4 | 5 | 关于地理信息的同步,可以使用代码中的, 6 | 7 | cn.rainx.exif 包下的`ExifUtils`类实现, 尤其对与Jpeg格式的写入,相对比较简单,如果要支持`Raw`格式的`Exif`信息的写入,可以参考: 8 | 9 | 如下实现: 10 | 11 | - java版 libraw 12 | - c++11版本 libopenraw 13 | 14 | ## 地理位置信息如何取值 15 | 16 | - 地理信息,通常可以使用Android自带的LocationManager获取 17 | - 利用百度,高德等第三方SDK获取GPS信息 18 | 19 | 建议使用第三方SDK,这样在调用的渐变性,适应性以及省电等方面都不需要自己过多考虑 20 | 21 | - 高德地图定位api 22 | - 百度地图定位api 23 | 24 | ## 坐标体系的转换 25 | 26 | 不同的坐标体系,在互相使用的时候,需进行转换,请注意 27 | 28 | 百度的坐标系说明 29 | 30 | ``` 31 | 目前国内主要有以下三种坐标系: 32 | 1\. WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系; 33 | 2\. GCJ02:表示经过国测局加密的坐标; 34 | 3\. BD09:为百度坐标系,其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标; 35 | ``` 36 | 37 | 高德的坐标转换 38 | 39 | 40 | 41 | ## 何时记录 42 | 43 | 安卓客户端最好根据时间序列缓存自己的位置信息,在上传的时候,从本地缓存的位置信息里面获取位置信息,并对文件进行更新,如果在文件更新的时候,安卓设备还没有位置信息,可以暂时不进行更新,并对未更新位置信息的照片文件进行记录,等待获取位置信息之后再进行更新。 44 | 45 | ## 参考 46 | 47 | - exif信息对照 48 | - 高德地图API 49 | - 百度地图API 50 | -------------------------------------------------------------------------------- /docs/OtherTransport.md: -------------------------------------------------------------------------------- 1 | # 传输方式不限于USB线。 2 | 3 | ## 还有那些方式可以使用 4 | 5 | - 这个本质上也算是USB线的方式,就是UMSC协议名,就是类似U盘,SD卡读卡器使用的协议。 6 | - 蓝牙设备传输,需要参考蓝牙的传输协议,Android官方SDK也有对蓝牙传输的支持 7 | - 基于wifi的传输协议,主要用于sony手机的传输 8 | - 视频输出线 (主要传输视频信息) 9 | -------------------------------------------------------------------------------- /docs/Simple_and_High_Adaptation.md: -------------------------------------------------------------------------------- 1 | # 简单,适配度高 2 | 3 | 和面向家用单反一样,如果要实现简单,适配度高,需要在代码的逻辑上增强对相机的识别和协议的识别,具体的流程如下 4 | 5 | ``` 6 | +---------------------------------------------------------+ 7 | | auto|detect connection | 8 | +----------------------------+----------------------------+ 9 | | 10 | | 11 | +----------------------------v----------------------------+ 12 | | get usb vendor and device id | 13 | +----------------------------+----------------------------+ 14 | | 15 | | 16 | +----------------------------v----------------------------+ 17 | | Search local(remote) to get best protocol | 18 | +----------------------------+----------------------------+ 19 | | 20 | | 21 | +----------------------------v----------------------------+ 22 | | Start sync(use ptp or wifi or msc or others) | 23 | +------------+-----------------------------------+--------+ 24 | | ^ 25 | | | 26 | | | 27 | | | 28 | +-----------------------------------+ 29 | 30 | if failed , auto change to other protocol and report to server 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/iOSSupport.md: -------------------------------------------------------------------------------- 1 | # iPhone的支持 2 | 3 | 我们目前的传输来看,安卓手机基本上使用的是OTG线与相机进行连接的,但是对于iPhone来说,实现OTG连接有一定难度,我们看iPhone的连接接口,是苹果的Lightning接口,Lightning接口可以看如下的介绍: 4 | 5 | - 6 | - 7 | - 8 | 9 | Lightning接口和USB还是有较大区别的 10 | 11 | ``` 12 | 作者:路痴:https://www.zhihu.com/question/24845265/answer/29188584 13 | Lightning和USB不是同一个层面的东西。认为两者只差一个双面插入就错了。从theiphonewiki Lightning Connector 里这段匿名懦夫(很可能是个apple工程师)爆料来看,Lightning的设计是:让线智能化,线里面必须有个主控芯片用来和iDevice交互。然后通过线跟主机交互的过程分别是:从iDevice->线主控,不知名高速串行总线;从线主控到PC,USB2.0(当前——我是说截至2014.8)。这个设计的好处不言而喻,iDevice成为真正意义上的主机(不是USB otg那种首鼠两端的),理论上可以输入/输出到任意设备(还记得之前听说的Lightning耳机么?)。就数据线而言,虽然当前只有USB 2.0,但以后换成USB3.0/3.1很可能也只是换根线的问题。(未考虑 @时国怀 提到的物理设计问题。但iPhone5发布时USB3.0已出,我估计苹果应该不至于没考虑过这类问题吧)坏处当然是由于线并非dumb线,成本提高(不过这个难题已被国内山寨厂率先攻克),另外输出HDMI这类数据时必须先在iDevice端编码,线主控再解码成HDMI数据流,这也就是之前Panic发现iPhone5的HDMI输出线里居然塞进了一台iPhone4的CPU的原因,延迟不说,画质也因此受到影响。而USB,不管它是什么版本……都是dumb线。 14 | Lightning和USB不是同一个层面的东西。认为两者只差一个双面插入就错了。从theiphonewiki Lightning Connector 里这段匿名懦夫(很可能是个apple工程师)爆料来看,Lightning的设计是:让线智能化,线里面必须有个主控芯片用来和iDevice交互。然后通过线跟主机交互的过程分别是:从iDevice->线主控,不知名高速串行总线;从线主控到PC,USB2.0(当前——我是说截至2014.8)。这个设计的好处不言而喻,iDevice成为真正意义上的主机(不是USB otg那种首鼠两端的),理论上可以输入/输出到任意设备(还记得之前听说的Lightning耳机么?)。就数据线而言,虽然当前只有USB 2.0,但以后换成USB3.0/3.1很可能也只是换根线的问题。(未考虑 @时国怀 提到的物理设计问题。但iPhone5发布时USB3.0已出,我估计苹果应该不至于没考虑过这类问题吧)坏处当然是由于线并非dumb线,成本提高(不过这个难题已被国内山寨厂率先攻克),另外输出HDMI这类数据时必须先在iDevice端编码,线主控再解码成HDMI数据流,这也就是之前Panic发现iPhone5的HDMI输出线里居然塞进了一台iPhone4的CPU的原因,延迟不说,画质也因此受到影响。而USB,不管它是什么版本……都是dumb线。 15 | ``` 16 | 17 | 可见,苹果的Lightning数据线和USB设备相对,多出了一个MFI认证层,这里面,苹果在商业上限制了只有经过MFI认证的设备才可以与之连接传输,虽然苹果有自己的USB Host设备,并切Lightning的传输线缆部分与USB设备并无却别,但是这层MFI认证让第三方USB设备的接入增加很大的难度.. 18 | -------------------------------------------------------------------------------- /docs/tracker.md: -------------------------------------------------------------------------------- 1 | # 性能指标,Bug以及设备收集 2 | 3 | ## 性能指标 4 | 5 | 表名 : `iu_perf_tracker` 6 | 7 | 字段 | 类型 | 备注 8 | ------------------------ | ------------ | ---------------------------------------- 9 | id | integer | 自增 10 | vender_id | integer | 11 | device_id | integer | 12 | serial | varchar(255) | 设备串号 13 | android_device_unique_id | varchar(255) | 安卓设备唯一编号 14 | android_user_unique_id | integer | 使用用户的唯一id编号 15 | android_device_info | varchar(255) | Build.BRAND + Build.DEVICE + Build.MODEL 16 | android_os_ver | integer | 操作系统版本 17 | lib_version | integer | 所使用的库的版本 18 | sync_trigger_mode | integer | 所使用的同步触发模式 19 | sync_mode | integer | 同步模式 20 | sync_record_mode | integer | 同步记录模式 21 | filesize | integer | 传输文件尺寸 22 | start_ts | float | 开始时间 23 | end_ts | float | 结束时间 24 | cost_ts | float | 消耗时间 25 | created_at | float | 记录创建时间 26 | 27 | ## Bug信息收集 28 | 29 | 表名 : `iu_bug_tracker` 30 | 31 | 字段 | 类型 | 备注 32 | ------------------------ | ------------ | ---------------------------------------- 33 | vender_id | integer | 34 | device_id | integer | 35 | serial | varchar(255) | 设备串号 36 | android_device_unique_id | varchar(255) | 安卓设备唯一编号 37 | android_user_unique_id | integer | 使用用户的唯一id编号 38 | android_device_info | varchar(255) | Build.BRAND + Build.DEVICE + Build.MODEL 39 | android_os_ver | integer | 操作系统版本 40 | lib_version | integer | 所使用的库的版本 41 | sync_trigger_mode | integer | 所使用的同步触发模式 42 | sync_mode | integer | 同步模式 43 | sync_record_mode | integer | 同步记录模式 44 | exception | text | 产生的异常 45 | traceback | text | 调用堆栈 46 | fileinfo | text | 如果当前正在传输文件,则记录文件的一些信息 47 | created_at | float | 记录创建时间 48 | 49 | ## 设备信息收集 50 | 51 | 表名`iu_device_info_tracker` 52 | 53 | 字段 | 类型 | 备注 54 | ------------------------ | ------------ | ---------------------------------------- 55 | id | integer | 自增 56 | vender_id | integer | 57 | device_id | integer | 58 | serial | varchar(255) | 设备串号 59 | android_device_unique_id | varchar(255) | 安卓设备唯一编号 60 | android_user_unique_id | integer | 使用用户的唯一id编号 61 | android_device_info | varchar(255) | Build.BRAND + Build.DEVICE + Build.MODEL 62 | lib_version | integer | 所使用的库的版本 63 | android_os_ver | integer | 操作系统版本 64 | 65 | device_info | text | 设备信息 | created_at | float | 记录创建时间 66 | 67 | ## TraceBack获取 68 | 69 | 70 | 71 | - Arrays.toString(Thread.currentThread().getStackTrace()); simple enough. – 72 | - To print it nicely you can use apache StringUtils: StringUtils.join(currentThread().getStackTrace(), "\n"); 73 | - String fullStackTrace = org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace(e); 74 | 75 | ## Exception获取 76 | 77 | - String errorMessage = e.getMessage(); 78 | - e.toString() 79 | 80 | ```java 81 | Exception e = new Exception("hello"); 82 | Log.v(TAG, e.toString()); 83 | Log.v(TAG, e.getMessage()); 84 | Log.v(TAG, e.getClass().toString()); 85 | ``` 86 | 87 | result 88 | 89 | ```java 90 | 09-09 18:59:22.779 10394-10409/cn.rainx.demo I/TestRunner: started: testException(cn.rainx.demo.ExampleInstrumentedTest) 91 | 09-09 18:59:22.781 10394-10409/cn.rainx.demo V/cn.rainx.demo.ExampleInstrumentedTest: java.lang.Exception: hello 92 | 09-09 18:59:22.781 10394-10409/cn.rainx.demo V/cn.rainx.demo.ExampleInstrumentedTest: hello 93 | 09-09 18:59:22.781 10394-10409/cn.rainx.demo V/cn.rainx.demo.ExampleInstrumentedTest: class java.lang.Exception 94 | 09-09 18:59:22.781 10394-10409/cn.rainx.demo I/TestRunner: finished: testException(cn.rainx.demo.ExampleInstrumentedTest) 95 | ``` 96 | 97 | ## tracker 记录设备信息 98 | 99 | 每次device 信息report 应该是根据 手机uuid + 相机serial 来唯一标识确保只上传一次。 100 | -------------------------------------------------------------------------------- /docs/yed/whats_instand_upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/docs/yed/whats_instand_upload.png -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Tue May 30 11:24:01 CST 2017 16 | #systemProp.http.proxyHost=127.0.0.1 17 | org.gradle.jvmargs=-Xmx1536m 18 | #systemProp.http.proxyPort=12080 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /library/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /library/build.gradle: -------------------------------------------------------------------------------- 1 | // apply plugin: 'com.android.application' 2 | apply plugin: 'com.android.library' 3 | 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.2" 7 | defaultConfig { 8 | // applicationId "mtp.rainx.cn.mtpcontroller" 9 | minSdkVersion 21 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 26 | exclude group: 'com.android.support', module: 'support-annotations' 27 | }) 28 | compile 'com.android.support:appcompat-v7:25.3.1' 29 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 30 | compile 'com.github.satyan:sugar:1.5' 31 | compile 'com.loopj.android:android-async-http:1.4.9' 32 | testCompile 'junit:junit:4.12' 33 | } 34 | -------------------------------------------------------------------------------- /library/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/rainx/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /library/src/androidTest/java/mtp/rainx/cn/mtpcontroller/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package mtp.rainx.cn.mtpcontroller; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("mtp.rainx.cn.mtpcontroller", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/exif/ExifUtils.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.exif; 2 | 3 | import android.media.ExifInterface; 4 | import android.util.Log; 5 | 6 | import java.io.IOException; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | 10 | /** 11 | * Created by rainx on 2017/7/2. 12 | */ 13 | 14 | public class ExifUtils { 15 | 16 | public static final String TAG= "ExifUtils"; 17 | 18 | // 使用exifinterface 更新exif信息,根据安卓版本的不同,可以支持jpeg 和部分 raw 格式,如果更新失败,返回false 19 | /* 20 | GPS: 21 | 22 | https://stackoverflow.com/questions/5280479/how-to-save-gps-coordinates-in-exif-data-on-android 23 | 24 | GPSLatitude 25 | 26 | Indicates the latitude. The latitude is expressed as three RATIONAL values giving the degrees, minutes, and seconds, respectively. If latitude is expressed as degrees, minutes and seconds, a typical format would be dd/1,mm/1,ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two decimal places, the format would be dd/1,mmmm/100,0/1. 27 | 28 | https://docs.google.com/viewer?url=http%3A%2F%2Fwww.exif.org%2FExif2-2.PDF 29 | 30 | Datetime: 31 | 32 | Cant set Date Taken/DateTime tag using the ExifInterface in Android 33 | 34 | https://stackoverflow.com/questions/9004462/cant-set-date-taken-datetime-tag-using-the-exifinterface-in-android 35 | 36 | */ 37 | 38 | /** 39 | * 40 | * @param filePath 图片文件路径 41 | * @param latitude 纬度 42 | * @param longitude 经度 43 | * @param date 更新时间 44 | * @return 45 | */ 46 | public static boolean updateExifLocation(String filePath, double latitude, double longitude, Date date) { 47 | ExifInterface exif = null; 48 | try { 49 | // handle gps 50 | exif = new ExifInterface(filePath); 51 | exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, GPS.convert(latitude)); 52 | exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, GPS.latitudeRef(latitude)); 53 | exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, GPS.convert(longitude)); 54 | exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, GPS.longitudeRef(longitude)); 55 | 56 | // handle datetime 57 | SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); 58 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy:MM:dd"); 59 | SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); 60 | exif.setAttribute("DateTimeOriginal", dateTimeFormat.format(date)); 61 | exif.setAttribute("DateTimeDigitized", dateTimeFormat.format(date)); 62 | exif.setAttribute(ExifInterface.TAG_DATETIME,dateTimeFormat.format(date)); 63 | exif.setAttribute(ExifInterface.TAG_GPS_DATESTAMP,dateFormat.format(date)); 64 | exif.setAttribute(ExifInterface.TAG_GPS_TIMESTAMP,timeFormat.format(date)); 65 | 66 | exif.saveAttributes(); 67 | return true; 68 | } catch (IOException e) { 69 | e.printStackTrace(); 70 | Log.d(TAG, "can handle this file format, file is : " + filePath); 71 | return false; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/exif/GPS.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.exif; 2 | 3 | /* 4 | * @author fabien 5 | */ 6 | public class GPS { 7 | private static StringBuilder sb = new StringBuilder(20); 8 | 9 | /** 10 | * returns ref for latitude which is S or N. 11 | * @param latitude 12 | * @return S or N 13 | */ 14 | public static String latitudeRef(double latitude) { 15 | return latitude<0.0d?"S":"N"; 16 | } 17 | 18 | /** 19 | * returns ref for latitude which is S or N. 20 | * @param longitude 21 | * @return S or N 22 | */ 23 | public static String longitudeRef(double longitude) { 24 | return longitude<0.0d?"W":"E"; 25 | } 26 | 27 | /** 28 | * convert latitude into DMS (degree minute second) format. For instance
29 | * -79.948862 becomes
30 | * 79/1,56/1,55903/1000
31 | * It works for latitude and longitude
32 | * @param latitude could be longitude. 33 | * @return 34 | */ 35 | synchronized public static final String convert(double latitude) { 36 | latitude=Math.abs(latitude); 37 | int degree = (int) latitude; 38 | latitude *= 60; 39 | latitude -= (degree * 60.0d); 40 | int minute = (int) latitude; 41 | latitude *= 60; 42 | latitude -= (minute * 60.0d); 43 | int second = (int) (latitude*1000.0d); 44 | 45 | sb.setLength(0); 46 | sb.append(degree); 47 | sb.append("/1,"); 48 | sb.append(minute); 49 | sb.append("/1,"); 50 | sb.append(second); 51 | sb.append("/1000,"); 52 | return sb.toString(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/db/SyncDevice.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.db; 2 | 3 | import com.orm.SugarRecord; 4 | import com.orm.dsl.Unique; 5 | 6 | /** 7 | * 8 | * 9 | * device_uuid string unique 10 | * device_name string 11 | * product_name string 12 | * manufacturer_name string 13 | * vendor_id integer 14 | * device_id integer 15 | * product_id integer 16 | * serial_number string 17 | * version string (version of device) 18 | * sync_idlist_json text 19 | * is_syncing boolean 20 | * sync_at unix_timestamp 21 | * updated_at unix_timestamp 22 | * created_at unix_timestamp 23 | * 24 | * Created by rainx on 2017/5/27. 25 | */ 26 | 27 | public class SyncDevice extends SugarRecord { 28 | @Unique 29 | String deviceUUID; 30 | String deviceName; 31 | String productName; 32 | String manufacturerName; 33 | Integer vendorId; 34 | Integer deviceId; 35 | Integer productId; 36 | String serialNumber; 37 | String version; 38 | String syncIdList; 39 | boolean isSyncing; 40 | Long syncAt; 41 | Long updatedAt; 42 | Long createdAt; 43 | 44 | public SyncDevice() { 45 | // empty constructor 46 | } 47 | 48 | public SyncDevice(String deviceUUID, String deviceName, String productName, String manufacturerName, Integer vendorId, Integer deviceId, Integer productId, String serialNumber, String version, String syncIdList, boolean isSyncing, Long syncAt, Long updatedAt, Long createdAt) { 49 | this.deviceUUID = deviceUUID; 50 | this.deviceName = deviceName; 51 | this.productName = productName; 52 | this.manufacturerName = manufacturerName; 53 | this.vendorId = vendorId; 54 | this.deviceId = deviceId; 55 | this.productId = productId; 56 | this.serialNumber = serialNumber; 57 | this.version = version; 58 | this.syncIdList = syncIdList; 59 | this.isSyncing = isSyncing; 60 | this.syncAt = syncAt; 61 | this.updatedAt = updatedAt; 62 | this.createdAt = createdAt; 63 | } 64 | 65 | public String getDeviceUUID() { 66 | return deviceUUID; 67 | } 68 | 69 | public void setDeviceUUID(String deviceUUID) { 70 | this.deviceUUID = deviceUUID; 71 | } 72 | 73 | public String getDeviceName() { 74 | return deviceName; 75 | } 76 | 77 | public void setDeviceName(String deviceName) { 78 | this.deviceName = deviceName; 79 | } 80 | 81 | public String getProductName() { 82 | return productName; 83 | } 84 | 85 | public void setProductName(String productName) { 86 | this.productName = productName; 87 | } 88 | 89 | public String getManufacturerName() { 90 | return manufacturerName; 91 | } 92 | 93 | public void setManufacturerName(String manufacturerName) { 94 | this.manufacturerName = manufacturerName; 95 | } 96 | 97 | public Integer getVendorId() { 98 | return vendorId; 99 | } 100 | 101 | public void setVendorId(Integer vendorId) { 102 | this.vendorId = vendorId; 103 | } 104 | 105 | public Integer getDeviceId() { 106 | return deviceId; 107 | } 108 | 109 | public void setDeviceId(Integer deviceId) { 110 | this.deviceId = deviceId; 111 | } 112 | 113 | public Integer getProductId() { 114 | return productId; 115 | } 116 | 117 | public void setProductId(Integer productId) { 118 | this.productId = productId; 119 | } 120 | 121 | public String getSerialNumber() { 122 | return serialNumber; 123 | } 124 | 125 | public void setSerialNumber(String serialNumber) { 126 | this.serialNumber = serialNumber; 127 | } 128 | 129 | public String getVersion() { 130 | return version; 131 | } 132 | 133 | public void setVersion(String version) { 134 | this.version = version; 135 | } 136 | 137 | public String getSyncIdList() { 138 | return syncIdList; 139 | } 140 | 141 | public void setSyncIdList(String syncIdList) { 142 | this.syncIdList = syncIdList; 143 | } 144 | 145 | public boolean isSyncing() { 146 | return isSyncing; 147 | } 148 | 149 | public void setSyncing(boolean syncing) { 150 | isSyncing = syncing; 151 | } 152 | 153 | public Long getSyncAt() { 154 | return syncAt; 155 | } 156 | 157 | public void setSyncAt(Long syncAt) { 158 | this.syncAt = syncAt; 159 | } 160 | 161 | public Long getUpdatedAt() { 162 | return updatedAt; 163 | } 164 | 165 | public void setUpdatedAt(Long updatedAt) { 166 | this.updatedAt = updatedAt; 167 | } 168 | 169 | public Long getCreatedAt() { 170 | return createdAt; 171 | } 172 | 173 | public void setCreatedAt(Long createdAt) { 174 | this.createdAt = createdAt; 175 | } 176 | 177 | } 178 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/db/SyncDeviceManager.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.db; 2 | 3 | import android.hardware.usb.UsbDevice; 4 | import android.util.Log; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Date; 8 | import java.util.List; 9 | 10 | import cn.rainx.ptp.detect.CameraDetector; 11 | 12 | /** 13 | * !!! Not thread safe !!! 14 | * 理论上我们的程序应该运行在单一线程,所以为了性能考虑,没有加锁 15 | * Created by rainx on 2017/5/27. 16 | */ 17 | 18 | public class SyncDeviceManager { 19 | public static final String TAG = "SyncDeviceManager"; 20 | 21 | private UsbDevice device; 22 | 23 | public SyncDeviceManager(UsbDevice device) { 24 | this.device = device; 25 | } 26 | 27 | 28 | /** 29 | * 更新同步设备的基本信息, 如果设备信息已经存在,则只更新时间 30 | * 31 | * @return true: if first time create 32 | * null: if already exists 33 | * 34 | */ 35 | public SyncDevice updateDeviceInfo() { 36 | String uuid = getUUIDFromDevice(device); 37 | SyncDevice syncDevice = getSyncDevice(device); 38 | 39 | if (syncDevice == null) { 40 | syncDevice = new SyncDevice( 41 | uuid, 42 | device.getDeviceName(), 43 | device.getProductName(), 44 | device.getManufacturerName(), 45 | device.getVendorId(), 46 | device.getDeviceId(), 47 | device.getProductId(), 48 | device.getSerialNumber(), 49 | "", //device.getVersion() require android version 23; 50 | "", // empty set 51 | false, 52 | null, 53 | new Date().getTime(), 54 | new Date().getTime() 55 | ); 56 | syncDevice.save(); 57 | Log.d(TAG, "device recording"); 58 | } else { 59 | // 设备已经记录了 60 | syncDevice.setUpdatedAt((new Date()).getTime()); 61 | syncDevice.save(); 62 | Log.d(TAG, "device already recorded"); 63 | } 64 | return syncDevice; 65 | } 66 | 67 | /** 68 | * 在当前的手机上清除拍照设备信息 69 | * 70 | * @return 是否执行了删除操作 71 | */ 72 | public boolean removeDevice() { 73 | SyncDevice syncDevice = getSyncDevice(device); 74 | if (syncDevice != null) { 75 | return syncDevice.delete(); 76 | } else { 77 | return false; 78 | } 79 | } 80 | 81 | /** 82 | * 开始同步 83 | */ 84 | public void startSync() { 85 | SyncDevice syncDevice = getSyncDevice(device); 86 | 87 | if (syncDevice.getSyncAt() == null || syncDevice.getSyncAt() == 0) { 88 | syncDevice.setSyncAt(new Date().getTime()); 89 | } 90 | 91 | syncDevice.setUpdatedAt(new Date().getTime()); 92 | syncDevice.setSyncing(true); 93 | syncDevice.save(); 94 | } 95 | 96 | /** 97 | * 停止同步 98 | */ 99 | public void stopSync() { 100 | SyncDevice syncDevice = getSyncDevice(device); 101 | 102 | syncDevice.setUpdatedAt(new Date().getTime()); 103 | syncDevice.setSyncing(false); 104 | syncDevice.save(); 105 | } 106 | 107 | 108 | /** 109 | * 更新id列表 110 | * @param ids 111 | */ 112 | public void updateIdList(List ids) { 113 | StringBuffer strIdListBuffer = new StringBuffer(); 114 | if (ids != null) { 115 | int i = 1; 116 | for (Integer id : ids){ 117 | strIdListBuffer.append(id); 118 | if (i++ != ids.size()) { 119 | strIdListBuffer.append(","); 120 | } 121 | } 122 | } 123 | String strIdList = strIdListBuffer.toString(); 124 | 125 | SyncDevice syncDevice = getSyncDevice(device); 126 | syncDevice.setSyncIdList(strIdList); 127 | syncDevice.setSyncAt(new Date().getTime()); 128 | syncDevice.setUpdatedAt(new Date().getTime()); 129 | syncDevice.save(); 130 | } 131 | 132 | /** 133 | * 获取id列表 134 | * @return 135 | */ 136 | public List getIdList() { 137 | SyncDevice syncDevice = getSyncDevice(device); 138 | return getIdList(syncDevice); 139 | } 140 | 141 | /*** 142 | * 根据syncDevice获取id列表 143 | */ 144 | public List getIdList(SyncDevice syncDevice) { 145 | String strIdList = syncDevice.getSyncIdList(); 146 | List idList = new ArrayList<>(); 147 | if (strIdList == null || strIdList.equals("")) { 148 | return idList; 149 | } 150 | 151 | String[] strIdArr = strIdList.split(","); 152 | for(String id : strIdArr) { 153 | idList.add(Integer.valueOf(id)); 154 | } 155 | return idList; 156 | } 157 | 158 | /** 159 | * 获取数据库中的同步设备ORM 160 | * @param device 161 | * @return 162 | */ 163 | public SyncDevice getSyncDevice(UsbDevice device) { 164 | String uuid = getUUIDFromDevice(device); 165 | Log.d(TAG, "getSyncDevice by uuid:" + uuid ); 166 | List syncDevices = SyncDevice.find(SyncDevice.class, "DEVICE_UUID=?", uuid); 167 | if (syncDevices!= null && syncDevices.size() > 0) { 168 | return syncDevices.get(0); 169 | } else { 170 | Log.d(TAG, "is null"); 171 | return null; 172 | } 173 | } 174 | 175 | 176 | public UsbDevice getDevice() { 177 | return device; 178 | } 179 | 180 | public void setDevice(UsbDevice device) { 181 | this.device = device; 182 | } 183 | 184 | /** 185 | * 获取所有同步设备信息 186 | * @return 187 | */ 188 | public List getAllSyncDevices() { 189 | return SyncDevice.listAll(SyncDevice.class); 190 | } 191 | 192 | private String getUUIDFromDevice(UsbDevice device) { 193 | CameraDetector cd = new CameraDetector(device); 194 | String uuid = cd.getDeviceUniqName(); 195 | return uuid; 196 | } 197 | 198 | 199 | 200 | } 201 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/db/Uuid.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.db; 2 | 3 | import com.orm.SugarApp; 4 | import com.orm.SugarRecord; 5 | import com.orm.dsl.Unique; 6 | 7 | /** 8 | * Created by rainx on 2017/8/26. 9 | */ 10 | 11 | public class Uuid extends SugarRecord { 12 | @Unique 13 | String key; 14 | String value; 15 | 16 | public String getKey() { 17 | return key; 18 | } 19 | 20 | public void setKey(String key) { 21 | this.key = key; 22 | } 23 | 24 | public String getValue() { 25 | return value; 26 | } 27 | 28 | public void setValue(String value) { 29 | this.value = value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/detect/CameraDetector.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.detect; 2 | 3 | import android.hardware.usb.UsbDevice; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by rainx on 2017/5/23. 11 | */ 12 | 13 | public class CameraDetector { 14 | 15 | // Select appropriate deviceInitiator, VIDs see http://www.linux-usb.org/usb.ids 16 | 17 | public static final int VENDOR_ID_CANON = 0x04a9; 18 | public static final int VENDOR_ID_NIKON = 0x04b0; 19 | public static final int VENDOR_ID_SONY = 0x054c; 20 | 21 | public static final int VENDOR_ID_OTHER = 0xffff; 22 | 23 | static List vendorIds; 24 | static { 25 | vendorIds = Arrays.asList( 26 | VENDOR_ID_CANON, 27 | VENDOR_ID_NIKON, 28 | VENDOR_ID_SONY 29 | ); 30 | } 31 | 32 | 33 | private UsbDevice device; 34 | public CameraDetector(UsbDevice device) { 35 | this.device = device; 36 | } 37 | 38 | 39 | public int getSupportedVendorId() { 40 | if (vendorIds.contains(device.getVendorId())) { 41 | return device.getVendorId(); 42 | } else { 43 | return VENDOR_ID_OTHER; 44 | } 45 | } 46 | 47 | public String getDeviceUniqName() { 48 | StringBuffer sb = new StringBuffer(); 49 | sb.append(device.getManufacturerName()) 50 | .append("_") 51 | .append(device.getProductName()) 52 | .append("_") 53 | .append(device.getSerialNumber()); 54 | return sb.toString(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/interfaces/FileAddedListener.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.interfaces; 2 | 3 | import cn.rainx.ptp.usbcamera.BaselineInitiator; 4 | import cn.rainx.ptp.usbcamera.Container; 5 | 6 | /** 7 | * Created by rainx on 2017/5/7. 8 | */ 9 | 10 | public interface FileAddedListener { 11 | /** 12 | * 当相机有新的文件被添加的时候触发 13 | * @param bi BaselineInitiator的子类 14 | * @param fileHandle 文件句柄 15 | * @param data 对应的Event(尼康)或者Response (佳能)数据 16 | */ 17 | void onFileAdded(BaselineInitiator bi, int fileHandle, Object data); 18 | } 19 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/interfaces/FileDownloadedListener.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.interfaces; 2 | 3 | import java.io.File; 4 | 5 | import cn.rainx.ptp.usbcamera.BaselineInitiator; 6 | 7 | /** 8 | * Created by rainx on 2017/5/7. 9 | */ 10 | 11 | public interface FileDownloadedListener { 12 | /** 13 | * 14 | * @param bi 15 | * @param fileHandle 相机的file handle 16 | * @param localFile 下载到本地额文件 17 | * @param timeduring 所花的时间 18 | */ 19 | void onFileDownloaded(BaselineInitiator bi, int fileHandle, File localFile, long timeduring); 20 | } 21 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/interfaces/FileTransferListener.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.interfaces; 2 | 3 | import cn.rainx.ptp.usbcamera.BaselineInitiator; 4 | import cn.rainx.ptp.usbcamera.Container; 5 | 6 | /** 7 | * Created by rainx on 2017/5/7. 8 | */ 9 | 10 | public interface FileTransferListener { 11 | /** 12 | * 13 | * @param bi 14 | * @param fileHandle 15 | * @param totalByteLength 文件总长度 16 | * @param transterByteLength 文件已传输长度 17 | */ 18 | void onFileTranster(BaselineInitiator bi, int fileHandle, int totalByteLength, int transterByteLength); 19 | } 20 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/params/SyncParams.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.params; 2 | 3 | /** 4 | * Created by rainx on 2017/5/27. 5 | */ 6 | 7 | public interface SyncParams { 8 | int SYNC_TRIGGER_MODE_EVENT = 0; 9 | int SYNC_TRIGGER_MODE_POLL_LIST = 1; 10 | 11 | int SYNC_MODE_SYNC_ALL = 0; 12 | int SYNC_MODE_SYNC_NEW_ADDED = 1; 13 | 14 | int SYNC_RECORD_MODE_REMEMBER = 0; // will save 15 | int SYNC_RECORD_MODE_FORGET = 1; 16 | 17 | 18 | int FILE_NAME_RULE_HANDLE_ID = 0; 19 | int FILE_NAME_RULE_OBJECT_NAME = 1; 20 | } 21 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/Container.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | import java.io.PrintStream; 21 | 22 | 23 | /** 24 | * PTP Command, Data, Response, and Event blocks use a "Generic Container 25 | * Structure" as a packet header. 26 | * 27 | *

Note that since the string values to which various codes map 28 | * have been interned, you may safely rely on "==" and "!=" when you 29 | * make comparisons against constant values. 30 | * 31 | * @version $Id: Container.java,v 1.8 2001/04/12 23:13:00 dbrownell Exp $ 32 | * @author David Brownell 33 | */ 34 | abstract public class Container extends Buffer 35 | { 36 | public static final int BLOCK_TYPE_COMMAND = 1; 37 | public static final int BLOCK_TYPE_DATA = 2; 38 | public static final int BLOCK_TYPE_RESPONSE = 3; 39 | public static final int BLOCK_TYPE_EVENT = 4; 40 | 41 | protected NameFactory factory; 42 | 43 | // get/put object handles (not 0, ~0) using session context 44 | // Session session; 45 | 46 | // fixed header layout, per annex D 47 | // NOTE: session id (9.3.2) is implicit: no multisession over USB 48 | // @ 0, u32 length 49 | // @ 4, u16 buffer type 50 | // @ 6, u16 code 51 | // @ 8, u32 xid 52 | // TOTAL: 12 bytes 53 | static final int HDR_LEN = 12; 54 | 55 | public Container (byte buf [], NameFactory f) 56 | { super (buf, buf.length); factory = f; } 57 | 58 | public Container (byte buf [], int len, NameFactory f) 59 | { super (buf, len); factory = f; } 60 | 61 | // package private 62 | public void putHeader (int len, int type, int code, int xid) 63 | { 64 | if (offset != 0) 65 | throw new IllegalStateException(); 66 | put32 (len); 67 | put16 (type); 68 | put16 (code); 69 | put32 (xid); 70 | } 71 | 72 | 73 | /** 74 | * Provides a printable representation of data from the block 75 | * header, including block type, code, and transaction ID. 76 | */ 77 | public String toString () 78 | { 79 | StringBuffer temp = new StringBuffer(); 80 | String type = getBlockTypeName (getBlockType ()); 81 | int code = getCode (); 82 | 83 | temp.append ("{ "); 84 | temp.append (type); 85 | temp.append ("; len "); 86 | temp.append (Integer.toString (getLength ())); 87 | temp.append ("; "); 88 | temp.append (getCodeName (code)); 89 | temp.append ("; xid "); 90 | temp.append (Integer.toString (getXID ())); 91 | 92 | // inelegant, but ... 93 | if (this instanceof ParamVector) { 94 | ParamVector vec = (ParamVector) this; 95 | int nparams = vec.getNumParams (); 96 | 97 | if (nparams > 0) { 98 | temp.append ("; "); 99 | for (int i = 0; i < nparams; i++) { 100 | if (i != 0) 101 | temp.append (" "); 102 | temp.append ("0x"); 103 | temp.append (Integer.toHexString (vec.getParam (i))); 104 | } 105 | } 106 | } 107 | 108 | temp.append (" }"); 109 | return temp.toString (); 110 | } 111 | 112 | void dump (PrintStream out) 113 | { 114 | out.println (toString ()); 115 | } 116 | 117 | 118 | protected void parse() 119 | { 120 | offset = HDR_LEN; 121 | } 122 | 123 | /** Returns the overall length of this data block, including header. */ 124 | public int getLength () 125 | { return /* unsigned */ getS32 (0); } 126 | 127 | /** 128 | * Returns the overall type of this data block as a coded integer. 129 | */ 130 | public final int getBlockType () 131 | { return getU16 (4); } 132 | 133 | /** 134 | * Returns an interned string, normally "command", "data", "response", 135 | * or "event", corresponding to the coded type. Unrecognized or 136 | * undefined values are returned as interned Integer.toHexString values. 137 | */ 138 | public static final String getBlockTypeName (int type) 139 | { 140 | switch (type) { 141 | case 1: return "command"; 142 | case 2: return "data"; 143 | case 3: return "response"; 144 | case 4: return "event"; 145 | default: return Integer.toHexString (type).intern (); 146 | } 147 | } 148 | 149 | /** 150 | * Returns the operation, response, or event code of this block. 151 | */ 152 | public final int getCode () 153 | { 154 | return getU16 (6); 155 | } 156 | 157 | /** 158 | * Returns an interned string identifying the type of code field, 159 | * such as "OperationCode", "ResponseCode", "ObjectFormatCode", 160 | * "EventCode", or "DevicePropsCode". Unrecognized or undefined 161 | * values are returned as interned Integer.toHexString values. 162 | */ 163 | public static final String getCodeType (int code) 164 | { 165 | switch (code >> 12) { 166 | case 1: return "OperationCode"; 167 | case 2: return "ResponseCode"; 168 | case 3: return "ObjectFormatCode"; 169 | case 4: return "EventCode"; 170 | case 5: return "DevicePropCode"; 171 | case 8 + 1: return "Vendor-OpCode"; 172 | case 8 + 2: return "Vendor-ResponseCode"; 173 | case 8 + 3: return "Vendor-FormatCode"; 174 | case 8 + 4: return "Vendor-EventCode"; 175 | case 8 + 5: return "Vendor-PropCode"; 176 | default: return Integer.toHexString (code >> 12).intern (); 177 | } 178 | } 179 | 180 | /** 181 | * Subclasses override this to map PTP codes to their names; the 182 | * results are always interned strings, so that they can be efficiently 183 | * compared ("=", "!=") against constants. Such per-instance methods 184 | * permit type-specific subclasses (and vendor extensions) to name their 185 | * code values, invoking superclass methods to name all other codes. 186 | */ 187 | public String getCodeName (int code) 188 | { 189 | return getCodeString (code); 190 | } 191 | 192 | /** 193 | * Returns an interned string with name of this container's code. 194 | */ 195 | public final String getCodeString () 196 | { 197 | return getCodeName (getCode ()).intern (); 198 | } 199 | 200 | /** 201 | * Returns an interned string with the hexadecimal value of 202 | * the specified container code. 203 | */ 204 | public static String getCodeString (int code) 205 | { 206 | return Integer.toHexString (code).intern (); 207 | } 208 | 209 | /** 210 | * Returns the ID of the transaction this block is associated with. 211 | */ 212 | public final int getXID () 213 | { return getS32 (8); } 214 | } 215 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/Data.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | 21 | /** 22 | * The optional middle phase of a PTP transaction involves sending 23 | * data to or from the responder. 24 | * 25 | * @version $Id: Data.java,v 1.4 2001/04/12 23:13:00 dbrownell Exp $ 26 | * @author David Brownell 27 | */ 28 | public class Data extends Container 29 | { 30 | private boolean in; 31 | 32 | public Data (NameFactory f) { this (true, null, 0, f); } 33 | 34 | public Data (boolean isIn, byte buf [], NameFactory f) 35 | { super (buf, f); in = isIn; } 36 | 37 | public Data (boolean isIn, byte buf [], int len, NameFactory f) 38 | { super (buf, len, f); in = isIn; } 39 | 40 | boolean isIn () 41 | { return in; } 42 | 43 | public String getCodeName (int code) 44 | { 45 | return factory.getOpcodeString (code); 46 | } 47 | 48 | public String toString () 49 | { 50 | StringBuffer temp = new StringBuffer(); 51 | int code = getCode (); 52 | 53 | temp.append ("{ "); 54 | temp.append (getBlockTypeName (getBlockType ())); 55 | if (in) 56 | temp.append (" IN"); 57 | else 58 | temp.append (" OUT"); 59 | temp.append ("; len "); 60 | temp.append (Integer.toString (getLength ())); 61 | temp.append ("; "); 62 | temp.append (factory.getOpcodeString (code)); 63 | temp.append ("; xid "); 64 | temp.append (Integer.toString (getXID ())); 65 | temp.append ("}"); 66 | return temp.toString (); 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/DeviceInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | import java.io.PrintStream; 21 | 22 | 23 | /** 24 | * DeviceInfo describes device functionality such supported image formats, 25 | * operations, events, and device properties. 26 | * 27 | * @version $Id: DeviceInfo.java,v 1.8 2001/04/12 23:13:00 dbrownell Exp $ 28 | * @author David Brownell 29 | */ 30 | public class DeviceInfo extends Data 31 | { 32 | // need some transport-neutral interface; this is USB-specific 33 | 34 | int standardVersion; 35 | int vendorExtensionId; 36 | int vendorExtensionVersion; 37 | String vendorExtensionDesc; 38 | 39 | int functionalMode; // may change; 40 | int operationsSupported []; // 10.2 41 | int eventsSupported []; // 12.5 42 | int propertiesSupported []; // 13.3.5 43 | 44 | int captureFormats []; // 6 45 | int imageFormats []; // 6 46 | String manufacturer; 47 | String model; 48 | 49 | String deviceVersion; 50 | String serialNumber; 51 | 52 | // FIXME add formal vendor hooks, which we'd consult for string 53 | // mappings ... we don't have any here. 54 | 55 | // Command, Response, ObjectInfo, Event, and DevicePropDesc can 56 | // all be subclassed; but we won't have instances here. And 57 | // there's also the vendor extension stuff here. 58 | 59 | 60 | // input -- we can't know buffer size yet 61 | DeviceInfo (NameFactory f) 62 | { super (true, null, 0, f); } 63 | 64 | 65 | private boolean supports (int supported [], int code) 66 | { 67 | for (int i = 0; i < supported.length; i++) { 68 | if (code == supported [i]) 69 | return true; 70 | } 71 | return false; 72 | } 73 | 74 | 75 | /** Returns true iff the device supports this operation */ 76 | public boolean supportsOperation (int opCode) 77 | { 78 | return supports (operationsSupported, opCode); 79 | } 80 | 81 | /** Returns true iff the device supports this event */ 82 | public boolean supportsEvent (int eventCode) 83 | { 84 | return supports (eventsSupported, eventCode); 85 | } 86 | 87 | /** Returns true iff the device supports this property */ 88 | public boolean supportsProperty (int propCode) 89 | { 90 | return supports (propertiesSupported, propCode); 91 | } 92 | 93 | /** Returns true iff the device supports this capture format */ 94 | public boolean supportsCaptureFormat (int formatCode) 95 | { 96 | return supports (captureFormats, formatCode); 97 | } 98 | 99 | /** Returns true iff the device supports this image format */ 100 | public boolean supportsImageFormat (int formatCode) 101 | { 102 | return supports (imageFormats, formatCode); 103 | } 104 | 105 | 106 | // fit names to standard length lines 107 | private int addString (PrintStream out, int last, String s) 108 | { 109 | last += s.length (); 110 | last++; 111 | if (last < 80) { 112 | out.print (s); 113 | out.print (" "); 114 | } else { 115 | out.println (); 116 | out.print ("\t"); 117 | out.print (s); 118 | out.print (" "); 119 | last = 8 + s.length () + 1; 120 | } 121 | return last; 122 | } 123 | 124 | protected void parse() 125 | { 126 | super.parse (); 127 | 128 | standardVersion = nextU16 (); 129 | vendorExtensionId = /* unsigned */ nextS32 (); 130 | vendorExtensionVersion = nextU16 (); 131 | vendorExtensionDesc = nextString (); 132 | 133 | functionalMode = nextU16 (); 134 | operationsSupported = nextU16Array (); 135 | eventsSupported = nextU16Array (); 136 | propertiesSupported = nextU16Array (); 137 | 138 | captureFormats = nextU16Array (); 139 | imageFormats = nextU16Array (); 140 | manufacturer = nextString (); 141 | model = nextString (); 142 | 143 | deviceVersion = nextString (); 144 | serialNumber = nextString (); 145 | } 146 | 147 | void lines (PrintStream out) 148 | { 149 | if (manufacturer != null) 150 | out.println ("Manufacturer: " + manufacturer); 151 | if (model != null) 152 | out.println ("Model: " + model); 153 | if (deviceVersion != null) 154 | out.println ("Device Version: " + deviceVersion); 155 | if (serialNumber != null) 156 | out.println ("Serial Number: " + serialNumber); 157 | 158 | if (functionalMode != 0) { 159 | out.print ("Functional Mode: "); 160 | out.println (funcMode (functionalMode)); 161 | } 162 | 163 | if (vendorExtensionId != 0) { 164 | out.print ("Extensions ("); 165 | out.print (Integer.toString (vendorExtensionId)); 166 | out.print (")"); 167 | if (vendorExtensionDesc != null) { 168 | out.print (": "); 169 | out.print (vendorExtensionDesc); 170 | } 171 | out.println (); 172 | 173 | // summarize extension: ops, props, events 174 | } 175 | } 176 | 177 | public String toString () 178 | { 179 | 180 | if (operationsSupported == null) { 181 | // System.err.println ("... device info uninitted"); 182 | return "... device info uninitted"; 183 | } 184 | String result = "DeviceInfo:\n"; 185 | result += ("PTP Version: " 186 | + (standardVersion / 100) 187 | + "." 188 | + (standardVersion % 100)); 189 | 190 | // per chapter 10 191 | result += ("\n\nOperations Supported:"); 192 | for (int i = 0; i < operationsSupported.length; i++) { 193 | result += "\n\t" +factory.getOpcodeString (operationsSupported [i]); 194 | } 195 | 196 | // per chapter 11 197 | result += ("\n\nEvents Supported:"); 198 | for (int i = 0; i < eventsSupported.length; i++) { 199 | result += "\n\t" +factory.getEventString (eventsSupported [i]); 200 | } 201 | 202 | // per chapter 13 203 | result += ("\n\nDevice Properties Supported:\n"); 204 | for (int i = 0; i < propertiesSupported.length; i++) { 205 | result += "\t" +factory.getPropertyName (propertiesSupported [i]); 206 | } 207 | 208 | // per 6.2 209 | result += ("\n\nCapture Formats Supported:\n"); 210 | for (int i = 0; i < captureFormats.length; i++) { 211 | result += "\t" +factory.getFormatString (captureFormats [i]); 212 | } 213 | 214 | // per 6.2 215 | result += ("\n\nImage Formats Supported:\n"); 216 | for (int i = 0; i < imageFormats.length; i++) { 217 | result += "\t" + factory.getFormatString (imageFormats [i]); 218 | } 219 | 220 | if (vendorExtensionId != 0) { 221 | result += ("\n\nVendor Extension, id "); 222 | result += (Integer.toString (vendorExtensionId)); 223 | result += (", version "); 224 | result += (standardVersion / 100); 225 | result += ("."); 226 | result += (standardVersion % 100); 227 | 228 | if (vendorExtensionDesc != null) { 229 | result += "\nDescription: " +vendorExtensionDesc; 230 | } 231 | } 232 | return result; 233 | } 234 | 235 | static String funcMode (int functionalMode) 236 | { 237 | switch (functionalMode) { 238 | case 0: 239 | return "standard"; 240 | case 1: 241 | return "sleeping"; 242 | default: 243 | // FIXME add vendor hook 244 | StringBuffer buf = new StringBuffer(); 245 | 246 | if ((functionalMode & 0x8000) == 0) 247 | buf.append ("reserved 0x"); 248 | else 249 | buf.append ("vendor 0x"); 250 | buf.append (Integer.toHexString (functionalMode & ~0x8000)); 251 | return buf.toString (); 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/DevicePropValue.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | import android.widget.TextView; 21 | 22 | import java.io.PrintStream; 23 | 24 | 25 | /** 26 | * DeviceProperty values wrap various types of integers and 27 | * arrays of integers, and strings. It's sort of like a 28 | * CORBA "Any", pairing a typecode and value, except that 29 | * the typecode is known in advance so it's never marshaled. 30 | * 31 | * @see DevicePropDesc 32 | * 33 | * @version $Id: DevicePropValue.java,v 1.4 2001/04/12 23:13:00 dbrownell Exp $ 34 | * @author David Brownell 35 | */ 36 | public class DevicePropValue extends Data 37 | { 38 | int typecode; 39 | Object value; 40 | 41 | DevicePropValue (int tc, NameFactory f) 42 | { super (f); typecode = tc; } 43 | 44 | private DevicePropValue (int tc, Object obj, NameFactory f) 45 | { 46 | super (f); 47 | typecode = tc; 48 | value = obj; 49 | 50 | // FIXME: marshal value into the buffer. 51 | } 52 | 53 | public Object getValue () 54 | { return value; } 55 | 56 | public int getTypeCode () 57 | { return typecode; } 58 | 59 | protected void parse() 60 | { 61 | value = get (typecode, this); 62 | } 63 | 64 | void dump (PrintStream out) 65 | { 66 | out.print ("Type: "); 67 | out.print (getTypeName (typecode)); 68 | out.print (", Value: "); 69 | out.println (value.toString ()); 70 | } 71 | 72 | void showInTextView (TextView tv) 73 | { 74 | tv.setText ("Type: "); 75 | tv.append (getTypeName (typecode)); 76 | tv.append (", Value: "); 77 | tv.append ("\n"); 78 | tv.append (value.toString ()); 79 | } 80 | 81 | public String getCodeName (int code) 82 | { 83 | return getTypeName (code); 84 | } 85 | 86 | public static Object get (int code, Buffer buf) 87 | { 88 | Object value; 89 | 90 | switch (code) { 91 | case s8: 92 | return new Integer(buf.nextS8 ()); 93 | case u8: 94 | return new Integer(buf.nextU8 ()); 95 | case s16: 96 | return new Integer(buf.nextS16 ()); 97 | case u16: 98 | return new Integer(buf.nextU16 ()); 99 | case s32: 100 | return new Integer(buf.nextS32 ()); 101 | case u32: 102 | return new Long(0x0ffFFffFFL & buf.nextS32 ()); 103 | case s64: 104 | return new Long(buf.nextS64 ()); 105 | case u64: 106 | // FIXME: unsigned masquerading as signed ... 107 | return new Long(buf.nextS64 ()); 108 | 109 | // case s128: case u128: 110 | 111 | case s8array: 112 | return buf.nextS8Array (); 113 | case u8array: 114 | return buf.nextU8Array (); 115 | case s16array: 116 | return buf.nextS16Array (); 117 | case u16array: 118 | return buf.nextU16Array (); 119 | case u32array: 120 | // FIXME: unsigned masquerading as signed ... 121 | case s32array: 122 | return buf.nextS32Array (); 123 | case u64array: 124 | // FIXME: unsigned masquerading as signed ... 125 | case s64array: 126 | return buf.nextS64Array (); 127 | // case s128array: case u128array: 128 | 129 | case string: 130 | return buf.nextString (); 131 | } 132 | throw new IllegalArgumentException(); 133 | } 134 | 135 | // code values, per 5.3 table 3 136 | 137 | public static final int s8 = 0x0001; 138 | /** Unsigned eight bit integer */ 139 | public static final int u8 = 0x0002; 140 | public static final int s16 = 0x0003; 141 | /** Unsigned sixteen bit integer */ 142 | public static final int u16 = 0x0004; 143 | public static final int s32 = 0x0005; 144 | /** Unsigned thirty two bit integer */ 145 | public static final int u32 = 0x0006; 146 | public static final int s64 = 0x0007; 147 | /** Unsigned sixty four bit integer */ 148 | public static final int u64 = 0x0008; 149 | public static final int s128 = 0x0009; 150 | /** Unsigned one hundred twenty eight bit integer */ 151 | public static final int u128 = 0x000a; 152 | public static final int s8array = 0x4001; 153 | /** Array of unsigned eight bit integers */ 154 | public static final int u8array = 0x4002; 155 | public static final int s16array = 0x4003; 156 | /** Array of unsigned sixteen bit integers */ 157 | public static final int u16array = 0x4004; 158 | public static final int s32array = 0x4005; 159 | /** Array of unsigned thirty two bit integers */ 160 | public static final int u32array = 0x4006; 161 | public static final int s64array = 0x4007; 162 | /** Array of unsigned sixty four bit integers */ 163 | public static final int u64array = 0x4008; 164 | public static final int s128array = 0x4009; 165 | /** Array of unsigned one hundred twenty eight bit integers */ 166 | public static final int u128array = 0x400a; 167 | /** Unicode string */ 168 | public static final int string = 0xffff; 169 | 170 | /** 171 | * Maps datatype codes to string names. 172 | * @param code datatype code 173 | * @return interned string identifying that datatype. 174 | */ 175 | public static String getTypeName (int code) 176 | { 177 | switch (code) { 178 | case s8: return "s8"; 179 | case u8: return "u8"; 180 | case s16: return "s16"; 181 | case u16: return "u16"; 182 | case s32: return "s32"; 183 | case u32: return "u32"; 184 | case s64: return "s64"; 185 | case u64: return "u64"; 186 | case s128: return "s128"; 187 | case u128: return "u128"; 188 | case s8array: return "s8array"; 189 | case u8array: return "u8array"; 190 | case s16array: return "s16array"; 191 | case u16array: return "u16array"; 192 | case s32array: return "s32array"; 193 | case u32array: return "u32array"; 194 | case s64array: return "s64array"; 195 | case u64array: return "u64array"; 196 | case s128array: return "s128array"; 197 | case u128array: return "u128array"; 198 | case string: return "string"; 199 | } 200 | return Container.getCodeString (code); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/Event.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // Copyright 2010 by Stefano Fornari 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License 15 | // along with this program; if not, write to the Free Software 16 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 | // 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | /** 21 | * Events are sent spontaneously from responders to initiators. 22 | * Event codes, described in chapter 12 of the PTP specification, 23 | * identify what happened. Some events have a parameter, and 24 | * additional data (for vendor extensions) may be available 25 | * using the class control request "Get Extended Event Data". 26 | * 27 | * @author David Brownell 28 | * @author Stefano Fonari 29 | */ 30 | public class Event extends ParamVector { 31 | 32 | public Event(byte buf[], NameFactory f) { 33 | super(buf, buf.length, f); 34 | } 35 | /** EventCode: */ 36 | public static final int Undefined = 0x4000; 37 | /** EventCode: */ 38 | public static final int CancelTransaction = 0x4001; 39 | /** EventCode: */ 40 | public static final int ObjectAdded = 0x4002; 41 | /** EventCode: */ 42 | public static final int ObjectRemoved = 0x4003; 43 | /** EventCode: */ 44 | public static final int StoreAdded = 0x4004; 45 | /** EventCode: */ 46 | public static final int StoreRemoved = 0x4005; 47 | /** EventCode: */ 48 | public static final int DevicePropChanged = 0x4006; 49 | /** EventCode: */ 50 | public static final int ObjectInfoChanged = 0x4007; 51 | /** EventCode: */ 52 | public static final int DeviceInfoChanged = 0x4008; 53 | /** EventCode: */ 54 | public static final int RequestObjectTransfer = 0x4009; 55 | /** EventCode: */ 56 | public static final int StoreFull = 0x400a; 57 | /** EventCode: */ 58 | public static final int DeviceReset = 0x400b; 59 | /** EventCode: */ 60 | public static final int StorageInfoChanged = 0x400c; 61 | /** EventCode: */ 62 | public static final int CaptureComplete = 0x400d; 63 | /** EventCode: a status event was dropped (missed an interrupt) */ 64 | public static final int UnreportedStatus = 0x400e; 65 | 66 | public String getCodeName(int code) { 67 | //return factory.getEventString(code); 68 | return _getEventString(code); 69 | } 70 | 71 | static public String _getEventString(int code) { 72 | switch (code) { 73 | case Undefined: 74 | return "Undefined"; 75 | case CancelTransaction: 76 | return "CancelTransaction"; 77 | case ObjectAdded: 78 | return "ObjectAdded"; 79 | case ObjectRemoved: 80 | return "ObjectRemoved"; 81 | 82 | case StoreAdded: 83 | return "StoreAdded"; 84 | case StoreRemoved: 85 | return "StoreRemoved"; 86 | case DevicePropChanged: 87 | return "DevicePropChanged"; 88 | case ObjectInfoChanged: 89 | return "ObjectInfoChanged"; 90 | 91 | case DeviceInfoChanged: 92 | return "DeviceInfoChanged"; 93 | case RequestObjectTransfer: 94 | return "RequestObjectTransfer"; 95 | case StoreFull: 96 | return "StoreFull"; 97 | case DeviceReset: 98 | return "DeviceReset"; 99 | 100 | case StorageInfoChanged: 101 | return "StorageInfoChanged"; 102 | case CaptureComplete: 103 | return "CaptureComplete"; 104 | case UnreportedStatus: 105 | return "UnreportedStatus"; 106 | 107 | 108 | } 109 | return getCodeString(code); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/FileSendData.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.net.URLConnection; 23 | 24 | 25 | /** 26 | * Used with {@link BaselineInitiator#sendObject sendObject}, this can read 27 | * objects from files using a relatively small amount of in-memory buffering. 28 | * That reduces system resource requirements for working with large files 29 | * such as uncompressed TIF images supported by higher end imaging devices. 30 | * 31 | * @see BaselineInitiator#sendObject 32 | * 33 | * @version $Id: FileSendData.java,v 1.3 2001/04/12 23:13:00 dbrownell Exp $ 34 | * @author David Brownell 35 | */ 36 | public class FileSendData extends Data 37 | { 38 | private InputStream in; 39 | private int filesize; 40 | 41 | // FIXME: shouldn't be "File" streams 42 | 43 | /** 44 | * Constructs a data object which fills the given underlying file. 45 | */ 46 | public FileSendData (URLConnection data, NameFactory f) 47 | throws IOException 48 | { 49 | super (false, new byte [128 * 1024], f); 50 | in = data.getInputStream (); 51 | filesize = data.getContentLength (); 52 | } 53 | 54 | public int getLength () 55 | { 56 | return HDR_LEN + filesize; 57 | } 58 | 59 | /** 60 | * Reads object data from the underlying file. 61 | */ 62 | public int read (byte buf [], int off, int len) 63 | throws IOException 64 | { 65 | return in.read (buf, off, len); 66 | } 67 | 68 | /** 69 | * Closes the underlying file. 70 | */ 71 | public void close () 72 | throws IOException 73 | { 74 | in.close (); 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/InitiatorFactory.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.usbcamera; 2 | 3 | import android.hardware.usb.UsbDevice; 4 | import android.hardware.usb.UsbManager; 5 | import android.util.Log; 6 | 7 | import cn.rainx.ptp.detect.CameraDetector; 8 | import cn.rainx.ptp.usbcamera.eos.EosInitiator; 9 | import cn.rainx.ptp.usbcamera.nikon.NikonInitiator; 10 | import cn.rainx.ptp.usbcamera.sony.SonyInitiator; 11 | 12 | /** 13 | * Created by rainx on 2017/5/27. 14 | */ 15 | 16 | public class InitiatorFactory { 17 | public static final String TAG = InitiatorFactory.class.getName(); 18 | 19 | static public BaselineInitiator produceInitiator(UsbDevice device, UsbManager usbManager) throws PTPException { 20 | BaselineInitiator bi; 21 | CameraDetector cd = new CameraDetector(device); 22 | if (cd.getSupportedVendorId() == CameraDetector.VENDOR_ID_CANON) { 23 | Log.d(TAG, "Device is CANON, open EOSInitiator"); 24 | bi = new EosInitiator(device, usbManager.openDevice(device)); 25 | } else if (cd.getSupportedVendorId() == CameraDetector.VENDOR_ID_NIKON) { 26 | Log.d(TAG, "Device is Nikon, open NikonInitiator"); 27 | bi = new NikonInitiator(device, usbManager.openDevice(device)); 28 | } else if (cd.getSupportedVendorId() == CameraDetector.VENDOR_ID_SONY) { 29 | Log.d(TAG, "Device is Sony, open SonyInitiator"); 30 | bi = new SonyInitiator(device, usbManager.openDevice(device)); 31 | } else /* if (cd.getSupportedVendorId() == CameraDetector.VENDOR_ID_OTHER) */ { 32 | Log.d(TAG, "Unkown device, open BaselineInitiator"); 33 | bi = new BaselineInitiator (device, usbManager.openDevice(device)); 34 | } 35 | 36 | return bi; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/KodakExtension.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | /** 21 | * This class packages the vendor extension published by 22 | * Eastman Kodak Corporation, which has promised to openly 23 | * publish the complete specification needed to use these. 24 | * 25 | * @version $Id: KodakExtension.java,v 1.1 2001/04/12 23:13:00 dbrownell Exp $ 26 | */ 27 | class KodakExtension extends NameFactory 28 | { 29 | // FIXME: this should eventually host APIs for the 30 | // specialized commands, so it'll need to subclass 31 | // at least BaselineInitiator (so it can do I/O). 32 | 33 | KodakExtension () { } 34 | 35 | 36 | /*-------------------------------------------------------------*/ 37 | 38 | /** 39 | * This is like SendObjectInfo. However, things like 40 | * the association ("MUSIC") need to be filled in; 41 | * there are no optional parameters. 42 | */ 43 | public static final int SendFileObjectInfo = 0x9005; 44 | 45 | /** 46 | * This is SendObject, except that it was preceded 47 | * by SendFileObjectInfo 48 | */ 49 | public static final int SendFileObject = 0x9006; 50 | 51 | 52 | public String getOpcodeString (int code) 53 | { 54 | switch (code) { 55 | case SendFileObjectInfo: 56 | return "Kodak_SendFileObjectInfo"; 57 | case SendFileObject: 58 | return "Kodak_SendFileObject"; 59 | } 60 | return Command._getOpcodeString (code); 61 | } 62 | 63 | /*-------------------------------------------------------------*/ 64 | 65 | /** ResponseCode: */ 66 | public static final int FilenameRequired = 0xa001; 67 | 68 | /** ResponseCode: */ 69 | public static final int FilenameConflicts = 0xa002; 70 | 71 | /** ResponseCode: */ 72 | public static final int FilenameInvalid = 0xa003; 73 | 74 | public String getResponseString (int code) 75 | { 76 | switch (code) { 77 | case FilenameRequired: 78 | return "Kodak_FilenameRequired"; 79 | case FilenameConflicts: 80 | return "Kodak_FilenameConflicts"; 81 | case FilenameInvalid: 82 | return "Kodak_FilenameInvalid"; 83 | } 84 | return Response._getResponseString (code); 85 | } 86 | 87 | /*-------------------------------------------------------------*/ 88 | 89 | /** ObjectFormatCode: ".fw" file for device firmware. */ 90 | public static final int Firmware = 0xb001; 91 | 92 | /** ObjectFormatCode: ".m3u" style MP3 playlist. */ 93 | public static final int M3U = 0xb002; 94 | 95 | 96 | public String getFormatString (int code) 97 | { 98 | switch (code) { 99 | case Firmware: 100 | return "Kodak_Firmware"; 101 | case M3U: 102 | return "Kodak_M3U"; 103 | } 104 | return ObjectInfo._getFormatString (code); 105 | } 106 | 107 | /*-------------------------------------------------------------*/ 108 | 109 | /** Property code: */ 110 | public static final int prop1 = 0xd001; 111 | 112 | /** Property code: */ 113 | public static final int prop2 = 0xd002; 114 | 115 | /** Property code: */ 116 | public static final int prop3 = 0xd003; 117 | 118 | /** Property code: */ 119 | public static final int prop4 = 0xd004; 120 | 121 | /** Property code: */ 122 | public static final int prop5 = 0xd005; 123 | 124 | /** Property code: */ 125 | public static final int prop6 = 0xd006; 126 | 127 | public String getPropertyName (int code) 128 | { 129 | switch (code) { 130 | case prop1: 131 | return "Kodak_prop1"; 132 | case prop2: 133 | return "Kodak_prop2"; 134 | case prop3: 135 | return "Kodak_prop3"; 136 | case prop4: 137 | return "Kodak_prop4"; 138 | case prop5: 139 | return "Kodak_prop5"; 140 | case prop6: 141 | return "Kodak_prop6"; 142 | } 143 | return DevicePropDesc._getPropertyName (code); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/NameFactory.java: -------------------------------------------------------------------------------- 1 | // Copyright 2001 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | /** 21 | * Supports use of objects using vendor extension codes. 22 | * The base class produces names only for standard PTP 23 | * operations, responses, properties, events, and formats. 24 | * 25 | * @version $Id: NameFactory.java,v 1.1 2001/04/12 23:13:00 dbrownell Exp $ 26 | */ 27 | public class NameFactory 28 | { 29 | // package private 30 | protected NameFactory () { } 31 | 32 | // package private 33 | protected NameFactory updateFactory (int vendorExtensionId) 34 | { 35 | switch (vendorExtensionId) { 36 | case 0: return this; 37 | case 1: return new KodakExtension (); 38 | } 39 | if (BaselineInitiator.DEBUG) 40 | System.err.println ("Don't know extension " + vendorExtensionId); 41 | return this; 42 | } 43 | 44 | /* 45 | static String vendorToString (int vendorExtensionId) 46 | { 47 | switch (vendorExtensionId) { 48 | // from PIMA website 49 | case 1: return "Eastman Kodak Company"; 50 | case 2: return "Seiko Epson"; 51 | case 3: return "Agilent Technologies, Inc."; 52 | case 4: return "Polaroid Corporation"; 53 | case 5: return "Agfa-Gevaert"; 54 | case 6: return "Microsoft Corporation"; 55 | default: 56 | return "0x" + Integer.toHexString (vendorExtensionId); 57 | } 58 | } 59 | */ 60 | 61 | /** 62 | * Maps command codes to string names. 63 | * @param code device command code 64 | * @return interned string identifying that command. 65 | */ 66 | // bits 14:12 = 001 67 | public String getOpcodeString (int code) 68 | { return Command._getOpcodeString (code); } 69 | 70 | /** 71 | * Maps response codes to string names. 72 | * @param code response code 73 | * @return interned string identifying that response. 74 | */ 75 | // bits 14:12 = 010 76 | public String getResponseString (int code) 77 | { return Response._getResponseString (code); } 78 | 79 | /** 80 | * Maps object format codes to string names. 81 | * @param code device format code 82 | * @return interned string identifying that format. 83 | */ 84 | // bits 14:12 = 011 85 | public String getFormatString (int code) 86 | { return ObjectInfo._getFormatString (code); } 87 | 88 | /** 89 | * Maps event codes to string names. 90 | * @param code device event code 91 | * @return interned string identifying that event. 92 | */ 93 | // bits 14:12 = 100 94 | public String getEventString (int code) 95 | { return Event._getEventString (code); } 96 | 97 | /** 98 | * Maps property codes to string names. 99 | * @param code device property code 100 | * @return interned string identifying that property. 101 | */ 102 | // bits 14:12 = 101 103 | public String getPropertyName (int code) 104 | { return DevicePropDesc._getPropertyName (code); } 105 | 106 | 107 | // FIXME: hooks for vendor-specific filesystem types. 108 | } 109 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/OutputStreamData.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2010 by Stefano Fornari 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | */ 17 | package cn.rainx.ptp.usbcamera; 18 | 19 | import java.io.IOException; 20 | import java.io.OutputStream; 21 | 22 | /** 23 | * Used with {@link BaselineInitiator#fillObject fillObject}, this writes 24 | * objects to a give output stream. 25 | * 26 | * @see BaselineInitiator#fillObject 27 | * 28 | * @author ste 29 | */ 30 | public class OutputStreamData extends Data { 31 | 32 | private OutputStream out; 33 | 34 | /** 35 | * Constructs a data object which fills the given underlying file. 36 | */ 37 | public OutputStreamData(OutputStream o, NameFactory f) { 38 | super(f); 39 | out = o; 40 | } 41 | 42 | /** 43 | * Writes object data to the underlying output stream 44 | */ 45 | public void write(byte buf[], int off, int len) throws IOException { 46 | out.write(buf, off, len); 47 | } 48 | 49 | /** 50 | * Closes the underlying output stream. 51 | */ 52 | public void close() throws IOException { 53 | out.close(); 54 | } 55 | 56 | @Override 57 | protected final void parse() { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/PTPBusyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * cameracontrol 3 | * Copyright (C) 2010 Stefano Fornari 4 | * 5 | * This program is free software; you can redistribute it and/or modify it under 6 | * the terms of the GNU Affero General Public License version 3 as published by 7 | * the Free Software Foundation with the addition of the following permission 8 | * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED 9 | * WORK IN WHICH THE COPYRIGHT IS OWNED BY Stefano Fornari, Stefano Fornari 10 | * DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 11 | * 12 | * This program is distributed in the hope that it will be useful, but WITHOUT 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 14 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 15 | * details. 16 | * 17 | * You should have received a copy of the GNU Affero General Public License 18 | * along with this program; if not, see http://www.gnu.org/licenses or write to 19 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 20 | * MA 02110-1301 USA. 21 | */ 22 | 23 | package cn.rainx.ptp.usbcamera; 24 | 25 | /** 26 | * 27 | * @author ste 28 | */ 29 | public class PTPBusyException extends PTPException { 30 | 31 | /** 32 | * Creates a new instance of PTPBusyException without detail message. 33 | */ 34 | public PTPBusyException() { 35 | super("Device is busy"); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/PTPException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2010 by Stefano Fornari 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | package cn.rainx.ptp.usbcamera; 18 | 19 | public class PTPException extends Exception { 20 | 21 | private int errorCode; 22 | 23 | public PTPException() { 24 | this(""); 25 | } 26 | 27 | public PTPException(int errorCode) { 28 | this("", null, errorCode); 29 | } 30 | 31 | public PTPException(String string) { 32 | this(string, null); 33 | } 34 | 35 | public PTPException(String string, int errorCode) { 36 | this(string, null, errorCode); 37 | } 38 | 39 | public PTPException(String string, Throwable t) { 40 | this(string, t, Response.Undefined); 41 | } 42 | 43 | public PTPException(String string, Throwable t, int errorCode) { 44 | super(string, t); 45 | this.errorCode = errorCode; 46 | } 47 | 48 | public int getErrorCode() { 49 | return errorCode; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/PTPOpenSessionException.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.usbcamera; 2 | 3 | /** 4 | * Created by rainx on 2017/6/11. 5 | */ 6 | 7 | public class PTPOpenSessionException extends PTPException { 8 | public PTPOpenSessionException(String string, int code) { 9 | super(string, code); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/PTPUnsupportedException.java: -------------------------------------------------------------------------------- 1 | // Copyright 2010 by Stefano Fornari 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | package cn.rainx.ptp.usbcamera; 18 | 19 | public class PTPUnsupportedException extends PTPException { 20 | 21 | public PTPUnsupportedException(String string) { 22 | super(string); 23 | } 24 | 25 | public PTPUnsupportedException(String string, Throwable t) { 26 | super(string, t); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/ParamVector.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | import java.io.PrintStream; 21 | 22 | 23 | /** 24 | * This class is used for PTP messages consisting of only a set of 25 | * thirty-two bit parameters, such as commands, responses, and events. 26 | * 27 | * @version $Id: ParamVector.java,v 1.4 2001/04/12 23:13:00 dbrownell Exp $ 28 | * @author David Brownell 29 | */ 30 | public class ParamVector extends Container 31 | { 32 | ParamVector (byte buf [], NameFactory f) 33 | { super (buf, f); } 34 | 35 | ParamVector (byte buf [], int len, NameFactory f) 36 | { super (buf, len, f); } 37 | 38 | /** Returns the first positional parameter. */ 39 | public final int getParam1 () 40 | { return getS32 (12); } 41 | 42 | /** Returns the second positional parameter. */ 43 | public final int getParam2 () 44 | { return getS32 (16); } 45 | 46 | /** Returns the third positional parameter. */ 47 | public final int getParam3 () 48 | { return getS32 (20); } 49 | 50 | /** Returns the number of parameters in this data block */ 51 | public final int getNumParams () 52 | { return (length - MIN_LEN) / 4; } 53 | 54 | 55 | // no params 56 | static final int MIN_LEN = HDR_LEN; 57 | 58 | // allegedly some responses could have five params 59 | static final int MAX_LEN = 32; 60 | 61 | 62 | // NOTE: params in the spec are numbered from one, not zero 63 | int getParam (int i) 64 | { return getS32 (12 + (4 * i)); } 65 | 66 | void dump (PrintStream out) 67 | { 68 | out.print (this.toString ()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/Response.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | 21 | /** 22 | * The final phase of a PTP transaction sends a command's response from 23 | * the responder to the initiator. These include response codes, and are 24 | * described in chapter 11 of the PTP specification. 25 | * 26 | * @version $Id: Response.java,v 1.2 2001/04/12 23:13:00 dbrownell Exp $ 27 | * @author David Brownell 28 | */ 29 | public class Response extends ParamVector 30 | { 31 | Response (byte buf [], NameFactory f) 32 | { super (buf, f); } 33 | 34 | public Response (byte buf [], int len, NameFactory f) 35 | { super (buf, len, f); } 36 | 37 | 38 | /** ResponseCode: */ 39 | public static final int Undefined = 0x2000; 40 | /** ResponseCode: */ 41 | public static final int OK = 0x2001; 42 | /** ResponseCode: */ 43 | public static final int GeneralError = 0x2002; 44 | /** ResponseCode: */ 45 | public static final int SessionNotOpen = 0x2003; 46 | 47 | /** ResponseCode: */ 48 | public static final int InvalidTransactionID = 0x2004; 49 | /** ResponseCode: */ 50 | public static final int OperationNotSupported = 0x2005; 51 | /** ResponseCode: */ 52 | public static final int ParameterNotSupported = 0x2006; 53 | /** ResponseCode: */ 54 | public static final int IncompleteTransfer = 0x2007; 55 | 56 | /** ResponseCode: */ 57 | public static final int InvalidStorageID = 0x2008; 58 | /** ResponseCode: */ 59 | public static final int InvalidObjectHandle = 0x2009; 60 | /** ResponseCode: */ 61 | public static final int DevicePropNotSupported = 0x200a; 62 | /** ResponseCode: */ 63 | public static final int InvalidObjectFormatCode = 0x200b; 64 | 65 | /** ResponseCode: */ 66 | public static final int StoreFull = 0x200c; 67 | /** ResponseCode: */ 68 | public static final int ObjectWriteProtected = 0x200d; 69 | /** ResponseCode: */ 70 | public static final int StoreReadOnly = 0x200e; 71 | /** ResponseCode: */ 72 | public static final int AccessDenied = 0x200f; 73 | 74 | 75 | /** ResponseCode: */ 76 | public static final int NoThumbnailPresent = 0x2010; 77 | /** ResponseCode: */ 78 | public static final int SelfTestFailed = 0x2011; 79 | /** ResponseCode: */ 80 | public static final int PartialDeletion = 0x2012; 81 | /** ResponseCode: */ 82 | public static final int StoreNotAvailable = 0x2013; 83 | 84 | /** ResponseCode: */ 85 | public static final int SpecificationByFormatUnsupported = 0x2014; 86 | /** ResponseCode: */ 87 | public static final int NoValidObjectInfo = 0x2015; 88 | /** ResponseCode: */ 89 | public static final int InvalidCodeFormat = 0x2016; 90 | /** ResponseCode: */ 91 | public static final int UnknownVendorCode = 0x2017; 92 | 93 | /** ResponseCode: */ 94 | public static final int CaptureAlreadyTerminated = 0x2018; 95 | /** ResponseCode: */ 96 | public static final int DeviceBusy = 0x2019; 97 | /** ResponseCode: */ 98 | public static final int InvalidParentObject = 0x201a; 99 | /** ResponseCode: */ 100 | public static final int InvalidDevicePropFormat = 0x201b; 101 | 102 | /** ResponseCode: */ 103 | public static final int InvalidDevicePropValue = 0x201c; 104 | /** ResponseCode: */ 105 | public static final int InvalidParameter = 0x201d; 106 | /** ResponseCode: */ 107 | public static final int SessionAlreadyOpen = 0x201e; 108 | /** ResponseCode: */ 109 | public static final int TransactionCanceled = 0x201f; 110 | 111 | /** ResponseCode: */ 112 | public static final int SpecificationOfDestinationUnsupported = 0x2020; 113 | 114 | 115 | public String getCodeName (int code) 116 | { 117 | return factory.getResponseString (code); 118 | } 119 | 120 | public static String _getResponseString (int code) 121 | { 122 | switch (code) { 123 | case Undefined: return "Undefined"; 124 | case OK: return "OK"; 125 | case GeneralError: return "GeneralError"; 126 | case SessionNotOpen: return "SessionNotOpen"; 127 | 128 | case InvalidTransactionID: return "InvalidTransactionID"; 129 | case OperationNotSupported: return "OperationNotSupported"; 130 | case ParameterNotSupported: return "ParameterNotSupported"; 131 | case IncompleteTransfer: return "IncompleteTransfer"; 132 | 133 | case InvalidStorageID: return "InvalidStorageID"; 134 | case InvalidObjectHandle: return "InvalidObjectHandle"; 135 | case DevicePropNotSupported: return "DevicePropNotSupported"; 136 | case InvalidObjectFormatCode: return "InvalidObjectFormatCode"; 137 | 138 | case StoreFull: return "StoreFull"; 139 | case ObjectWriteProtected: return "ObjectWriteProtected"; 140 | case StoreReadOnly: return "StoreReadOnly"; 141 | case AccessDenied: return "AccessDenied"; 142 | 143 | case NoThumbnailPresent: return "NoThumbnailPresent"; 144 | case SelfTestFailed: return "SelfTestFailed"; 145 | case PartialDeletion: return "PartialDeletion"; 146 | case StoreNotAvailable: return "StoreNotAvailable"; 147 | 148 | case SpecificationByFormatUnsupported: 149 | return "SpecificationByFormatUnsupported"; 150 | case NoValidObjectInfo: return "NoValidObjectInfo"; 151 | case InvalidCodeFormat: return "InvalidCodeFormat"; 152 | case UnknownVendorCode: return "UnknownVendorCode"; 153 | 154 | case CaptureAlreadyTerminated: return "CaptureAlreadyTerminated"; 155 | case DeviceBusy: return "DeviceBusy"; 156 | case InvalidParentObject: return "InvalidParentObject"; 157 | case InvalidDevicePropFormat: return "InvalidDevicePropFormat"; 158 | 159 | case InvalidDevicePropValue: return "InvalidDevicePropValue"; 160 | case InvalidParameter: return "InvalidParameter"; 161 | case SessionAlreadyOpen: return "SessionAlreadyOpen"; 162 | case TransactionCanceled: return "TransactionCanceled"; 163 | 164 | case SpecificationOfDestinationUnsupported: 165 | return "SpecificationOfDestinationUnsupported"; 166 | } 167 | return ("0x" + Integer.toHexString (code)).intern (); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/Session.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | 21 | /** 22 | * Encapsulates the session between a PTP initiator and responder. 23 | * 24 | * @version $Id: Session.java,v 1.3 2001/04/12 23:13:00 dbrownell Exp $ 25 | * @author David Brownell 26 | */ 27 | public class Session 28 | { 29 | private int sessionId; 30 | private int xid; 31 | private boolean active; 32 | private NameFactory factory; 33 | 34 | public Session () { } 35 | 36 | void setFactory (NameFactory f) { factory = f; } 37 | 38 | NameFactory getFactory () { return factory; } 39 | 40 | int getNextXID () 41 | { return (active ? xid++ : 0); } 42 | 43 | int getNextSessionID () 44 | { 45 | if (!active) 46 | return ++sessionId; 47 | throw new IllegalStateException("already active"); 48 | } 49 | 50 | boolean isActive () 51 | { return active; } 52 | 53 | void open () 54 | { xid = 1; active = true; } 55 | 56 | void close () 57 | { active = false; } 58 | 59 | int getSessionId () 60 | { return sessionId; } 61 | 62 | // track objects and their info by handles; 63 | // hookup to marshaling system and event framework 64 | } 65 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/StorageInfo.java: -------------------------------------------------------------------------------- 1 | // Copyright 2000 by David Brownell 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | // 17 | 18 | package cn.rainx.ptp.usbcamera; 19 | 20 | import android.widget.TextView; 21 | 22 | import java.io.PrintStream; 23 | 24 | /** 25 | * StorageInfo provides information such as the type and capacity of 26 | * storage media, whether it's removable, and more. 27 | * 28 | * @version $Id: StorageInfo.java,v 1.5 2001/04/12 23:13:00 dbrownell Exp $ 29 | * @author David Brownell 30 | */ 31 | public class StorageInfo extends Data 32 | { 33 | int storageType; 34 | int filesystemType; 35 | int accessCapability; 36 | long maxCapacity; 37 | 38 | long freeSpaceInBytes; 39 | int freeSpaceInImages; 40 | String storageDescription; 41 | String volumeLabel; 42 | 43 | StorageInfo (NameFactory f) { super (f); } 44 | 45 | protected void parse() 46 | { 47 | super.parse (); 48 | 49 | storageType = nextU16 (); 50 | filesystemType = nextU16 (); 51 | accessCapability = nextU16 (); 52 | maxCapacity = /* unsigned */ nextS64 (); 53 | freeSpaceInBytes = /* unsigned */ nextS64 (); 54 | freeSpaceInImages = /* unsigned */ nextS32 (); 55 | storageDescription = nextString (); 56 | volumeLabel = nextString (); 57 | } 58 | 59 | void line (PrintStream out) 60 | { 61 | String temp; 62 | 63 | switch (storageType) { 64 | case 0: temp = "undefined"; break; 65 | case 1: temp = "Fixed ROM"; break; 66 | case 2: temp = "Removable ROM"; break; 67 | case 3: temp = "Fixed RAM"; break; 68 | case 4: temp = "Removable RAM"; break; 69 | default: 70 | temp = "Reserved-0x" + Integer.toHexString (storageType); 71 | } 72 | out.println ("Storage Type: " + temp); 73 | } 74 | 75 | void line (TextView tv) 76 | { 77 | String temp; 78 | 79 | switch (storageType) { 80 | case 0: temp = "undefined"; break; 81 | case 1: temp = "Fixed ROM"; break; 82 | case 2: temp = "Removable ROM"; break; 83 | case 3: temp = "Fixed RAM"; break; 84 | case 4: temp = "Removable RAM"; break; 85 | default: 86 | temp = "Reserved-0x" + Integer.toHexString (storageType); 87 | } 88 | tv.append ("Storage Type: " + temp +" \n"); 89 | } 90 | 91 | void dump (PrintStream out) 92 | { 93 | String temp; 94 | 95 | super.dump (out); 96 | out.println ("StorageInfo:"); 97 | line (out); 98 | 99 | switch (filesystemType) { 100 | case 0: temp = "undefined"; break; 101 | case 1: temp = "flat"; break; 102 | case 2: temp = "hierarchical"; break; 103 | case 3: temp = "dcf"; break; 104 | default: 105 | if ((filesystemType & 0x8000) != 0) 106 | temp = "Reserved-0x"; 107 | else 108 | temp = "Vendor-0x"; 109 | temp += Integer.toHexString (filesystemType); 110 | } 111 | out.println ("Filesystem Type: " + temp); 112 | 113 | // access: rw, ro, or ro "with object deletion" 114 | 115 | // CF card sizes are "marketing megabytes", not real ones 116 | if (maxCapacity != ~0) 117 | out.println ("Capacity: " 118 | + maxCapacity + " bytes (" 119 | + ((maxCapacity + 500000)/1000000) + " MB)" 120 | ); 121 | if (freeSpaceInBytes != ~0) 122 | out.println ("Free space: " 123 | + freeSpaceInBytes + " bytes (" 124 | + ((freeSpaceInBytes + 500000)/1000000) + " MB)" 125 | ); 126 | if (freeSpaceInImages != ~0) 127 | out.println ("Free space in Images: " + freeSpaceInImages); 128 | 129 | if (storageDescription != null) 130 | out.println ("Description: " + storageDescription); 131 | if (volumeLabel != null) 132 | out.println ("Volume Label: " + volumeLabel); 133 | } 134 | 135 | 136 | void showInTextView (TextView tv) 137 | { 138 | String temp; 139 | 140 | 141 | tv.setText ("StorageInfo:"); 142 | tv.append ("\n"); 143 | line (tv); 144 | 145 | switch (filesystemType) { 146 | case 0: temp = "undefined"; break; 147 | case 1: temp = "flat"; break; 148 | case 2: temp = "hierarchical"; break; 149 | case 3: temp = "dcf"; break; 150 | default: 151 | if ((filesystemType & 0x8000) != 0) 152 | temp = "Reserved-0x"; 153 | else 154 | temp = "Vendor-0x"; 155 | temp += Integer.toHexString (filesystemType); 156 | } 157 | tv.append ("\n"); 158 | tv.append ("Filesystem Type: " + temp); 159 | 160 | // access: rw, ro, or ro "with object deletion" 161 | 162 | // CF card sizes are "marketing megabytes", not real ones 163 | if (maxCapacity != ~0) 164 | { 165 | tv.append ("\n"); 166 | tv.append ("Capacity: " 167 | + maxCapacity + " bytes (" 168 | + ((maxCapacity + 500000)/1000000) + " MB)" 169 | ); 170 | } 171 | if (freeSpaceInBytes != ~0) 172 | { 173 | tv.append ("\n"); 174 | tv.append ("Free space: " 175 | + freeSpaceInBytes + " bytes (" 176 | + ((freeSpaceInBytes + 500000)/1000000) + " MB)" 177 | ); 178 | } 179 | if (freeSpaceInImages != ~0) 180 | { 181 | tv.append ("\n"); 182 | tv.append ("Free space in Images: " + freeSpaceInImages); 183 | } 184 | if (storageDescription != null) 185 | { 186 | tv.append ("\n"); 187 | tv.append ("Description: " + storageDescription); 188 | } 189 | if (volumeLabel != null) 190 | { 191 | tv.append ("\n"); 192 | tv.append ("Volume Label: " + volumeLabel); 193 | } 194 | } 195 | 196 | 197 | public int getStorageType() { 198 | return storageType; 199 | } 200 | 201 | public int getFilesystemType() { 202 | return filesystemType; 203 | } 204 | 205 | public int getAccessCapability() { 206 | return accessCapability; 207 | } 208 | 209 | public long getMaxCapacity() { 210 | return maxCapacity; 211 | } 212 | 213 | public long getFreeSpaceInBytes() { 214 | return freeSpaceInBytes; 215 | } 216 | 217 | public int getFreeSpaceInImages() { 218 | return freeSpaceInImages; 219 | } 220 | 221 | public String getStorageDescription() { 222 | return storageDescription; 223 | } 224 | 225 | public String getVolumeLabel() { 226 | return volumeLabel; 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/eos/EosEvent.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2010 by Stefano Fornari 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | */ 17 | 18 | package cn.rainx.ptp.usbcamera.eos; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | 24 | /** 25 | * 26 | * @author ste 27 | */ 28 | public class EosEvent implements EosEventConstants { 29 | 30 | /** 31 | * Event code 32 | */ 33 | private int code; 34 | 35 | /** 36 | * Params 37 | */ 38 | private List params; 39 | 40 | /** 41 | * Creates a new parser with the given data 42 | * 43 | * @param data the events data - NOT NULL 44 | */ 45 | public EosEvent() { 46 | params = new ArrayList(); 47 | } 48 | 49 | public void setCode(int code) { 50 | this.code = code; 51 | } 52 | 53 | public int getCode() { 54 | return code; 55 | } 56 | 57 | /** 58 | * @param i the parameter index 59 | * @param param the param to set 60 | */ 61 | public void setParam(int i, Object value) { 62 | if (i<0) { 63 | throw new IllegalArgumentException("param index cannot be < 0"); 64 | } 65 | if (params.size() <= i) { 66 | ArrayList newParams = new ArrayList(i); 67 | newParams.addAll(params); 68 | params = newParams; 69 | for (int j=params.size(); j getParamCount())) { 82 | throw new IllegalArgumentException( 83 | "index " + i + " out of range (0-" + getParamCount() + ")" 84 | ); 85 | } 86 | return params.get(i-1); 87 | } 88 | 89 | public int getIntParam(int i) { 90 | return ((Integer)getParam(i)).intValue(); 91 | } 92 | 93 | public String getStringParam(int i) { 94 | return (String)getParam(i); 95 | } 96 | 97 | /** 98 | * 99 | * @return the number of parameters in this event 100 | */ 101 | public int getParamCount() { 102 | return params.size(); 103 | } 104 | 105 | public static String getEventName (int code){ 106 | switch (code) { 107 | case EosEventRequestGetEvent : return "EosEventRequestGetEvent"; 108 | case EosEventObjectAddedEx : return "EosEventObjectAddedEx"; 109 | case EosEventObjectRemoved : return "EosEventObjectRemoved"; 110 | case EosEventRequestGetObjectInfoEx : return "EosEventRequestGetObjectInfoEx"; 111 | case EosEventStorageStatusChanged : return "EosEventStorageStatusChanged"; 112 | case EosEventStorageInfoChanged : return "EosEventStorageInfoChanged"; 113 | case EosEventRequestObjectTransfer : return "EosEventRequestObjectTransfer"; 114 | case EosEventObjectInfoChangedEx : return "EosEventObjectInfoChangedEx"; 115 | case EosEventObjectContentChanged : return "EosEventObjectContentChanged"; 116 | case EosEventPropValueChanged : return "EosEventPropValueChanged"; 117 | case EosEventAvailListChanged : return "EosEventAvailListChanged"; 118 | case EosEventCameraStatusChanged : return "EosEventCameraStatusChanged"; 119 | case EosEventWillSoonShutdown : return "EosEventWillSoonShutdown"; 120 | case EosEventShutdownTimerUpdated : return "EosEventShutdownTimerUpdated"; 121 | case EosEventRequestCancelTransfer : return "EosEventRequestCancelTransfer"; 122 | case EosEventRequestObjectTransferDT : return "EosEventRequestObjectTransferDT"; 123 | case EosEventRequestCancelTransferDT : return "EosEventRequestCancelTransferDT"; 124 | case EosEventStoreAdded : return "EosEventStoreAdded"; 125 | case EosEventStoreRemoved : return "EosEventStoreRemoved"; 126 | case EosEventBulbExposureTime : return "EosEventBulbExposureTime"; 127 | case EosEventRecordingTime : return "EosEventRecordingTime"; 128 | case EosEventAfResult : return "EosEventRequestObjectTransferTS"; 129 | case EosEventRequestObjectTransferTS : return "EosEventAfResult"; 130 | } 131 | return "0x" + Integer.toHexString(code); 132 | } 133 | 134 | 135 | public String toString() { 136 | return "event name is : " + 137 | getEventName(getCode()) + 138 | "first event param is" + 139 | getIntParam(1); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/eos/EosEventFormat.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2010 by Stefano Fornari 2 | * 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; either version 2 of the License, or 6 | * (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | */ 17 | package cn.rainx.ptp.usbcamera.eos; 18 | 19 | import java.lang.reflect.Field; 20 | 21 | /** 22 | * This class formats an EosEvent to a string 23 | * 24 | * @author stefano fornari 25 | */ 26 | public class EosEventFormat implements EosEventConstants { 27 | public static String format(EosEvent e) { 28 | StringBuilder sb = new StringBuilder(); 29 | 30 | int eventCode = e.getCode(); 31 | 32 | sb.append(getEventName(eventCode)); 33 | sb.append(" [ "); 34 | if (eventCode == EosEventPropValueChanged) { 35 | int propCode = e.getIntParam(1); 36 | sb.append(getPropertyName(propCode)) 37 | .append(": "); 38 | 39 | if ((propCode >= EosPropPictureStyleStandard) && 40 | (propCode <= EosPropPictureStyleUserSet3)) { 41 | sb.append("(Sharpness: ") 42 | .append(e.getIntParam(4)) 43 | .append(", Contrast: ") 44 | .append(e.getIntParam(3)); 45 | if (((Boolean)e.getParam(2)).booleanValue()) { 46 | sb.append(", Filter effect: ") 47 | .append(getFilterEffect(e.getIntParam(5))) 48 | .append(", Toning effect: ") 49 | .append(getToningEffect(e.getIntParam(6))); 50 | } else { 51 | sb.append(", Saturation: ") 52 | .append(e.getIntParam(5)) 53 | .append(", Color tone: ") 54 | .append(e.getIntParam(6)); 55 | } 56 | sb.append(")"); 57 | } else { 58 | if (e.getParamCount()>1) { 59 | sb.append(e.getIntParam(2)); 60 | } 61 | } 62 | } else if (eventCode == EosEventObjectAddedEx) { 63 | sb.append(formatEosEventObjectAddedEx(e)); 64 | } 65 | sb.append(" ]"); 66 | 67 | return sb.toString(); 68 | } 69 | 70 | /** 71 | * Returns the printable name of the given event 72 | * 73 | * @param code event code 74 | * 75 | * @return the printable name of the given event 76 | */ 77 | public static String getEventName(int code) { 78 | Field[] fields = EosEventConstants.class.getDeclaredFields(); 79 | 80 | for (Field f: fields) { 81 | String name = f.getName(); 82 | if (name.startsWith("EosEvent")) { 83 | try { 84 | if (f.getInt(null) == code) { 85 | return name; 86 | } 87 | } catch (Exception e) { 88 | // 89 | // Nothing to do 90 | // 91 | } 92 | } 93 | } 94 | return "Unknown"; 95 | } 96 | 97 | /** 98 | * Returns the printable name of the given property 99 | * 100 | * @param code property code 101 | * 102 | * @return the printable name of the given property 103 | */ 104 | public static String getPropertyName(int code) { 105 | return getCodeName("EosProp", code); 106 | } 107 | 108 | /** 109 | * Returns the printable name of the given image format 110 | * 111 | * @param code image format code 112 | * 113 | * @return the printable name of the given image format 114 | */ 115 | public static String getImageFormatName(int code) { 116 | return getCodeName("ImageFormat", code); 117 | } 118 | 119 | /** 120 | * Returns the filter effect name given the code. Names are:
121 | * 0:None, 1:Yellow, 2:Orange, 3:Red, 4:Green 122 | * 123 | * @param code the filter effect code (0-4) 124 | * 125 | * @return the filter effect name 126 | */ 127 | public static String getFilterEffect(int code) { 128 | if ((code < 0) || (code > 4)) { 129 | throw new IllegalArgumentException("code must be in he range 0-4"); 130 | } 131 | 132 | switch (code) { 133 | case 0: return "None"; 134 | case 1: return "Yellow"; 135 | case 2: return "Orange"; 136 | case 3: return "Red"; 137 | case 4: return "Green"; 138 | } 139 | 140 | // 141 | // We should never get here 142 | // 143 | return "Unknown"; 144 | } 145 | 146 | /** 147 | * Returns the toning effect name given the code. Names are:
148 | * 0:None, 1:Sepia, 2:Blue, 3:Purple, 4:Green 149 | * 150 | * @param code the toning effect code (0-4) 151 | * 152 | * @return the toning effect name 153 | */ 154 | public static String getToningEffect(int code) { 155 | if ((code < 0) || (code > 4)) { 156 | throw new IllegalArgumentException("code must be in he range 0-4"); 157 | } 158 | 159 | switch (code) { 160 | case 0: return "None"; 161 | case 1: return "Sepia"; 162 | case 2: return "Blue"; 163 | case 3: return "Purple"; 164 | case 4: return "Green"; 165 | } 166 | 167 | // 168 | // We should never get here 169 | // 170 | return "Unknown"; 171 | } 172 | 173 | // --------------------------------------------------------- Private methods 174 | 175 | private static String formatEosEventObjectAddedEx(EosEvent event) { 176 | return String.format( 177 | "Filename: %s, Size(bytes): %d, ObjectID: 0x%08X, StorageID: 0x%08X, ParentID: 0x%08X, Format: %s", 178 | event.getStringParam(6), 179 | event.getIntParam(5), 180 | event.getIntParam(1), 181 | event.getIntParam(2), 182 | event.getIntParam(3), 183 | getImageFormatName(event.getIntParam(4)) 184 | ); 185 | } 186 | 187 | /** 188 | * Looks up and returns the name of the code give if there is a constant 189 | * field in EosEventConstants which starts with the given prefix 190 | * 191 | * @param prefix the field name prefix 192 | * @param code image format code 193 | * 194 | * @return the printable name of the given code 195 | */ 196 | private static String getCodeName(String prefix, int code) { 197 | Field[] fields = EosEventConstants.class.getDeclaredFields(); 198 | 199 | for (Field f: fields) { 200 | String name = f.getName(); 201 | if (name.startsWith(prefix)) { 202 | try { 203 | if (f.getInt(null) == code) { 204 | return name.substring(prefix.length()); 205 | } 206 | } catch (Exception e) { 207 | // 208 | // Nothing to do 209 | // 210 | } 211 | } 212 | } 213 | return "Unknown"; 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/eos/EosEventParser.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2010 by Stefano Fornari 2 | * 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; either version 2 of the License, or 6 | * (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | */ 17 | 18 | package cn.rainx.ptp.usbcamera.eos; 19 | 20 | import android.util.Log; 21 | 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | 25 | import cn.rainx.ptp.usbcamera.PTPException; 26 | import cn.rainx.ptp.usbcamera.PTPUnsupportedException; 27 | 28 | /** 29 | * This class parses a stream of bytes as a sequence of events accordingly 30 | * to how Canon EOS returns events. 31 | * 32 | * The event information is returned in a standard PTP data packet as a number 33 | * of records followed by an empty record at the end of the packet. Each record 34 | * consists of multiple four-byte fields and always starts with record length 35 | * field. Further structure of the record depends on the device property code 36 | * which always goes in the third field. The empty record consists of the size 37 | * field and four byte empty field, which is always zero. 38 | * 39 | * @author stefano fornari 40 | */ 41 | public class EosEventParser implements EosEventConstants { 42 | 43 | /** 44 | * The stream data are read from 45 | */ 46 | private InputStream is; 47 | 48 | /** 49 | * Creates a new parser to parse the given input stream 50 | * 51 | * @param is 52 | */ 53 | public EosEventParser(InputStream is) { 54 | if (is == null) { 55 | throw new IllegalArgumentException("The input stream cannot be null"); 56 | } 57 | 58 | this.is = is; 59 | } 60 | 61 | /** 62 | * Returns true is there are events in the stream (and the stream is still 63 | * open), false otherwise. 64 | * 65 | * @return true is there are events in the stream (and the stream is still 66 | * open), false otherwise. 67 | */ 68 | public boolean hasEvents() { 69 | try { 70 | // Log.d("EventParser", " available: " +is.available()); 71 | if (is.available() <= 0) { 72 | return false; 73 | } 74 | } catch (IOException e) { 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | 81 | /** 82 | * Returns the next event in the stream. 83 | * 84 | * @return the next event in the stream. 85 | * 86 | * @throws PTPException in case of errors 87 | */ 88 | public EosEvent getNextEvent() throws PTPException { 89 | EosEvent event = new EosEvent(); 90 | 91 | try { 92 | int len = getNextS32(); // len 93 | // Log.d("EventParser", " Len: " +len); 94 | if (len < 0x8) { 95 | throw new PTPUnsupportedException("Unsupported event (size<8 ???)"); 96 | } 97 | int code = getNextS32(); 98 | event.setCode(code); 99 | Log.d("EventParser", " Event len: " +len +", Code: 0x" + String.format("%04x", code) +" " +EosEvent.getEventName(code)); 100 | parseParameters(event, len-8); 101 | // Log.d("EventParser", " Event: params " +event.getParamCount()); 102 | for (int i = 1; i<= event.getParamCount(); i++) { 103 | Object p = event.getParam(i); 104 | if (p instanceof String) { 105 | Log.d("EventParser", " params " + i + ": " + String.format("%s", p)); 106 | } else if (p instanceof Boolean) { 107 | Log.d("EventParser", " params " + i + ": " + String.format("%b", p)); 108 | } else{ 109 | Log.d("EventParser", " params " + i + ": " + String.format("0x%04x %d", p, p)); 110 | } 111 | } 112 | } catch (IOException e) { 113 | Log.d ("EventParser", " Error reading event stream"); 114 | throw new PTPException("Error reading event stream", e); 115 | } 116 | 117 | return event; 118 | } 119 | 120 | 121 | // --------------------------------------------------------- Private methods 122 | 123 | private void parseParameters(EosEvent event, int len) 124 | throws PTPException, IOException { 125 | int code = event.getCode(); 126 | 127 | if (code == EosEventPropValueChanged) { 128 | parsePropValueChangedParameters(event); 129 | } else if (code == EosEventShutdownTimerUpdated) { 130 | // 131 | // No parameters 132 | // 133 | } else if (code == EosEventCameraStatusChanged) { 134 | event.setParam(1, getNextS32()); 135 | } else if (code == EosEventObjectAddedEx) { 136 | parseEosEventObjectAddedEx(event); 137 | } else{ 138 | is.skip(len); 139 | throw new PTPUnsupportedException("Unsupported event"); 140 | } 141 | } 142 | 143 | private void parsePropValueChangedParameters(EosEvent event) 144 | throws IOException { 145 | int property = getNextS32(); 146 | event.setParam(1, property); // property changed 147 | 148 | if ((property >= EosPropPictureStyleStandard) && 149 | (property <= EosPropPictureStyleUserSet3)) { 150 | boolean monochrome = (property == EosPropPictureStyleMonochrome); 151 | int size = getNextS32(); 152 | if (size > 0x1C) { 153 | // 154 | // It is a EosPropPictureStyleUserXXX, let's read the type (then 155 | // we do not use it) 156 | // 157 | monochrome = (getNextS32() == EosPropPictureStyleUserTypeMonochrome); 158 | } 159 | event.setParam(2, (monochrome) ? Boolean.TRUE : Boolean.FALSE); 160 | event.setParam(3, getNextS32()); // contrast 161 | event.setParam(4, getNextS32()); // sharpness 162 | if (monochrome) { 163 | getNextS32(); 164 | getNextS32(); 165 | event.setParam(5, getNextS32()); // filter effect 166 | event.setParam(6, getNextS32()); // toning effect 167 | } else { 168 | event.setParam(5, getNextS32()); // saturation 169 | event.setParam(6, getNextS32()); // color tone 170 | getNextS32(); 171 | getNextS32(); 172 | } 173 | } else { 174 | // 175 | // default 176 | // 177 | event.setParam(2, getNextS32()); 178 | } 179 | 180 | } 181 | 182 | private void parseEosEventObjectAddedEx(EosEvent event) 183 | throws IOException { 184 | event.setParam(1, getNextS32() ); // object id 185 | event.setParam(2, getNextS32() ); // storage id 186 | event.setParam(4, getNextS16() ); // format 187 | is.skip(10); 188 | event.setParam(5, getNextS32() ); // size 189 | event.setParam(3, getNextS32() ); // parent object id 190 | is.skip(4); // unknown 191 | event.setParam(6, getNextString()); // file name 192 | is.skip(4); 193 | } 194 | 195 | /** 196 | * Reads and return the next signed 32 bit integer read from the input 197 | * stream. 198 | * 199 | * @return the next signed 32 bit integer in the stream 200 | * 201 | * @throws IOException in case of IO errors 202 | */ 203 | private final int getNextS32() throws IOException { 204 | int retval; 205 | 206 | retval = (0xff & is.read()) ; 207 | retval |= (0xff & is.read()) << 8; 208 | retval |= (0xff & is.read()) << 16; 209 | retval |= is.read() << 24; 210 | 211 | return retval; 212 | } 213 | 214 | /** 215 | * Reads and return the next signed 16 bit integer read from the input 216 | * stream. 217 | * 218 | * @return the next signed 16 bit integer in the stream 219 | * 220 | * @throws IOException in case of IO errors 221 | */ 222 | private final int getNextS16() throws IOException { 223 | int retval; 224 | 225 | retval = (0xff & is.read()) ; 226 | retval |= (0xff & is.read()) << 8; 227 | 228 | return retval; 229 | } 230 | 231 | /** 232 | * Reads and return the next string read from the input stream. Strings are 233 | * zero (32 bit) terminated string 234 | * 235 | * @return the next string in the stream 236 | * 237 | * @throws IOException in case of IO errors 238 | */ 239 | private final String getNextString() throws IOException { 240 | StringBuilder retval = new StringBuilder(); 241 | 242 | char c = 0; 243 | while ((c = (char)is.read()) != 0) { 244 | retval.append(c); 245 | } 246 | 247 | // 248 | // At this point we read the string and one zero. We need to read the 249 | // remaining 3 zeros 250 | // 251 | is.skip(3); 252 | 253 | return retval.toString(); 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/nikon/NikonEvent.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2010 by Stefano Fornari 2 | // 3 | // This program is free software; you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation; either version 2 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program; if not, write to the Free Software 15 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | */ 17 | 18 | package cn.rainx.ptp.usbcamera.nikon; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | 24 | /** 25 | * 26 | * @author ste 27 | */ 28 | public class NikonEvent implements NikonEventConstants { 29 | 30 | /** 31 | * Event code 32 | */ 33 | private int code; 34 | 35 | /** 36 | * Params 37 | */ 38 | private List params; 39 | 40 | /** 41 | * Creates a new parser with the given data 42 | * 43 | * @param data the events data - NOT NULL 44 | */ 45 | public NikonEvent() { 46 | params = new ArrayList(); 47 | } 48 | 49 | public void setCode(int code) { 50 | this.code = code; 51 | } 52 | 53 | public int getCode() { 54 | return code; 55 | } 56 | 57 | /** 58 | * @param i the parameter index 59 | * @param param the param to set 60 | */ 61 | public void setParam(int i, Object value) { 62 | if (i<0) { 63 | throw new IllegalArgumentException("param index cannot be < 0"); 64 | } 65 | if (params.size() <= i) { 66 | ArrayList newParams = new ArrayList(i); 67 | newParams.addAll(params); 68 | params = newParams; 69 | for (int j=params.size(); j getParamCount())) { 82 | throw new IllegalArgumentException( 83 | "index " + i + " out of range (0-" + getParamCount() + ")" 84 | ); 85 | } 86 | return params.get(i-1); 87 | } 88 | 89 | public int getIntParam(int i) { 90 | return ((Integer)getParam(i)).intValue(); 91 | } 92 | 93 | public String getStringParam(int i) { 94 | return (String)getParam(i); 95 | } 96 | 97 | /** 98 | * 99 | * @return the number of parameters in this event 100 | */ 101 | public int getParamCount() { 102 | return params.size(); 103 | } 104 | 105 | public static String getEventName (int code){ 106 | switch (code) { 107 | case EosEventRequestGetEvent : return "EosEventRequestGetEvent"; 108 | case EosEventObjectAddedEx : return "EosEventObjectAddedEx"; 109 | case EosEventObjectRemoved : return "EosEventObjectRemoved"; 110 | case EosEventRequestGetObjectInfoEx : return "EosEventRequestGetObjectInfoEx"; 111 | case EosEventStorageStatusChanged : return "EosEventStorageStatusChanged"; 112 | case EosEventStorageInfoChanged : return "EosEventStorageInfoChanged"; 113 | case EosEventRequestObjectTransfer : return "EosEventRequestObjectTransfer"; 114 | case EosEventObjectInfoChangedEx : return "EosEventObjectInfoChangedEx"; 115 | case EosEventObjectContentChanged : return "EosEventObjectContentChanged"; 116 | case EosEventPropValueChanged : return "EosEventPropValueChanged"; 117 | case EosEventAvailListChanged : return "EosEventAvailListChanged"; 118 | case EosEventCameraStatusChanged : return "EosEventCameraStatusChanged"; 119 | case EosEventWillSoonShutdown : return "EosEventWillSoonShutdown"; 120 | case EosEventShutdownTimerUpdated : return "EosEventShutdownTimerUpdated"; 121 | case EosEventRequestCancelTransfer : return "EosEventRequestCancelTransfer"; 122 | case EosEventRequestObjectTransferDT : return "EosEventRequestObjectTransferDT"; 123 | case EosEventRequestCancelTransferDT : return "EosEventRequestCancelTransferDT"; 124 | case EosEventStoreAdded : return "EosEventStoreAdded"; 125 | case EosEventStoreRemoved : return "EosEventStoreRemoved"; 126 | case EosEventBulbExposureTime : return "EosEventBulbExposureTime"; 127 | case EosEventRecordingTime : return "EosEventRecordingTime"; 128 | case EosEventAfResult : return "EosEventRequestObjectTransferTS"; 129 | case EosEventRequestObjectTransferTS : return "EosEventAfResult"; 130 | } 131 | return "0x" + Integer.toHexString(code); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/nikon/NikonEventFormat.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2010 by Stefano Fornari 2 | * 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; either version 2 of the License, or 6 | * (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | */ 17 | package cn.rainx.ptp.usbcamera.nikon; 18 | 19 | import java.lang.reflect.Field; 20 | 21 | /** 22 | * This class formats an EosEvent to a string 23 | * 24 | * @author stefano fornari 25 | */ 26 | public class NikonEventFormat implements NikonEventConstants { 27 | public static String format(NikonEvent e) { 28 | StringBuilder sb = new StringBuilder(); 29 | 30 | int eventCode = e.getCode(); 31 | 32 | sb.append(getEventName(eventCode)); 33 | sb.append(" [ "); 34 | if (eventCode == EosEventPropValueChanged) { 35 | int propCode = e.getIntParam(1); 36 | sb.append(getPropertyName(propCode)) 37 | .append(": "); 38 | 39 | if ((propCode >= EosPropPictureStyleStandard) && 40 | (propCode <= EosPropPictureStyleUserSet3)) { 41 | sb.append("(Sharpness: ") 42 | .append(e.getIntParam(4)) 43 | .append(", Contrast: ") 44 | .append(e.getIntParam(3)); 45 | if (((Boolean)e.getParam(2)).booleanValue()) { 46 | sb.append(", Filter effect: ") 47 | .append(getFilterEffect(e.getIntParam(5))) 48 | .append(", Toning effect: ") 49 | .append(getToningEffect(e.getIntParam(6))); 50 | } else { 51 | sb.append(", Saturation: ") 52 | .append(e.getIntParam(5)) 53 | .append(", Color tone: ") 54 | .append(e.getIntParam(6)); 55 | } 56 | sb.append(")"); 57 | } else { 58 | if (e.getParamCount()>1) { 59 | sb.append(e.getIntParam(2)); 60 | } 61 | } 62 | } else if (eventCode == EosEventObjectAddedEx) { 63 | sb.append(formatEosEventObjectAddedEx(e)); 64 | } 65 | sb.append(" ]"); 66 | 67 | return sb.toString(); 68 | } 69 | 70 | /** 71 | * Returns the printable name of the given event 72 | * 73 | * @param code event code 74 | * 75 | * @return the printable name of the given event 76 | */ 77 | public static String getEventName(int code) { 78 | Field[] fields = NikonEventConstants.class.getDeclaredFields(); 79 | 80 | for (Field f: fields) { 81 | String name = f.getName(); 82 | if (name.startsWith("EosEvent")) { 83 | try { 84 | if (f.getInt(null) == code) { 85 | return name; 86 | } 87 | } catch (Exception e) { 88 | // 89 | // Nothing to do 90 | // 91 | } 92 | } 93 | } 94 | return "Unknown"; 95 | } 96 | 97 | /** 98 | * Returns the printable name of the given property 99 | * 100 | * @param code property code 101 | * 102 | * @return the printable name of the given property 103 | */ 104 | public static String getPropertyName(int code) { 105 | return getCodeName("EosProp", code); 106 | } 107 | 108 | /** 109 | * Returns the printable name of the given image format 110 | * 111 | * @param code image format code 112 | * 113 | * @return the printable name of the given image format 114 | */ 115 | public static String getImageFormatName(int code) { 116 | return getCodeName("ImageFormat", code); 117 | } 118 | 119 | /** 120 | * Returns the filter effect name given the code. Names are:
121 | * 0:None, 1:Yellow, 2:Orange, 3:Red, 4:Green 122 | * 123 | * @param code the filter effect code (0-4) 124 | * 125 | * @return the filter effect name 126 | */ 127 | public static String getFilterEffect(int code) { 128 | if ((code < 0) || (code > 4)) { 129 | throw new IllegalArgumentException("code must be in he range 0-4"); 130 | } 131 | 132 | switch (code) { 133 | case 0: return "None"; 134 | case 1: return "Yellow"; 135 | case 2: return "Orange"; 136 | case 3: return "Red"; 137 | case 4: return "Green"; 138 | } 139 | 140 | // 141 | // We should never get here 142 | // 143 | return "Unknown"; 144 | } 145 | 146 | /** 147 | * Returns the toning effect name given the code. Names are:
148 | * 0:None, 1:Sepia, 2:Blue, 3:Purple, 4:Green 149 | * 150 | * @param code the toning effect code (0-4) 151 | * 152 | * @return the toning effect name 153 | */ 154 | public static String getToningEffect(int code) { 155 | if ((code < 0) || (code > 4)) { 156 | throw new IllegalArgumentException("code must be in he range 0-4"); 157 | } 158 | 159 | switch (code) { 160 | case 0: return "None"; 161 | case 1: return "Sepia"; 162 | case 2: return "Blue"; 163 | case 3: return "Purple"; 164 | case 4: return "Green"; 165 | } 166 | 167 | // 168 | // We should never get here 169 | // 170 | return "Unknown"; 171 | } 172 | 173 | // --------------------------------------------------------- Private methods 174 | 175 | private static String formatEosEventObjectAddedEx(NikonEvent event) { 176 | return String.format( 177 | "Filename: %s, Size(bytes): %d, ObjectID: 0x%08X, StorageID: 0x%08X, ParentID: 0x%08X, Format: %s", 178 | event.getStringParam(6), 179 | event.getIntParam(5), 180 | event.getIntParam(1), 181 | event.getIntParam(2), 182 | event.getIntParam(3), 183 | getImageFormatName(event.getIntParam(4)) 184 | ); 185 | } 186 | 187 | /** 188 | * Looks up and returns the name of the code give if there is a constant 189 | * field in EosEventConstants which starts with the given prefix 190 | * 191 | * @param prefix the field name prefix 192 | * @param code image format code 193 | * 194 | * @return the printable name of the given code 195 | */ 196 | private static String getCodeName(String prefix, int code) { 197 | Field[] fields = NikonEventConstants.class.getDeclaredFields(); 198 | 199 | for (Field f: fields) { 200 | String name = f.getName(); 201 | if (name.startsWith(prefix)) { 202 | try { 203 | if (f.getInt(null) == code) { 204 | return name.substring(prefix.length()); 205 | } 206 | } catch (Exception e) { 207 | // 208 | // Nothing to do 209 | // 210 | } 211 | } 212 | } 213 | return "Unknown"; 214 | } 215 | 216 | } 217 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/nikon/NikonEventParser.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2010 by Stefano Fornari 2 | * 3 | * This program is free software; you can redistribute it and/or modify 4 | * it under the terms of the GNU General Public License as published by 5 | * the Free Software Foundation; either version 2 of the License, or 6 | * (at your option) any later version. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16 | */ 17 | 18 | package cn.rainx.ptp.usbcamera.nikon; 19 | 20 | import android.util.Log; 21 | 22 | 23 | import java.io.IOException; 24 | import java.io.InputStream; 25 | 26 | import cn.rainx.ptp.usbcamera.PTPException; 27 | import cn.rainx.ptp.usbcamera.PTPUnsupportedException; 28 | 29 | /** 30 | * This class parses a stream of bytes as a sequence of events accordingly 31 | * to how Canon EOS returns events. 32 | * 33 | * The event information is returned in a standard PTP data packet as a number 34 | * of records followed by an empty record at the end of the packet. Each record 35 | * consists of multiple four-byte fields and always starts with record length 36 | * field. Further structure of the record depends on the device property code 37 | * which always goes in the third field. The empty record consists of the size 38 | * field and four byte empty field, which is always zero. 39 | * 40 | * @author stefano fornari 41 | */ 42 | public class NikonEventParser implements NikonEventConstants { 43 | 44 | /** 45 | * The stream data are read from 46 | */ 47 | private InputStream is; 48 | 49 | /** 50 | * Creates a new parser to parse the given input stream 51 | * 52 | * @param is 53 | */ 54 | public NikonEventParser(InputStream is) { 55 | if (is == null) { 56 | throw new IllegalArgumentException("The input stream cannot be null"); 57 | } 58 | 59 | this.is = is; 60 | } 61 | 62 | /** 63 | * Returns true is there are events in the stream (and the stream is still 64 | * open), false otherwise. 65 | * 66 | * @return true is there are events in the stream (and the stream is still 67 | * open), false otherwise. 68 | */ 69 | public boolean hasEvents() { 70 | try { 71 | // Log.d("EventParser", " available: " +is.available()); 72 | if (is.available() <= 0) { 73 | return false; 74 | } 75 | } catch (IOException e) { 76 | return false; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | /** 83 | * Returns the next event in the stream. 84 | * 85 | * @return the next event in the stream. 86 | * 87 | * @throws PTPException in case of errors 88 | */ 89 | public NikonEvent getNextEvent() throws PTPException { 90 | NikonEvent event = new NikonEvent(); 91 | 92 | try { 93 | int len = getNextS32(); // len 94 | // Log.d("EventParser", " Len: " +len); 95 | if (len < 0x8) { 96 | throw new PTPUnsupportedException("Unsupported event (size<8 ???)"); 97 | } 98 | int code = getNextS32(); 99 | event.setCode(code); 100 | Log.d("EventParser", " Event len: " +len +", Code: 0x" + String.format("%04x", code) +" " +NikonEvent.getEventName(code)); 101 | parseParameters(event, len-8); 102 | // Log.d("EventParser", " Event: params " +event.getParamCount()); 103 | for (int i = 1; i<= event.getParamCount(); i++) 104 | Log.d("EventParser", " params " +i +": " + String.format("0x%04x %d",event.getParam(i), event.getParam(i))); 105 | } catch (IOException e) { 106 | Log.d ("EventParser", " Error reading event stream"); 107 | throw new PTPException("Error reading event stream", e); 108 | } 109 | 110 | return event; 111 | } 112 | 113 | 114 | // --------------------------------------------------------- Private methods 115 | 116 | private void parseParameters(NikonEvent event, int len) 117 | throws PTPException, IOException { 118 | int code = event.getCode(); 119 | 120 | if (code == EosEventPropValueChanged) { 121 | parsePropValueChangedParameters(event); 122 | } else if (code == EosEventShutdownTimerUpdated) { 123 | // 124 | // No parameters 125 | // 126 | } else if (code == EosEventCameraStatusChanged) { 127 | event.setParam(1, getNextS32()); 128 | } else if (code == EosEventObjectAddedEx) { 129 | parseEosEventObjectAddedEx(event); 130 | } else{ 131 | is.skip(len); 132 | throw new PTPUnsupportedException("Unsupported event"); 133 | } 134 | } 135 | 136 | private void parsePropValueChangedParameters(NikonEvent event) 137 | throws IOException { 138 | int property = getNextS32(); 139 | event.setParam(1, property); // property changed 140 | 141 | if ((property >= EosPropPictureStyleStandard) && 142 | (property <= EosPropPictureStyleUserSet3)) { 143 | boolean monochrome = (property == EosPropPictureStyleMonochrome); 144 | int size = getNextS32(); 145 | if (size > 0x1C) { 146 | // 147 | // It is a EosPropPictureStyleUserXXX, let's read the type (then 148 | // we do not use it) 149 | // 150 | monochrome = (getNextS32() == EosPropPictureStyleUserTypeMonochrome); 151 | } 152 | event.setParam(2, (monochrome) ? Boolean.TRUE : Boolean.FALSE); 153 | event.setParam(3, getNextS32()); // contrast 154 | event.setParam(4, getNextS32()); // sharpness 155 | if (monochrome) { 156 | getNextS32(); 157 | getNextS32(); 158 | event.setParam(5, getNextS32()); // filter effect 159 | event.setParam(6, getNextS32()); // toning effect 160 | } else { 161 | event.setParam(5, getNextS32()); // saturation 162 | event.setParam(6, getNextS32()); // color tone 163 | getNextS32(); 164 | getNextS32(); 165 | } 166 | } else { 167 | // 168 | // default 169 | // 170 | event.setParam(2, getNextS32()); 171 | } 172 | 173 | } 174 | 175 | private void parseEosEventObjectAddedEx(NikonEvent event) 176 | throws IOException { 177 | event.setParam(1, getNextS32() ); // object id 178 | event.setParam(2, getNextS32() ); // storage id 179 | event.setParam(4, getNextS16() ); // format 180 | is.skip(10); 181 | event.setParam(5, getNextS32() ); // size 182 | event.setParam(3, getNextS32() ); // parent object id 183 | is.skip(4); // unknown 184 | event.setParam(6, getNextString()); // file name 185 | is.skip(4); 186 | } 187 | 188 | /** 189 | * Reads and return the next signed 32 bit integer read from the input 190 | * stream. 191 | * 192 | * @return the next signed 32 bit integer in the stream 193 | * 194 | * @throws IOException in case of IO errors 195 | */ 196 | private final int getNextS32() throws IOException { 197 | int retval; 198 | 199 | retval = (0xff & is.read()) ; 200 | retval |= (0xff & is.read()) << 8; 201 | retval |= (0xff & is.read()) << 16; 202 | retval |= is.read() << 24; 203 | 204 | return retval; 205 | } 206 | 207 | /** 208 | * Reads and return the next signed 16 bit integer read from the input 209 | * stream. 210 | * 211 | * @return the next signed 16 bit integer in the stream 212 | * 213 | * @throws IOException in case of IO errors 214 | */ 215 | private final int getNextS16() throws IOException { 216 | int retval; 217 | 218 | retval = (0xff & is.read()) ; 219 | retval |= (0xff & is.read()) << 8; 220 | 221 | return retval; 222 | } 223 | 224 | /** 225 | * Reads and return the next string read from the input stream. Strings are 226 | * zero (32 bit) terminated string 227 | * 228 | * @return the next string in the stream 229 | * 230 | * @throws IOException in case of IO errors 231 | */ 232 | private final String getNextString() throws IOException { 233 | StringBuilder retval = new StringBuilder(); 234 | 235 | char c = 0; 236 | while ((c = (char)is.read()) != 0) { 237 | retval.append(c); 238 | } 239 | 240 | // 241 | // At this point we read the string and one zero. We need to read the 242 | // remaining 3 zeros 243 | // 244 | is.skip(3); 245 | 246 | return retval.toString(); 247 | } 248 | 249 | } 250 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/sony/SonyDevicePropDesc.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.usbcamera.sony; 2 | 3 | import cn.rainx.ptp.usbcamera.Buffer; 4 | import cn.rainx.ptp.usbcamera.DevicePropDesc; 5 | import cn.rainx.ptp.usbcamera.DevicePropValue; 6 | import cn.rainx.ptp.usbcamera.NameFactory; 7 | 8 | /** 9 | * Created by rainx on 2017/6/4. 10 | */ 11 | 12 | public class SonyDevicePropDesc extends DevicePropDesc { 13 | 14 | protected int unknown; 15 | 16 | 17 | /* Device Property pack/unpack */ 18 | /* 19 | #define PTP_dpd_Sony_DevicePropertyCode 0 20 | #define PTP_dpd_Sony_DataType 2 21 | #define PTP_dpd_Sony_GetSet 4 22 | #define PTP_dpd_Sony_Unknown 5 23 | #define PTP_dpd_Sony_FactoryDefaultValue 6 24 | */ 25 | 26 | protected Buffer buf; 27 | 28 | public SonyDevicePropDesc(NameFactory f, Buffer buf) { 29 | super(f); 30 | this.data = buf.data; 31 | this.offset = buf.offset; 32 | this.buf = buf; 33 | } 34 | 35 | public void parse () 36 | { 37 | // per 13.3.3, tables 23, 24, 25 38 | propertyCode = nextU16 (); 39 | dataType = nextU16 (); 40 | writable = nextU8 () != 0; 41 | 42 | unknown = nextS8(); 43 | 44 | // FIXME use factories, as vendor hooks 45 | factoryDefault = DevicePropValue.get (dataType, this); 46 | currentValue = DevicePropValue.get (dataType, this); 47 | 48 | formType = nextU8 (); 49 | switch (formType) { 50 | case 0: // no more 51 | break; 52 | case 1: // range: min, max, step 53 | constraints = new Range (dataType, this); 54 | break; 55 | case 2: // enumeration: n, value1, ... valueN 56 | constraints = parseEnumeration (); 57 | break; 58 | default: 59 | System.err.println ("ILLEGAL prop desc form, " + formType); 60 | formType = 0; 61 | break; 62 | } 63 | 64 | // sync offset 65 | this.buf.offset = this.offset; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/ptp/usbcamera/sony/SonyExtDeviceInfo.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.ptp.usbcamera.sony; 2 | 3 | import android.util.Log; 4 | 5 | import java.util.Arrays; 6 | 7 | import cn.rainx.ptp.usbcamera.Data; 8 | import cn.rainx.ptp.usbcamera.NameFactory; 9 | 10 | /** 11 | * Created by rainx on 2017/6/11. 12 | */ 13 | 14 | public class SonyExtDeviceInfo extends Data { 15 | private static final String TAG = "SonyExtDeviceInfo"; 16 | 17 | int operationsSupported []; // 10.2 18 | int eventsSupported []; // 12.5 19 | int propertiesSupported []; // 13.3.5 20 | int allSupported[]; 21 | 22 | public SonyExtDeviceInfo(NameFactory f) { 23 | super (true, null, 0, f); 24 | } 25 | 26 | /** Returns true iff the device supports this operation */ 27 | public boolean supportsOperation (int opCode) 28 | { 29 | return supports (operationsSupported, opCode); 30 | } 31 | 32 | /** Returns true iff the device supports this event */ 33 | public boolean supportsEvent (int eventCode) 34 | { 35 | return supports (eventsSupported, eventCode); 36 | } 37 | 38 | /** Returns true iff the device supports this property */ 39 | public boolean supportsProperty (int propCode) 40 | { 41 | return supports (propertiesSupported, propCode); 42 | } 43 | 44 | private boolean supports (int supported [], int code) 45 | { 46 | for (int i = 0; i < supported.length; i++) { 47 | if (code == supported [i]) 48 | return true; 49 | } 50 | return false; 51 | } 52 | 53 | protected void parse () 54 | { 55 | super.parse (); 56 | // https://github.com/gphoto/libgphoto2/blob/979e75f15b4bed396a6cac7a505c8d65b92608f2/camlibs/ptp2/ptp.c 57 | // ptp_sony_get_vendorpropcodes 58 | // skip first 59 | nextU16(); 60 | allSupported = nextU16Array (); 61 | 62 | // 一个U16Array还没有读完的情况 63 | if (allSupported.length * 2 + 2 + 4 < getLength()) { 64 | int[] p2 = nextU16Array(); 65 | int oldLen = allSupported.length; 66 | allSupported = Arrays.copyOf(allSupported, oldLen + p2.length); 67 | 68 | for (int i = 0; i < p2.length; i++) { 69 | allSupported[oldLen + i] = p2[i]; 70 | } 71 | } 72 | // https://github.com/gphoto/libgphoto2/blob/master/camlibs/ptp2/library.c 73 | // search PTP_OC_SONY_GetSDIOGetExtDeviceInfo 74 | 75 | int opcodes = 0, propcodes = 0, events = 0, j = 0,k = 0,l = 0; 76 | 77 | for (int op : allSupported) { 78 | switch (op & 0x7000) { 79 | case 0x1000: opcodes++; break; 80 | case 0x4000: events++; break; 81 | case 0x5000: propcodes++; break; 82 | default: 83 | Log.d (TAG, "ptp_sony_get_vendorpropcodes() unknown opcode " + op); 84 | break; 85 | } 86 | } 87 | 88 | operationsSupported = new int[opcodes]; 89 | eventsSupported = new int[events]; 90 | propertiesSupported = new int[propcodes]; 91 | 92 | for (int op : allSupported) { 93 | switch (op & 0x7000) { 94 | case 0x1000: 95 | operationsSupported[k++] = op; 96 | break; 97 | case 0x4000: 98 | eventsSupported[l++] = op; 99 | break; 100 | case 0x5000: 101 | propertiesSupported[j++] = op; 102 | break; 103 | default: 104 | break; 105 | } 106 | } 107 | 108 | } 109 | 110 | 111 | public String toString() { 112 | 113 | String result = "DeviceInfo:\n"; 114 | // per chapter 10 115 | result += ("\n\nOperations Supported:"); 116 | for (int i = 0; i < operationsSupported.length; i++) { 117 | result += "\n\t" +factory.getOpcodeString (operationsSupported [i]); 118 | } 119 | 120 | // per chapter 11 121 | result += ("\n\nEvents Supported:"); 122 | for (int i = 0; i < eventsSupported.length; i++) { 123 | result += "\n\t" +factory.getEventString (eventsSupported [i]); 124 | } 125 | 126 | // per chapter 13 127 | result += ("\n\nDevice Properties Supported:\n"); 128 | for (int i = 0; i < propertiesSupported.length; i++) { 129 | result += "\t" +factory.getPropertyName (propertiesSupported [i]); 130 | } 131 | return result; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/tracker/BaseInfo.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.tracker; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageManager; 5 | import android.hardware.usb.UsbDevice; 6 | import android.os.Build; 7 | import android.util.Log; 8 | 9 | import com.loopj.android.http.RequestParams; 10 | 11 | import java.util.List; 12 | import java.util.UUID; 13 | 14 | import cn.rainx.exif.ExifUtils; 15 | import cn.rainx.ptp.db.Uuid; 16 | import cn.rainx.ptp.usbcamera.BaselineInitiator; 17 | 18 | /** 19 | * Created by rainx on 2017/8/26. 20 | */ 21 | 22 | public class BaseInfo { 23 | 24 | private int vendorId; 25 | private int deviceId; 26 | private String serial; 27 | private String androidDeviceUniqueId; 28 | private Integer androidUserUniqueId; 29 | private String androidDeviceInfo; 30 | private int androidOSVer; 31 | private int libVersion; 32 | private String deviceInfo = "empty"; 33 | private Context context; 34 | 35 | public static final String TAG = "BaseInfo"; 36 | 37 | 38 | public BaseInfo(Context context, BaselineInitiator initiator, Integer userUniqueId) 39 | { 40 | this.context = context; 41 | 42 | libVersion = initLibVersion(); 43 | androidDeviceUniqueId = initAndroidDevicerUniqueId(); 44 | androidOSVer = initAndroidOSVer(); 45 | androidUserUniqueId = userUniqueId; 46 | 47 | androidDeviceInfo = Build.DEVICE.toString(); 48 | 49 | 50 | Log.d(TAG, "get android os ver " + androidOSVer); 51 | Log.d(TAG, "android uuid is " + androidDeviceUniqueId); 52 | Log.d(TAG, "libversion is " + libVersion); 53 | if (initiator != null) { 54 | UsbDevice device = initiator.getDevice(); 55 | 56 | if (device != null) { 57 | vendorId = device.getVendorId(); 58 | deviceId = device.getDeviceId(); 59 | serial = device.getSerialNumber(); 60 | } else { 61 | vendorId = -1; 62 | deviceId = -1; 63 | serial = "empty"; 64 | } 65 | try { 66 | deviceInfo = initiator.getDeviceInfo().toString(); 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | // ignore it 70 | deviceInfo = "Exception when get device info"; 71 | } 72 | 73 | } else { 74 | vendorId = -1; 75 | deviceId = -1; 76 | serial = "empty"; 77 | 78 | } 79 | 80 | } 81 | 82 | public Context getContext() { 83 | return context; 84 | } 85 | 86 | public String getSerial() { 87 | return serial; 88 | } 89 | 90 | public void setSerial(String serial) { 91 | this.serial = serial; 92 | } 93 | 94 | public String getAndroidDeviceUniqueId() { 95 | return androidDeviceUniqueId; 96 | } 97 | 98 | public void setAndroidDeviceUniqueId(String androidDeviceUniqueId) { 99 | this.androidDeviceUniqueId = androidDeviceUniqueId; 100 | } 101 | 102 | public Integer getAndroidUserUniqueId() { 103 | return androidUserUniqueId; 104 | } 105 | 106 | public void setAndroidUserUniqueId(Integer androidUserUniqueId) { 107 | this.androidUserUniqueId = androidUserUniqueId; 108 | } 109 | 110 | public String getAndroidDeviceInfo() { 111 | return androidDeviceInfo; 112 | } 113 | 114 | public void setAndroidDeviceInfo(String androidDeviceInfo) { 115 | this.androidDeviceInfo = androidDeviceInfo; 116 | } 117 | 118 | public int getAndroidOSVer() { 119 | return androidOSVer; 120 | } 121 | 122 | public void setAndroidOSVer(int androidOSVer) { 123 | this.androidOSVer = androidOSVer; 124 | } 125 | 126 | public void setLibVersion(int libVersion) { 127 | this.libVersion = libVersion; 128 | } 129 | 130 | public int getLibVersion() { 131 | return libVersion; 132 | } 133 | 134 | public int initLibVersion() 135 | { 136 | try { 137 | return context.getPackageManager() 138 | .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA) 139 | .metaData.getInt("iu_libversion"); 140 | } catch (PackageManager.NameNotFoundException e) { 141 | e.printStackTrace(); 142 | return 0; 143 | } 144 | } 145 | 146 | public String initAndroidDevicerUniqueId() { 147 | List uuids = Uuid.find(Uuid.class, "key=?", "uuid"); 148 | if (uuids != null && uuids.size() > 0) { 149 | return uuids.get(0).getValue(); 150 | } else { 151 | String uuid = UUID.randomUUID().toString(); 152 | Uuid u = new Uuid(); 153 | u.setKey("uuid"); 154 | u.setValue(uuid); 155 | u.save(); 156 | return uuid; 157 | } 158 | } 159 | 160 | public int initAndroidOSVer() { 161 | return Build.VERSION.SDK_INT; 162 | } 163 | 164 | 165 | public RequestParams getParams() { 166 | RequestParams params = new RequestParams(); 167 | params.put("vender_id", vendorId); 168 | params.put("device_id", deviceId); 169 | params.put("serial", serial); 170 | params.put("android_device_unique_id", androidDeviceUniqueId); 171 | params.put("android_user_unique_id", androidUserUniqueId); 172 | params.put("android_device_info", androidDeviceInfo); 173 | params.put("android_os_ver", androidOSVer); 174 | params.put("lib_version", libVersion); 175 | params.put("device_info", deviceInfo); 176 | 177 | return params; 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /library/src/main/java/cn/rainx/tracker/IuTracker.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.tracker; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.SharedPreferences; 6 | import android.os.Looper; 7 | import android.preference.Preference; 8 | import android.util.Log; 9 | 10 | import com.loopj.android.http.AsyncHttpClient; 11 | import com.loopj.android.http.JsonHttpResponseHandler; 12 | import com.loopj.android.http.RequestParams; 13 | import com.loopj.android.http.SyncHttpClient; 14 | 15 | import org.json.JSONObject; 16 | 17 | import cn.rainx.ptp.usbcamera.BaselineInitiator; 18 | import cn.rainx.ptp.usbcamera.PTPException; 19 | import cz.msebera.android.httpclient.Header; 20 | import cz.msebera.android.httpclient.annotation.NotThreadSafe; 21 | 22 | import static android.util.Log.*; 23 | 24 | /** 25 | * Created by rainx on 2017/8/20. 26 | */ 27 | 28 | public class IuTracker { 29 | 30 | public static final String TAG = IuTracker.class.getSimpleName(); 31 | 32 | private boolean bugTrackerEnabled = true; 33 | private boolean perfTrackerEnabled = true; 34 | private boolean deviceInfoTrackerEnabled = true; 35 | private String restBaseUri; 36 | private static volatile IuTracker _instance= null; 37 | 38 | /** 39 | * 获取单例 40 | * @param restBaseUri rest服务的基础uri 如 http://127.0.0.1:8000 41 | * @return 42 | */ 43 | public static synchronized IuTracker getInstance(String restBaseUri) { 44 | if (_instance == null) { 45 | _instance = new IuTracker(restBaseUri); 46 | } 47 | return _instance; 48 | } 49 | 50 | public static AsyncHttpClient syncHttpClient= new SyncHttpClient(); 51 | public static AsyncHttpClient asyncHttpClient = new AsyncHttpClient(); 52 | 53 | 54 | private IuTracker(String restBaseUri) { 55 | this.restBaseUri = restBaseUri; 56 | 57 | } 58 | 59 | public void setBugTrackerEnabled(boolean bugTrackerEnabled) { 60 | this.bugTrackerEnabled = bugTrackerEnabled; 61 | } 62 | 63 | public void setPerfTrackerEnabled(boolean perfTrackerEnabled) { 64 | this.perfTrackerEnabled = perfTrackerEnabled; 65 | } 66 | 67 | public void setDeviceInfoTrackerEnabled(boolean deviceInfoTrackerEnabled) { 68 | this.deviceInfoTrackerEnabled = deviceInfoTrackerEnabled; 69 | } 70 | 71 | public void setRestBaseUri(String restBaseUri) { 72 | int len = restBaseUri.length(); 73 | if (restBaseUri.substring(len - 1, len).equals("/")) { 74 | restBaseUri = restBaseUri.substring(0, len - 1); 75 | } 76 | this.restBaseUri = restBaseUri; 77 | } 78 | 79 | 80 | /** 81 | * 向服务器报告设备信息,一个客户端只会报告一次 82 | * @param baseInfo 83 | */ 84 | public void reportDeviceInfo(BaseInfo baseInfo) { 85 | // need implement 86 | // 1 持久化确保改接口对于一个手机只被运行一次 87 | // 检测是否已经report 过 88 | 89 | if (!deviceInfoTrackerEnabled) { 90 | return; 91 | } 92 | 93 | final String key = "Already_Reported_DeviceInfo"; 94 | 95 | final SharedPreferences sp = baseInfo.getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE); 96 | if (!sp.getBoolean(key, false)) { 97 | 98 | String url = restBaseUri + "/device_info_tracker/"; 99 | 100 | RequestParams params = baseInfo.getParams(); 101 | 102 | getClient().post(url, params, new JsonHttpResponseHandler() { 103 | @Override 104 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) { 105 | Log.i(TAG, "done!"); 106 | Log.d(TAG, response.toString()); 107 | sp.edit().putBoolean(key, true); 108 | sp.edit().commit(); 109 | } 110 | 111 | @Override 112 | public void onFailure(int statusCode , Header[] headers, Throwable e, JSONObject response) { 113 | e.printStackTrace(); 114 | } 115 | }); 116 | 117 | 118 | } 119 | 120 | } 121 | 122 | /** 123 | * 向服务器报告文件传输速度 124 | * @param baseInfo 基础信息 125 | * @param initiator USB Initiator 设备 126 | * @param filesize 文件大小 127 | * @param startTs 开始时间 128 | * @param endTs 结束时间 129 | * @param costTs 消耗时间 130 | */ 131 | public void reportPerf(BaseInfo baseInfo, BaselineInitiator initiator, 132 | int filesize, float startTs, float endTs, float costTs) { 133 | 134 | 135 | if (!perfTrackerEnabled) { 136 | return; 137 | } 138 | 139 | String url = restBaseUri + "/perf_tracker/"; 140 | 141 | RequestParams params = baseInfo.getParams(); 142 | if (initiator != null) { 143 | params.put("sync_trigger_mode", initiator.getSyncTriggerMode()); 144 | params.put("sync_mode", initiator.getSyncMode()); 145 | params.put("sync_record_mode", initiator.getSyncRecordMode()); 146 | } else { 147 | params.put("sync_trigger_mode", -1); 148 | params.put("sync_mode", -1); 149 | params.put("sync_record_mode", -1); 150 | } 151 | params.put("filesize", filesize); 152 | params.put("start_ts", startTs); 153 | params.put("end_ts", endTs); 154 | params.put("cost_ts", costTs); 155 | 156 | getClient().post(url, params, new JsonHttpResponseHandler() { 157 | @Override 158 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) { 159 | Log.i(TAG, "done!"); 160 | Log.d(TAG, response.toString()); 161 | } 162 | 163 | @Override 164 | public void onFailure(int statusCode , Header[] headers, Throwable e, JSONObject response) { 165 | e.printStackTrace(); 166 | } 167 | }); 168 | 169 | } 170 | 171 | /** 172 | * 向服务器报告bug 173 | * @param baseInfo 基础信息 174 | * @param initiator USB Initiator 设备 175 | * @param exception 异常信息 176 | * @param traceback 调用堆栈 177 | * @param fileinfo 正在传输的文件信息 178 | */ 179 | public void reportBug(BaseInfo baseInfo, BaselineInitiator initiator, String exception, 180 | String traceback, String fileinfo) { 181 | 182 | if (!bugTrackerEnabled) { 183 | return; 184 | } 185 | 186 | String url = restBaseUri + "/bug_tracker/"; 187 | RequestParams params = baseInfo.getParams(); 188 | if (initiator != null) { 189 | params.put("sync_trigger_mode", initiator.getSyncTriggerMode()); 190 | params.put("sync_mode", initiator.getSyncMode()); 191 | params.put("sync_record_mode", initiator.getSyncRecordMode()); 192 | } else { 193 | params.put("sync_trigger_mode", -1); 194 | params.put("sync_mode", -1); 195 | params.put("sync_record_mode", -1); 196 | } 197 | params.put("exception", exception); 198 | params.put("traceback", traceback); 199 | params.put("fileinfo", fileinfo); 200 | 201 | getClient().post(url, params, new JsonHttpResponseHandler() { 202 | @Override 203 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) { 204 | Log.i(TAG, "done!"); 205 | Log.d(TAG, response.toString()); 206 | } 207 | 208 | @Override 209 | public void onFailure(int statusCode , Header[] headers, Throwable e, JSONObject response) { 210 | e.printStackTrace(); 211 | } 212 | }); 213 | } 214 | 215 | /** 216 | * @return an async client when calling from the main thread, otherwise a sync client. 217 | */ 218 | private static AsyncHttpClient getClient() 219 | { 220 | // Return the synchronous HTTP client when the thread is not prepared 221 | if (Looper.myLooper() == null) 222 | return syncHttpClient; 223 | return asyncHttpClient; 224 | } 225 | 226 | } 227 | -------------------------------------------------------------------------------- /library/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /library/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shenqinwei/mtp_controller/bb289b7ac89d73f13a29ea7cc13a307934589f0a/library/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /library/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /library/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MtpController 3 | 获取设备PTP/MTP信息 4 | 全部设备信息 5 | 连接到设备 6 | 获取全部媒体对象 7 | 传输对象 8 | 获取对象信息 9 | 10 | -------------------------------------------------------------------------------- /library/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /library/src/main/res/xml/device_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /library/src/test/java/mtp/rainx/cn/mtpcontroller/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package mtp.rainx.cn.mtpcontroller; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Wed Nov 20 16:52:41 CST 2019 8 | ndk.dir=/Users/p-dev/Library/Android/sdk/ndk-bundle 9 | sdk.dir=/Users/p-dev/Library/Android/sdk 10 | -------------------------------------------------------------------------------- /mtp_controller.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /mtpcontrollerdemo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /mtpcontrollerdemo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | applicationId "cn.rainx.demo" 9 | minSdkVersion 21 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(dir: 'libs', include: ['*.jar']) 27 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 28 | exclude group: 'com.android.support', module: 'support-annotations' 29 | }) 30 | compile 'com.android.support:appcompat-v7:25.3.1' 31 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 32 | testCompile 'junit:junit:4.12' 33 | compile project(':library') 34 | } 35 | -------------------------------------------------------------------------------- /mtpcontrollerdemo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/rainx/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /mtpcontrollerdemo/src/androidTest/java/cn/rainx/demo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package cn.rainx.demo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | import android.util.Log; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import cn.rainx.ptp.usbcamera.BaselineInitiator; 12 | import cn.rainx.tracker.BaseInfo; 13 | import cn.rainx.tracker.IuTracker; 14 | 15 | import static org.junit.Assert.*; 16 | 17 | /** 18 | * Instrumentation test, which will execute on an Android device. 19 | * 20 | * @see Testing documentation 21 | */ 22 | @RunWith(AndroidJUnit4.class) 23 | public class ExampleInstrumentedTest { 24 | 25 | public static final String TAG = ExampleInstrumentedTest.class.getName(); 26 | 27 | @Test 28 | public void useAppContext() throws Exception { 29 | // Context of the app under test. 30 | Context appContext = InstrumentationRegistry.getTargetContext(); 31 | 32 | assertEquals("cn.rainx.demo", appContext.getPackageName()); 33 | } 34 | 35 | 36 | private BaseInfo getBaseInfo() { 37 | Context appContext = InstrumentationRegistry.getTargetContext(); 38 | BaseInfo baseInfo = new BaseInfo(appContext, null, 1000); 39 | int libver = baseInfo.getLibVersion(); 40 | Log.v(TAG, libver + ""); 41 | Log.v(TAG, baseInfo.getAndroidOSVer() + ""); 42 | //Log.v(TAG, baseInfo.getSerial()); 43 | Log.v(TAG, baseInfo.getAndroidDeviceUniqueId()); 44 | Log.v(TAG, baseInfo.getAndroidUserUniqueId() + ""); 45 | Log.v(TAG, baseInfo.getAndroidDeviceInfo()); 46 | return baseInfo; 47 | } 48 | 49 | @Test 50 | public void testReportBaseInfo() { 51 | BaseInfo baseinfo = getBaseInfo(); 52 | IuTracker tracker = IuTracker.getInstance("http://10.63.255.84:8000"); 53 | tracker.reportDeviceInfo(baseinfo); 54 | } 55 | 56 | @Test 57 | public void testReportPerf() { 58 | BaseInfo baseinfo = getBaseInfo(); 59 | IuTracker tracker = IuTracker.getInstance("http://10.63.255.84:8000"); 60 | tracker.reportPerf(baseinfo, null, 1024 * 1024 , System.currentTimeMillis(), 61 | System.currentTimeMillis() + 10, 10); 62 | } 63 | 64 | @Test 65 | public void testReportBug() { 66 | BaseInfo baseinfo = getBaseInfo(); 67 | IuTracker tracker = IuTracker.getInstance("http://10.63.255.84:8000"); 68 | tracker.reportBug(baseinfo, null, "PTPException", "....traceback....", "filename : xxxx"); 69 | } 70 | 71 | @Test 72 | public void testException() { 73 | Exception e = new Exception("hello"); 74 | Log.v(TAG, e.toString()); 75 | Log.v(TAG, e.getMessage()); 76 | Log.v(TAG, e.getClass().toString()); 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /mtpcontrollerdemo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /mtpcontrollerdemo/src/main/res/layout/activity_controller.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 22 | 23 | 26 | 27 | 31 | 32 |