main
陆洲 3 months ago
parent 2b0bd0c200
commit 48a3f9c1ad

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

@ -0,0 +1,2 @@
#Wed Sep 24 19:01:56 CST 2025
gradle.version=8.2.1

@ -0,0 +1,2 @@
#Sat Sep 06 21:08:15 CST 2025
java.home=D\:\\APP\\Android\\Android Studio\\Android Studio Setup\\jbr

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

@ -0,0 +1,342 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="imageAssetPanel">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="actionbar">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="C:\Users\白云\AppData\Local\Temp\ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="launcher">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="backgroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="scalingPercent" value="0" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundClipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="C:\Users\白云\AppData\Local\Temp\ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="D:\PictureVideo\picture\AppIcon\IMG_20251104_191245.jpg" />
<entry key="scalingPercent" value="105" />
<entry key="trimmed" value="true" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundTextAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="backgroundAssetType" value="COLOR" />
<entry key="backgroundColor" value="fcfdfd" />
<entry key="generateLegacyIcon" value="false" />
<entry key="generatePlayStoreIcon" value="false" />
<entry key="generateRoundIcon" value="false" />
<entry key="previewDensity" value="hdpi" />
<entry key="showSafeZone" value="false" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="launcherLegacy">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="C:\Users\白云\AppData\Local\Temp\ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="image">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="D:\PictureVideo\picture\AppIcon\IMG_20251104_191245.jpg" />
<entry key="paddingPercent" value="-5" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="assetType" value="IMAGE" />
<entry key="cropped" value="true" />
<entry key="imageAsset" value="D:\PictureVideo\picture\AppIcon\IMG_20251104_191245.jpg" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="notification">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="clipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
<entry key="imagePath" value="C:\Users\白云\AppData\Local\Temp\ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="text">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="textAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="tvBanner">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="tvChannel">
<value>
<PersistentState>
<option name="children">
<map>
<entry key="foregroundClipArt">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="imagePath" value="C:\Users\白云\AppData\Local\Temp\ic_android_black_24dp.xml" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundImage">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundText">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
<entry key="foregroundTextAsset">
<value>
<PersistentState>
<option name="values">
<map>
<entry key="color" value="000000" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
<option name="values">
<map>
<entry key="previewDensity" value="hdpi" />
<entry key="showSafeZone" value="false" />
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</PersistentState>
</value>
</entry>
</map>
</option>
</component>
</project>

@ -0,0 +1,123 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-09-10T14:52:55.484948900Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=D:\APP\Android\AndroidAVD\Pixel_7.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "automatic"
}

@ -0,0 +1,74 @@
# Java版本兼容性问题解决指南
## 问题描述
```
Unsupported class file major version 65
```
这个错误表示你的Java版本太新了Gradle 8.0不支持。
## Java版本对照表
- Java 8 = major version 52
- Java 11 = major version 55
- Java 17 = major version 61
- Java 21 = major version 65 ← 你的版本
## 解决方案
### 方案1降级Gradle版本推荐
我已经将项目配置为使用兼容的版本:
- Gradle 7.6.1
- Android Gradle Plugin 7.4.2
- Kotlin 1.8.10
- compileSdk 33
### 方案2检查Java版本
在命令行中运行:
```bash
java -version
javac -version
```
### 方案3设置JAVA_HOME
如果安装了多个Java版本设置JAVA_HOME指向Java 11或Java 17
**Windows:**
```cmd
set JAVA_HOME=C:\Program Files\Java\jdk-11.0.16
```
**或者在Android Studio中设置**
1. File → Project Structure → SDK Location
2. 设置JDK location为Java 11或Java 17
### 方案4清理Gradle缓存
删除Gradle缓存目录
```bash
# Windows
rmdir /s "C:\Users\你的用户名\.gradle\caches"
rmdir /s "C:\Users\你的用户名\.gradle\wrapper"
# 或者在Android Studio中
File → Invalidate Caches and Restart
```
## 验证修复
1. 重新同步项目Sync Project with Gradle Files
2. 如果还有问题检查Android Studio的JDK设置
3. 确保使用的是Java 11或Java 17
## 推荐配置
- **Java版本**: Java 11 或 Java 17
- **Gradle版本**: 7.6.1
- **Android Gradle Plugin**: 7.4.2
- **Kotlin版本**: 1.8.10
## 常见问题
- **Q: 为什么不能使用Java 21**
A: Gradle 7.x不支持Java 21需要Gradle 8.3+才支持
- **Q: 如何安装Java 11**
A: 从Oracle或OpenJDK官网下载安装
- **Q: Android Studio显示错误的Java版本**
A: 在Project Structure中重新设置JDK路径

@ -0,0 +1,72 @@
# 网络问题解决指南
## 问题描述
```
Could not install Gradle distribution from 'https://services.gradle.org/distributions/gradle-8.0-bin.zip'.
Reason: java.net.SocketException: Connection reset
```
## 解决方案
### 方案1使用国内镜像源推荐
我已经配置了阿里云镜像源,应该能解决大部分网络问题。
### 方案2手动下载Gradle
如果镜像源还是有问题,可以手动下载:
1. 访问https://mirrors.cloud.tencent.com/gradle/gradle-8.0-bin.zip
2. 下载到本地
3. 将文件放到:`C:\Users\你的用户名\.gradle\wrapper\dists\gradle-8.0-bin\随机字符串\`
4. 重新同步项目
### 方案3使用代理
如果你有代理软件,在 `gradle.properties` 中配置:
```properties
systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=7890
systemProp.https.proxyHost=127.0.0.1
systemProp.https.proxyPort=7890
```
### 方案4降级Gradle版本
如果8.0版本有问题可以降级到7.6
1. 修改 `gradle/wrapper/gradle-wrapper.properties`
```properties
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.6-bin.zip
```
2. 修改 `build.gradle` 中的Android Gradle Plugin版本
```gradle
id 'com.android.application' version '7.4.2' apply false
```
### 方案5清理缓存
在Android Studio中
1. File → Invalidate Caches and Restart
2. 选择 "Invalidate and Restart"
### 方案6使用离线模式
如果网络问题持续,可以:
1. 下载所有依赖到本地
2. 在 `gradle.properties` 中添加:
```properties
org.gradle.offline=true
```
## 验证修复
修复后在Android Studio中
1. 点击 "Sync Project with Gradle Files"
2. 等待同步完成
3. 如果还有问题查看Build窗口的错误信息
## 常见问题
- **Q: 镜像源还是连不上?**
A: 尝试其他镜像源,如腾讯云、华为云等
- **Q: 代理配置不生效?**
A: 确保代理软件正在运行,端口号正确
- **Q: 手动下载后还是报错?**
A: 检查文件路径是否正确,文件名是否完整

@ -0,0 +1,314 @@
# Interactive Map App图述
> 一款完全离线运行的交互式地图记事工具。用户可以把任意图片当作底图,在上面布置带有文字 / 图片 / 链接 / 音频的标记,并通过二维码或蓝牙离线分享给他人。
## 目录
- [系统简介](#系统简介)
- [配置环境](#配置环境)
- [简介](#简介)
- [核心特性](#核心特性)
- [系统架构](#系统架构)
- [数据模型与持久化](#数据模型与持久化)
- [分享能力](#分享能力)
- [界面与交互流程](#界面与交互流程)
- [项目结构](#项目结构)
- [开发环境](#开发环境)
- [快速开始](#快速开始)
- [构建与调试](#构建与调试)
- [权限与隐私](#权限与隐私)
- [常见问题](#常见问题)
- [配套文档](#配套文档)
## 系统简介
### 项目背景
在当前的可视化沟通与现场讲解领域,专业人士(如导游、房产经纪、教师)虽有用直观方式进行讲解的强烈需求,却缺乏高效的专用工具。现有解决方案存在显著不足:
- **通用图片标记工具功能单一**:无法关联多媒体内容,信息承载能力有限
- **网页版热点图生成器依赖电脑端操作**:移动性差,无法满足现场即时创作需求
- **大型行业专用软件过于昂贵和复杂**:中小型团队及个人从业者难以承担成本和培训成本
市场亟需一款专注于移动端、能融合多媒体交互、并支持离线分享的轻量级专业工具,以填补这一空白,提升各行业一线人员的讲解效率与体验。
### 系统定位
**图述Interactive Map** 是一款面向专业人士的移动端交互式地图制作与分享工具,具有以下特点:
- **应用类型**Android 移动应用
- **运行环境**Android 7.0 (API 24) 及以上版本
- **数据存储**:完全本地化,使用 Room 数据库和内部存储
- **网络依赖**:无需网络连接,完全离线运行
- **分享方式**:二维码分享(主要方式)、蓝牙直连传输(大文件场景)
### 核心价值
1. **解决创作流程移动化不足的问题**:用户可以在手机上完成从素材准备到内容制作的全流程
2. **解决信息呈现形式单一的问题**:静态图片可以承载丰富的多媒体信息(文字、图片、链接、音频)
3. **解决离线场景下分享与演示困难的问题**:在网络信号不佳的展会、景区、楼宇内,通过二维码或蓝牙实现高效的信息传递
4. **解决专业工具成本高昂、使用复杂的问题**:为中小型团队及个人从业者提供一个低成本、易上手的替代方案
### 目标用户群体
- **导游行业**:旅游导游、景区讲解员,用于制作景点导览图和路线说明
- **房产经纪**:房产销售人员,用于制作楼盘平面图、户型图的多媒体展示
- **教育培训**:教师、培训师,用于制作教学地图、知识图谱和互动课件
- **展陈行业**:博物馆、展览馆工作人员,用于制作展品导览和展区说明
- **企业培训**:企业内部培训师,用于制作培训材料和现场演示
### 软件创意
1. **"图片即画布,标记即入口"的核心理念**:将任何静态图片转化为一个信息丰富的交互界面,通过点击标记点解锁深层内容,极大增强了单张图片的信息承载量和表现力。
2. **移动端全流程闭环创作**:支持"现场拍摄 → 即时标记 → 关联多媒体 → 生成二维码"的全流程操作,所有步骤均在手机端完成,极致便捷。
3. **基于二维码的离线分享技术**:创新性地将项目所有数据编码入一个二维码中,实现了无需服务器、无需网络的离线分享与演示,特别适合现场实地场景。
4. **低代码、可视化的创作方式**:提供极其直观的拖拽、点击式操作界面,用户无需任何编程基础,即可在几分钟内创作出一个专业的交互式讲解材料。
## 配置环境
### 系统要求
#### 开发环境要求
- **操作系统**Windows 10/11、macOS 10.15+ 或 Linux推荐 Ubuntu 20.04+
- **Android Studio**Hedgehog (2023.1.1) 或更高版本
- **JDK**JDK 1.8 或更高版本(推荐 JDK 11 或 JDK 17
- **Gradle**8.2 或更高版本(通常随 Android Studio 自动安装)
- **Android SDK**Android SDK Platform 34 及 Build Tools 34.0.0
#### 运行环境要求
- **Android 设备**Android 7.0 (API 24) 及以上版本
- **设备功能**
- 相机功能(用于扫描二维码)
- 蓝牙功能(可选,用于大文件分享)
- 存储空间:建议至少 100MB 可用空间
### 环境配置步骤
#### 1. 安装 Android Studio
1. 访问 [Android Studio 官网](https://developer.android.com/studio) 下载最新版本
2. 运行安装程序,按照向导完成安装
3. 首次启动时,选择"Standard"安装类型,会自动下载必要的 SDK 组件
#### 2. 配置 Android SDK
1. 打开 Android Studio进入 `File``Settings`Windows/Linux`Android Studio``Preferences`macOS
2. 导航到 `Appearance & Behavior``System Settings``Android SDK`
3. 在 `SDK Platforms` 标签页中,勾选并安装:
- Android 14.0 (API 34)
- Android 7.0 (API 24) 及以上版本(用于测试兼容性)
4. 在 `SDK Tools` 标签页中,确保以下工具已安装:
- Android SDK Build-Tools 34.0.0
- Android SDK Platform-Tools
- Android SDK Command-line Tools
- Google Play services
- Intel x86 Emulator Accelerator (HAXM installer)(如果使用 Intel 处理器)
#### 3. 配置 JDK
1. 在 Android Studio 中,进入 `File``Project Structure``SDK Location`
2. 设置 `JDK location` 为已安装的 JDK 路径(推荐使用 Android Studio 自带的 JDK
3. 确保项目配置中的 `jvmTarget` 设置为 1.8(在 `app/build.gradle` 中)
#### 4. 配置 Gradle
1. 项目使用 Gradle Wrapper通常无需手动配置
2. 如需手动配置,编辑 `gradle/wrapper/gradle-wrapper.properties`,确保 `distributionUrl` 指向 Gradle 8.2 或更高版本
3. 首次同步项目时Gradle 会自动下载所需的依赖
#### 5. 配置项目本地属性
1. 检查项目根目录下的 `local.properties` 文件
2. 确保 `sdk.dir` 指向正确的 Android SDK 路径,例如:
```properties
sdk.dir=C\:\\Users\\YourUsername\\AppData\\Local\\Android\\Sdk
```
或 macOS/Linux
```properties
sdk.dir=/Users/YourUsername/Library/Android/sdk
```
#### 6. 配置网络代理(如需要)
如果在受限网络环境下,参考 `NETWORK_FIX.md` 配置 Gradle 代理:
1. 在项目根目录创建或编辑 `gradle.properties`
2. 添加代理配置:
```properties
systemProp.http.proxyHost=your.proxy.host
systemProp.http.proxyPort=8080
systemProp.https.proxyHost=your.proxy.host
systemProp.https.proxyPort=8080
```
#### 7. 验证配置
1. 打开项目后Android Studio 会自动同步 Gradle
2. 检查 `Build``Sync Project with Gradle Files` 是否成功
3. 查看 `Build` 窗口,确认没有错误
4. 运行 `./gradlew tasks`Windows: `gradlew.bat tasks`)验证 Gradle 配置
### 常见配置问题
| 问题 | 解决方案 |
| --- | --- |
| **Gradle 同步失败** | 检查网络连接,或配置代理(参考 `NETWORK_FIX.md` |
| **JDK 版本不兼容** | 确保使用 JDK 1.8 或更高版本,在 `Project Structure` 中配置正确的 JDK 路径 |
| **SDK 平台缺失** | 在 Android Studio 的 SDK Manager 中安装所需的 SDK Platform |
| **Build Tools 版本不匹配** | 在 SDK Manager 中安装对应版本的 Build Tools |
| **NDK 相关错误** | 本项目不依赖 NDK如出现相关错误可在 `local.properties` 中移除 NDK 配置 |
更多配置问题请参考:
- `JAVA_VERSION_FIX.md`JDK 版本兼容说明
- `NETWORK_FIX.md`Gradle 拉取依赖的网络排障
## 简介
Interactive Map App 面向校内导览、线下展陈、亲友旅行分享等场景提供“图片即地图”的信息承载方式。应用内置两种身份模式:
- **Author制作模式**:可新建项目、编辑标记、导出分享。由 `ProjectMode` 与主界面密码锁控制。
- **Viewer查看模式**:仅浏览地图并查看标记内容,避免误触编辑。
应用完全使用本地 Room 数据库和内部存储,不依赖任何云端或后台服务,确保数据可离线保存与传输。
## 核心特性
- **多媒体标记**`MarkerContent` 支持文字、图片、链接及音频(含时长元数据)。标记位置以 0~1 的相对坐标存储,天然适配不同分辨率。
- **蓝牙直连分享**`BluetoothSendActivity` / `BluetoothReceiveActivity` 使用 `BluetoothUtils` + `ChunkedProtocol` 实现 64KB 分片与 SHA-256 校验,兼顾大文件与稳定性。
- **离线优先**:所有素材文件保存在沙盒目录,通过 `ImageUtils` 等工具读取;接收蓝牙包即可恢复完整项目。
- **高级交互**:主界面支持网格/列表切换、拖拽排序、搜索与文件夹筛选;`ViewMapActivity` 提供双指缩放与标记动态渲染;`CreateMapActivity` 的标记在画布点按后即时生成。
- **可靠性设计**蓝牙发送内置多次重试、速度统计与用户可取消操作Room DAO 提供“安全查询”路径避免 SQLite Blob 过大导致崩溃。
## 系统架构
项目采用四层分层架构,详情见 `docs/Chapter2_Architecture.md`
1. **界面层 (UI Layer)**Activity + ViewBinding 负责交互(例如 `MainActivity`、`ViewMapActivity`、`CreateMapActivity` 等)。
2. **业务逻辑层 (Business Logic)**:以 `MapRepository`、`BluetoothUtils`、`ChunkedProtocol` 为核心,封装地图 CRUD、分享协议与跨设备通信。
3. **数据层 (Data Layer)**Room `AppDatabase`、`MapDao`、`MarkerDao`、`MapEntity`、`MarkerEntity` 持久化地图与标记。
4. **设备接口层**:封装 Android 蓝牙 API、相册/摄像头/文件访问等平台能力。
Mermaid 逻辑图(`docs/Chapter2_Architecture.md`)展示了 Activity 与 DAO/工具类之间的依赖,可作为复用或二次开发参考。
## 数据模型与持久化
- **MapEntity**:记录地图 ID、标题、背景图片路径、创建/更新时间。
- **MarkerEntity**:外键指向地图,存储相对坐标、内容类型、序列化内容及时间戳。
- **MarkerContentSerializer**:基于 Gson 的序列化/反序列化入口,可按类型扩展新的标记形态。
- **AppDatabase**Room 单库,默认落在应用私有目录。通过 `MapRepository.initializeDatabase()` 延迟注入 DAO规避 Application 阶段的初始化崩溃。
- **资源文件**:图片、音频等由 `ImageUtils` 保存到 `filesDir`,并可在分享前被转换成 data URI 嵌入包体。
## 分享能力
当前版本仅支持蓝牙 (`BluetoothSendActivity`/`BluetoothReceiveActivity`) 通道,适用于大图、大量标记或含音频的离线传输。发送端根据包体大小自动选择 GZIP 压缩包或 `ChunkedProtocol` 流式传输。
**ChunkedProtocol** 帧结构:
```
Magic(4) + Version(1)
MANIFEST 帧:包含地图元数据/标记元数据/资源索引
FILE 帧文件名、长度、sha256、内容支持背景图/标记图片/音频等)
END 帧:传输完成
```
接收端在 `BluetoothReceiveActivity` 中解析帧,调用 `ChunkedProtocol.materializeToEntities()` 将素材落地,再通过 `MapRepository.importMapData()` 入库。
## 界面与交互流程
界面设计详见 `docs/Chapter3_UI_Design.md``docs/diagrams/*.mmd`。核心流程概述:
1. **MainUI**:浏览/搜索地图、切换网格或列表、按文件夹过滤、长按拖拽排序FAB 提供创建/蓝牙入口,菜单可切换查看或编辑模式。
2. **CreateMapUI**:输入标题、选择背景图、在画布任意点添加标记,支持立即预览与跳转 `EditMarkerActivity`
3. **EditMarkerUI**:在同一界面编辑文字/图片/链接/音频,区块随内容类型切换显隐。
4. **ViewMapUI**:双指缩放背景图,点击标记弹出内容面板,可按权限进一步编辑。
5. **BluetoothSend / Receive UI**:扫描设备、展示发送/接收进度、可取消/重试。
详细的用户引导与截图参考 `USAGE.md`
## 项目结构
```
InteractiveMap/
├─ app/
│ ├─ src/main/java/com/interactivemap/app/
│ │ ├─ adapter/ # RecyclerView 与蓝牙列表适配器
│ │ ├─ data/ # Room 实体、DAO、数据库
│ │ ├─ repository/ # MapRepository 聚合 DAO
│ │ ├─ viewmodel/ # Main/Create 等 ViewModel
│ │ ├─ ui/ # 各 Activity 与 UI 片段
│ │ ├─ utils/ # Bluetooth/QR/Image/Chunk 工具
│ │ └─ model/ # ProjectMode 等枚举
│ ├─ src/main/res/ # Material 主题、布局、图标
│ └─ build.gradle # Android 模块配置
├─ docs/ # 架构与界面设计文档、Mermaid 图
├─ USAGE.md # 面向终端用户的操作指南
├─ JAVA_VERSION_FIX.md # JDK 版本兼容说明
├─ NETWORK_FIX.md # Gradle 拉取依赖的网络排障
└─ README.md # 当前文档
```
## 开发环境
### 开发工具版本
- **Android Studio**Hedgehog (2023.1.1) 或更高版本
- **Gradle**8.2 或更高版本
- **Android Gradle Plugin**`com.android.application` 8.1.4
- **Kotlin**1.9.24`jvmTarget` 1.8
- **SDK**`compileSdk 34` / `targetSdk 34` / `minSdk 24`
### 主要依赖库
#### Jetpack 组件
- **AppCompat**:兼容旧版本 Android
- **Material Design**1.8.0,提供现代化 UI 组件
- **Lifecycle**:生命周期感知组件
- **Navigation**:界面导航管理
- **Room**2.6.1,本地数据库框架
#### 第三方库
- **Glide**4.14.2,图片加载和缓存
- **PhotoView**2.3.0,图片缩放查看
- **Gson**2.10.1JSON 序列化/反序列化
- **Dexter**6.2.3,权限请求管理
- **ZXing**:二维码生成与识别库(如已集成)
### 项目配置
项目采用标准的 Android 项目结构,使用 Kotlin 作为主要开发语言,采用 MVVM 架构模式和 Room 数据库进行数据持久化。详细的技术架构说明请参考 `docs/Chapter2_Architecture.md`
## 快速开始
1. **克隆项目**`git clone` 后进入 `InteractiveMap`
2. **同步依赖**`./gradlew tasks` 或在 Android Studio 中 `Sync Project with Gradle Files`
3. **配置 SDK**:确保安装 Android 34 平台及 Build Tools 34.0.0。
4. **连接设备/模拟器**Android 7.0 (API 24) 及以上。
5. **运行调试**Android Studio 中选择 `app` 目标 -> `Run`,或执行 `./gradlew installDebug`
首启会提示蓝牙与存储权限,可在设置中重新开启。若蓝牙传输需 Android 12+ 权限,请在设置-应用-权限中授予 `附近设备`
## 构建与调试
- **编译 APK**`./gradlew assembleDebug` 或 `assembleRelease`Release 构建默认不混淆,可按需修改 `proguard-rules.pro`)。
- **单元测试**`./gradlew testDebugUnitTest`。
- **界面测试**`./gradlew connectedAndroidTest`(需真机/模拟器在线)。
- **清理缓存**Windows 用户可使用 `clean_gradle.bat`,跨平台使用 `./gradlew clean`
- **日志排查**
- 蓝牙:过滤 `BluetoothUtils`/`Send`/`Receive` Tag。
- 数据库:查看 `MapRepository`/`MainViewModel` 日志。
## 权限与隐私
- **必需权限**
- 存储/图片选择导入背景与标记资源Android 13 以上需 `READ_MEDIA_*`,可在后续版本中替换)。
- 蓝牙与附近设备:扫描、配对及数据传输。
- **隐私策略**
- 所有地图与素材仅保存在本地数据库与内部文件夹。
- 分享时数据一次性打包;接收端手动选择是否导入。
## 常见问题
| 问题 | 说明 |
| --- | --- |
| **无法搜索到对方设备** | 确认双方设备都开启可发现模式,并在 Android 12+ 设备上授予“附近设备”权限;可先点击“已配对设备”再发起扫描。 |
| **标记位置偏移** | 标记使用相对坐标,若底图宽高比被裁剪会产生视觉偏差,建议保持原始比例。 |
| **蓝牙传输失败** | 确认双方已配对并靠近,确保授予附近设备权限;发送页会显示重试状态。 |
| **编辑模式密码忘记** | 进入应用清除数据即可重置(会删除本地地图,请谨慎)。 |
更多面向终端用户的问答见 `USAGE.md`
## 配套文档
- `docs/Chapter2_Architecture.md`分层架构说明Mermaid 逻辑图。
- `docs/Chapter3_UI_Design.md`:各界面元素定义、类图、页面流。
- `docs/diagrams/*.mmd`Mermaid 源文件,可用 VS Code 或在线编辑器导出。
- `NETWORK_FIX.md`:在受限网络环境下配置 Gradle 代理的步骤。
> 欢迎根据自身业务扩展标记类型或接入新的分享通道,若新增传输方式,建议复用 `MarkerContentSerializer``ChunkedProtocol` 以保持兼容性。

@ -0,0 +1,46 @@
# 使用说明
## 快速开始
### 1. 创建你的第一张地图
1. 打开App
2. 点击右下角的"+"按钮
3. 选择一张图片作为底图
4. 输入地图标题(可选)
5. 点击"保存地图"
### 2. 添加标记点
1. 在创建地图界面,点击底图
2. 选择标记点内容类型:
- **文字**:输入文字说明
- **图片**:从相册选择图片
- **链接**:输入网址和标题
3. 点击"保存"
### 3. 生成二维码分享
1. 添加完标记点后,点击"生成二维码"
2. 可以分享二维码给其他人
3. 其他人用这个App扫描二维码就能查看你的地图
### 4. 查看他人分享的地图
1. 点击扫描按钮
2. 扫描二维码
3. 点击标记点查看内容
## 注意事项
- 标记点位置是自动排列的,不需要手动定位
- 二维码包含完整数据,建议使用较小的图片
- 支持文字、图片、链接三种内容类型
- 所有数据都保存在手机本地,无需网络
## 常见问题
**Q: 为什么标记点位置不准确?**
A: 为了简化操作,标记点位置是自动排列的,点击标记点可以查看内容。
**Q: 二维码太大扫不出来?**
A: 建议使用较小的底图,或者减少标记点数量。
**Q: 可以编辑已保存的地图吗?**
A: 可以,在主界面点击地图卡片进入编辑模式。

@ -0,0 +1,77 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
namespace 'com.interactivemap.app'
compileSdk 34
defaultConfig {
applicationId "com.interactivemap.app"
minSdk 24
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
// compileSdk Build Tools 33.0.1
buildToolsVersion '34.0.0'
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.9.0'
implementation 'androidx.activity:activity-ktx:1.9.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.5.3'
// Room database ( Kotlin 1.9.x JDK 17)
implementation 'androidx.room:room-runtime:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1'
kapt 'androidx.room:room-compiler:2.6.1'
// QR Code generation and scanning
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
implementation 'com.google.zxing:core:3.5.1'
// Image loading and processing
implementation 'com.github.bumptech.glide:glide:4.14.2'
// PhotoView for pan/zoom support
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
// JSON serialization
implementation 'com.google.code.gson:gson:2.10.1'
// Permissions
implementation 'com.karumi:dexter:6.2.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

@ -0,0 +1,125 @@
// Generated by view binder compiler. Do not edit!
package com.interactivemap.app.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.viewbinding.ViewBinding;
import androidx.viewbinding.ViewBindings;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.interactivemap.app.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityBluetoothReceiveBinding implements ViewBinding {
@NonNull
private final CoordinatorLayout rootView;
@NonNull
public final MaterialButton buttonRestart;
@NonNull
public final MaterialButton buttonStop;
@NonNull
public final LinearProgressIndicator progressReceive;
@NonNull
public final TextView textViewReceiveProgress;
@NonNull
public final TextView textViewStatus;
@NonNull
public final Toolbar toolbar;
private ActivityBluetoothReceiveBinding(@NonNull CoordinatorLayout rootView,
@NonNull MaterialButton buttonRestart, @NonNull MaterialButton buttonStop,
@NonNull LinearProgressIndicator progressReceive, @NonNull TextView textViewReceiveProgress,
@NonNull TextView textViewStatus, @NonNull Toolbar toolbar) {
this.rootView = rootView;
this.buttonRestart = buttonRestart;
this.buttonStop = buttonStop;
this.progressReceive = progressReceive;
this.textViewReceiveProgress = textViewReceiveProgress;
this.textViewStatus = textViewStatus;
this.toolbar = toolbar;
}
@Override
@NonNull
public CoordinatorLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityBluetoothReceiveBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityBluetoothReceiveBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_bluetooth_receive, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityBluetoothReceiveBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.buttonRestart;
MaterialButton buttonRestart = ViewBindings.findChildViewById(rootView, id);
if (buttonRestart == null) {
break missingId;
}
id = R.id.buttonStop;
MaterialButton buttonStop = ViewBindings.findChildViewById(rootView, id);
if (buttonStop == null) {
break missingId;
}
id = R.id.progressReceive;
LinearProgressIndicator progressReceive = ViewBindings.findChildViewById(rootView, id);
if (progressReceive == null) {
break missingId;
}
id = R.id.textViewReceiveProgress;
TextView textViewReceiveProgress = ViewBindings.findChildViewById(rootView, id);
if (textViewReceiveProgress == null) {
break missingId;
}
id = R.id.textViewStatus;
TextView textViewStatus = ViewBindings.findChildViewById(rootView, id);
if (textViewStatus == null) {
break missingId;
}
id = R.id.toolbar;
Toolbar toolbar = ViewBindings.findChildViewById(rootView, id);
if (toolbar == null) {
break missingId;
}
return new ActivityBluetoothReceiveBinding((CoordinatorLayout) rootView, buttonRestart,
buttonStop, progressReceive, textViewReceiveProgress, textViewStatus, toolbar);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}

@ -0,0 +1,168 @@
// Generated by view binder compiler. Do not edit!
package com.interactivemap.app.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.viewbinding.ViewBinding;
import androidx.viewbinding.ViewBindings;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.interactivemap.app.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityBluetoothSendBinding implements ViewBinding {
@NonNull
private final CoordinatorLayout rootView;
@NonNull
public final MaterialButton buttonCancelSend;
@NonNull
public final MaterialButton buttonSelectDevice;
@NonNull
public final LinearProgressIndicator progressBar;
@NonNull
public final TextView textViewBytes;
@NonNull
public final TextView textViewMapTitle;
@NonNull
public final TextView textViewMarkerCount;
@NonNull
public final TextView textViewProgress;
@NonNull
public final TextView textViewSpeed;
@NonNull
public final TextView textViewStatus;
@NonNull
public final Toolbar toolbar;
private ActivityBluetoothSendBinding(@NonNull CoordinatorLayout rootView,
@NonNull MaterialButton buttonCancelSend, @NonNull MaterialButton buttonSelectDevice,
@NonNull LinearProgressIndicator progressBar, @NonNull TextView textViewBytes,
@NonNull TextView textViewMapTitle, @NonNull TextView textViewMarkerCount,
@NonNull TextView textViewProgress, @NonNull TextView textViewSpeed,
@NonNull TextView textViewStatus, @NonNull Toolbar toolbar) {
this.rootView = rootView;
this.buttonCancelSend = buttonCancelSend;
this.buttonSelectDevice = buttonSelectDevice;
this.progressBar = progressBar;
this.textViewBytes = textViewBytes;
this.textViewMapTitle = textViewMapTitle;
this.textViewMarkerCount = textViewMarkerCount;
this.textViewProgress = textViewProgress;
this.textViewSpeed = textViewSpeed;
this.textViewStatus = textViewStatus;
this.toolbar = toolbar;
}
@Override
@NonNull
public CoordinatorLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityBluetoothSendBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityBluetoothSendBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_bluetooth_send, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityBluetoothSendBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.buttonCancelSend;
MaterialButton buttonCancelSend = ViewBindings.findChildViewById(rootView, id);
if (buttonCancelSend == null) {
break missingId;
}
id = R.id.buttonSelectDevice;
MaterialButton buttonSelectDevice = ViewBindings.findChildViewById(rootView, id);
if (buttonSelectDevice == null) {
break missingId;
}
id = R.id.progressBar;
LinearProgressIndicator progressBar = ViewBindings.findChildViewById(rootView, id);
if (progressBar == null) {
break missingId;
}
id = R.id.textViewBytes;
TextView textViewBytes = ViewBindings.findChildViewById(rootView, id);
if (textViewBytes == null) {
break missingId;
}
id = R.id.textViewMapTitle;
TextView textViewMapTitle = ViewBindings.findChildViewById(rootView, id);
if (textViewMapTitle == null) {
break missingId;
}
id = R.id.textViewMarkerCount;
TextView textViewMarkerCount = ViewBindings.findChildViewById(rootView, id);
if (textViewMarkerCount == null) {
break missingId;
}
id = R.id.textViewProgress;
TextView textViewProgress = ViewBindings.findChildViewById(rootView, id);
if (textViewProgress == null) {
break missingId;
}
id = R.id.textViewSpeed;
TextView textViewSpeed = ViewBindings.findChildViewById(rootView, id);
if (textViewSpeed == null) {
break missingId;
}
id = R.id.textViewStatus;
TextView textViewStatus = ViewBindings.findChildViewById(rootView, id);
if (textViewStatus == null) {
break missingId;
}
id = R.id.toolbar;
Toolbar toolbar = ViewBindings.findChildViewById(rootView, id);
if (toolbar == null) {
break missingId;
}
return new ActivityBluetoothSendBinding((CoordinatorLayout) rootView, buttonCancelSend,
buttonSelectDevice, progressBar, textViewBytes, textViewMapTitle, textViewMarkerCount,
textViewProgress, textViewSpeed, textViewStatus, toolbar);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}

@ -0,0 +1,150 @@
// Generated by view binder compiler. Do not edit!
package com.interactivemap.app.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.viewbinding.ViewBinding;
import androidx.viewbinding.ViewBindings;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.textfield.TextInputEditText;
import com.interactivemap.app.R;
import com.interactivemap.app.ui.widget.ZoomableImageView;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityCreateMapBinding implements ViewBinding {
@NonNull
private final CoordinatorLayout rootView;
@NonNull
public final MaterialButton buttonSaveMap;
@NonNull
public final MaterialButton buttonSelectBackground;
@NonNull
public final TextInputEditText editTextTitle;
@NonNull
public final ZoomableImageView imageViewBackground;
@NonNull
public final FrameLayout markerContainer;
@NonNull
public final ProgressBar progressBar;
@NonNull
public final TextView textViewSelectImage;
@NonNull
public final Toolbar toolbar;
private ActivityCreateMapBinding(@NonNull CoordinatorLayout rootView,
@NonNull MaterialButton buttonSaveMap, @NonNull MaterialButton buttonSelectBackground,
@NonNull TextInputEditText editTextTitle, @NonNull ZoomableImageView imageViewBackground,
@NonNull FrameLayout markerContainer, @NonNull ProgressBar progressBar,
@NonNull TextView textViewSelectImage, @NonNull Toolbar toolbar) {
this.rootView = rootView;
this.buttonSaveMap = buttonSaveMap;
this.buttonSelectBackground = buttonSelectBackground;
this.editTextTitle = editTextTitle;
this.imageViewBackground = imageViewBackground;
this.markerContainer = markerContainer;
this.progressBar = progressBar;
this.textViewSelectImage = textViewSelectImage;
this.toolbar = toolbar;
}
@Override
@NonNull
public CoordinatorLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityCreateMapBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityCreateMapBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_create_map, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityCreateMapBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.buttonSaveMap;
MaterialButton buttonSaveMap = ViewBindings.findChildViewById(rootView, id);
if (buttonSaveMap == null) {
break missingId;
}
id = R.id.buttonSelectBackground;
MaterialButton buttonSelectBackground = ViewBindings.findChildViewById(rootView, id);
if (buttonSelectBackground == null) {
break missingId;
}
id = R.id.editTextTitle;
TextInputEditText editTextTitle = ViewBindings.findChildViewById(rootView, id);
if (editTextTitle == null) {
break missingId;
}
id = R.id.imageViewBackground;
ZoomableImageView imageViewBackground = ViewBindings.findChildViewById(rootView, id);
if (imageViewBackground == null) {
break missingId;
}
id = R.id.markerContainer;
FrameLayout markerContainer = ViewBindings.findChildViewById(rootView, id);
if (markerContainer == null) {
break missingId;
}
id = R.id.progressBar;
ProgressBar progressBar = ViewBindings.findChildViewById(rootView, id);
if (progressBar == null) {
break missingId;
}
id = R.id.textViewSelectImage;
TextView textViewSelectImage = ViewBindings.findChildViewById(rootView, id);
if (textViewSelectImage == null) {
break missingId;
}
id = R.id.toolbar;
Toolbar toolbar = ViewBindings.findChildViewById(rootView, id);
if (toolbar == null) {
break missingId;
}
return new ActivityCreateMapBinding((CoordinatorLayout) rootView, buttonSaveMap,
buttonSelectBackground, editTextTitle, imageViewBackground, markerContainer, progressBar,
textViewSelectImage, toolbar);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}

@ -0,0 +1,312 @@
// Generated by view binder compiler. Do not edit!
package com.interactivemap.app.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.viewbinding.ViewBinding;
import androidx.viewbinding.ViewBindings;
import com.github.chrisbanes.photoview.PhotoView;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.textfield.TextInputEditText;
import com.interactivemap.app.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityEditMarkerBinding implements ViewBinding {
@NonNull
private final CoordinatorLayout rootView;
@NonNull
public final MaterialButton buttonCancel;
@NonNull
public final MaterialButton buttonDelete;
@NonNull
public final MaterialButton buttonRecordAudio;
@NonNull
public final MaterialButton buttonSave;
@NonNull
public final MaterialButton buttonSelectAudio;
@NonNull
public final MaterialButton buttonSelectImage;
@NonNull
public final TextInputEditText editTextContent;
@NonNull
public final TextInputEditText editTextLinkTitle;
@NonNull
public final TextInputEditText editTextUrl;
@NonNull
public final PhotoView imageViewContent;
@NonNull
public final LinearLayout layoutAudioContent;
@NonNull
public final LinearLayout layoutImageContent;
@NonNull
public final LinearLayout layoutLinkContent;
@NonNull
public final LinearLayout layoutTextContent;
@NonNull
public final RadioButton radioButtonAudio;
@NonNull
public final RadioButton radioButtonImage;
@NonNull
public final RadioButton radioButtonLink;
@NonNull
public final RadioButton radioButtonText;
@NonNull
public final RadioGroup radioGroupContentType;
@NonNull
public final TextView textViewAudioInfo;
@NonNull
public final TextView textViewImageHint;
@NonNull
public final TextView textViewRecordingTime;
@NonNull
public final Toolbar toolbar;
private ActivityEditMarkerBinding(@NonNull CoordinatorLayout rootView,
@NonNull MaterialButton buttonCancel, @NonNull MaterialButton buttonDelete,
@NonNull MaterialButton buttonRecordAudio, @NonNull MaterialButton buttonSave,
@NonNull MaterialButton buttonSelectAudio, @NonNull MaterialButton buttonSelectImage,
@NonNull TextInputEditText editTextContent, @NonNull TextInputEditText editTextLinkTitle,
@NonNull TextInputEditText editTextUrl, @NonNull PhotoView imageViewContent,
@NonNull LinearLayout layoutAudioContent, @NonNull LinearLayout layoutImageContent,
@NonNull LinearLayout layoutLinkContent, @NonNull LinearLayout layoutTextContent,
@NonNull RadioButton radioButtonAudio, @NonNull RadioButton radioButtonImage,
@NonNull RadioButton radioButtonLink, @NonNull RadioButton radioButtonText,
@NonNull RadioGroup radioGroupContentType, @NonNull TextView textViewAudioInfo,
@NonNull TextView textViewImageHint, @NonNull TextView textViewRecordingTime,
@NonNull Toolbar toolbar) {
this.rootView = rootView;
this.buttonCancel = buttonCancel;
this.buttonDelete = buttonDelete;
this.buttonRecordAudio = buttonRecordAudio;
this.buttonSave = buttonSave;
this.buttonSelectAudio = buttonSelectAudio;
this.buttonSelectImage = buttonSelectImage;
this.editTextContent = editTextContent;
this.editTextLinkTitle = editTextLinkTitle;
this.editTextUrl = editTextUrl;
this.imageViewContent = imageViewContent;
this.layoutAudioContent = layoutAudioContent;
this.layoutImageContent = layoutImageContent;
this.layoutLinkContent = layoutLinkContent;
this.layoutTextContent = layoutTextContent;
this.radioButtonAudio = radioButtonAudio;
this.radioButtonImage = radioButtonImage;
this.radioButtonLink = radioButtonLink;
this.radioButtonText = radioButtonText;
this.radioGroupContentType = radioGroupContentType;
this.textViewAudioInfo = textViewAudioInfo;
this.textViewImageHint = textViewImageHint;
this.textViewRecordingTime = textViewRecordingTime;
this.toolbar = toolbar;
}
@Override
@NonNull
public CoordinatorLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityEditMarkerBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityEditMarkerBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_edit_marker, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityEditMarkerBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.buttonCancel;
MaterialButton buttonCancel = ViewBindings.findChildViewById(rootView, id);
if (buttonCancel == null) {
break missingId;
}
id = R.id.buttonDelete;
MaterialButton buttonDelete = ViewBindings.findChildViewById(rootView, id);
if (buttonDelete == null) {
break missingId;
}
id = R.id.buttonRecordAudio;
MaterialButton buttonRecordAudio = ViewBindings.findChildViewById(rootView, id);
if (buttonRecordAudio == null) {
break missingId;
}
id = R.id.buttonSave;
MaterialButton buttonSave = ViewBindings.findChildViewById(rootView, id);
if (buttonSave == null) {
break missingId;
}
id = R.id.buttonSelectAudio;
MaterialButton buttonSelectAudio = ViewBindings.findChildViewById(rootView, id);
if (buttonSelectAudio == null) {
break missingId;
}
id = R.id.buttonSelectImage;
MaterialButton buttonSelectImage = ViewBindings.findChildViewById(rootView, id);
if (buttonSelectImage == null) {
break missingId;
}
id = R.id.editTextContent;
TextInputEditText editTextContent = ViewBindings.findChildViewById(rootView, id);
if (editTextContent == null) {
break missingId;
}
id = R.id.editTextLinkTitle;
TextInputEditText editTextLinkTitle = ViewBindings.findChildViewById(rootView, id);
if (editTextLinkTitle == null) {
break missingId;
}
id = R.id.editTextUrl;
TextInputEditText editTextUrl = ViewBindings.findChildViewById(rootView, id);
if (editTextUrl == null) {
break missingId;
}
id = R.id.imageViewContent;
PhotoView imageViewContent = ViewBindings.findChildViewById(rootView, id);
if (imageViewContent == null) {
break missingId;
}
id = R.id.layoutAudioContent;
LinearLayout layoutAudioContent = ViewBindings.findChildViewById(rootView, id);
if (layoutAudioContent == null) {
break missingId;
}
id = R.id.layoutImageContent;
LinearLayout layoutImageContent = ViewBindings.findChildViewById(rootView, id);
if (layoutImageContent == null) {
break missingId;
}
id = R.id.layoutLinkContent;
LinearLayout layoutLinkContent = ViewBindings.findChildViewById(rootView, id);
if (layoutLinkContent == null) {
break missingId;
}
id = R.id.layoutTextContent;
LinearLayout layoutTextContent = ViewBindings.findChildViewById(rootView, id);
if (layoutTextContent == null) {
break missingId;
}
id = R.id.radioButtonAudio;
RadioButton radioButtonAudio = ViewBindings.findChildViewById(rootView, id);
if (radioButtonAudio == null) {
break missingId;
}
id = R.id.radioButtonImage;
RadioButton radioButtonImage = ViewBindings.findChildViewById(rootView, id);
if (radioButtonImage == null) {
break missingId;
}
id = R.id.radioButtonLink;
RadioButton radioButtonLink = ViewBindings.findChildViewById(rootView, id);
if (radioButtonLink == null) {
break missingId;
}
id = R.id.radioButtonText;
RadioButton radioButtonText = ViewBindings.findChildViewById(rootView, id);
if (radioButtonText == null) {
break missingId;
}
id = R.id.radioGroupContentType;
RadioGroup radioGroupContentType = ViewBindings.findChildViewById(rootView, id);
if (radioGroupContentType == null) {
break missingId;
}
id = R.id.textViewAudioInfo;
TextView textViewAudioInfo = ViewBindings.findChildViewById(rootView, id);
if (textViewAudioInfo == null) {
break missingId;
}
id = R.id.textViewImageHint;
TextView textViewImageHint = ViewBindings.findChildViewById(rootView, id);
if (textViewImageHint == null) {
break missingId;
}
id = R.id.textViewRecordingTime;
TextView textViewRecordingTime = ViewBindings.findChildViewById(rootView, id);
if (textViewRecordingTime == null) {
break missingId;
}
id = R.id.toolbar;
Toolbar toolbar = ViewBindings.findChildViewById(rootView, id);
if (toolbar == null) {
break missingId;
}
return new ActivityEditMarkerBinding((CoordinatorLayout) rootView, buttonCancel, buttonDelete,
buttonRecordAudio, buttonSave, buttonSelectAudio, buttonSelectImage, editTextContent,
editTextLinkTitle, editTextUrl, imageViewContent, layoutAudioContent, layoutImageContent,
layoutLinkContent, layoutTextContent, radioButtonAudio, radioButtonImage, radioButtonLink,
radioButtonText, radioGroupContentType, textViewAudioInfo, textViewImageHint,
textViewRecordingTime, toolbar);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}

@ -0,0 +1,103 @@
// Generated by view binder compiler. Do not edit!
package com.interactivemap.app.databinding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.viewbinding.ViewBinding;
import androidx.viewbinding.ViewBindings;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.interactivemap.app.R;
import java.lang.NullPointerException;
import java.lang.Override;
import java.lang.String;
public final class ActivityMainBinding implements ViewBinding {
@NonNull
private final CoordinatorLayout rootView;
@NonNull
public final ContentMainBinding contentMain;
@NonNull
public final FloatingActionButton fabCreateMap;
@NonNull
public final FloatingActionButton fabScanQr;
@NonNull
public final Toolbar toolbar;
private ActivityMainBinding(@NonNull CoordinatorLayout rootView,
@NonNull ContentMainBinding contentMain, @NonNull FloatingActionButton fabCreateMap,
@NonNull FloatingActionButton fabScanQr, @NonNull Toolbar toolbar) {
this.rootView = rootView;
this.contentMain = contentMain;
this.fabCreateMap = fabCreateMap;
this.fabScanQr = fabScanQr;
this.toolbar = toolbar;
}
@Override
@NonNull
public CoordinatorLayout getRoot() {
return rootView;
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater) {
return inflate(inflater, null, false);
}
@NonNull
public static ActivityMainBinding inflate(@NonNull LayoutInflater inflater,
@Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_main, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
@NonNull
public static ActivityMainBinding bind(@NonNull View rootView) {
// The body of this method is generated in a way you would not otherwise write.
// This is done to optimize the compiled bytecode for size and performance.
int id;
missingId: {
id = R.id.content_main;
View contentMain = ViewBindings.findChildViewById(rootView, id);
if (contentMain == null) {
break missingId;
}
ContentMainBinding binding_contentMain = ContentMainBinding.bind(contentMain);
id = R.id.fab_create_map;
FloatingActionButton fabCreateMap = ViewBindings.findChildViewById(rootView, id);
if (fabCreateMap == null) {
break missingId;
}
id = R.id.fab_scan_qr;
FloatingActionButton fabScanQr = ViewBindings.findChildViewById(rootView, id);
if (fabScanQr == null) {
break missingId;
}
id = R.id.toolbar;
Toolbar toolbar = ViewBindings.findChildViewById(rootView, id);
if (toolbar == null) {
break missingId;
}
return new ActivityMainBinding((CoordinatorLayout) rootView, binding_contentMain,
fabCreateMap, fabScanQr, toolbar);
}
String missingId = rootView.getResources().getResourceName(id);
throw new NullPointerException("Missing required view with ID: ".concat(missingId));
}
}

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

Loading…
Cancel
Save