@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_3" default="true" />
|
||||
<component name="masterDetails">
|
||||
<states>
|
||||
<state key="ProjectJDKs.UI">
|
||||
<settings>
|
||||
<last-edited>1.8</last-edited>
|
||||
<splitter-proportions>
|
||||
<option name="proportions">
|
||||
<list>
|
||||
<option value="0.2" />
|
||||
</list>
|
||||
</option>
|
||||
</splitter-proportions>
|
||||
</settings>
|
||||
</state>
|
||||
</states>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/AndroidApp.iml" filepath="$PROJECT_DIR$/.idea/AndroidApp.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,188 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="dfd94b2a-6d7a-49b9-9f27-735b64fff405" name="Default" comment="" />
|
||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||
<option name="TRACKING_ENABLED" value="true" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" />
|
||||
<component name="FavoritesManager">
|
||||
<favorites_list name="CodeIris Favorites" />
|
||||
</component>
|
||||
<component name="GradleLocalSettings">
|
||||
<option name="externalProjectsViewState">
|
||||
<projects_view />
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectFrameBounds">
|
||||
<option name="x" value="-7" />
|
||||
<option name="width" value="1932" />
|
||||
<option name="height" value="1047" />
|
||||
</component>
|
||||
<component name="ProjectView">
|
||||
<navigator currentView="AndroidView" proportions="" version="1">
|
||||
<flattenPackages />
|
||||
<showMembers />
|
||||
<showModules />
|
||||
<showLibraryContents />
|
||||
<hideEmptyPackages />
|
||||
<abbreviatePackageNames />
|
||||
<autoscrollToSource />
|
||||
<autoscrollFromSource />
|
||||
<sortByType />
|
||||
<manualOrder />
|
||||
<foldersAlwaysOnTop value="true" />
|
||||
</navigator>
|
||||
<panes>
|
||||
<pane id="PackagesPane" />
|
||||
<pane id="AndroidView" />
|
||||
<pane id="Scratches" />
|
||||
<pane id="ProjectPane" />
|
||||
<pane id="Scope" />
|
||||
</panes>
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="settings.editor.selected.configurable" value="preferences.lookFeel" />
|
||||
<property name="settings.editor.splitter.proportion" value="0.25526074" />
|
||||
<property name="android.sdk.path" value="D:/ANDROID/sdk" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="RunDashboard">
|
||||
<option name="ruleStates">
|
||||
<list>
|
||||
<RuleState>
|
||||
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
|
||||
</RuleState>
|
||||
<RuleState>
|
||||
<option name="name" value="StatusDashboardGroupingRule" />
|
||||
</RuleState>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration default="true" type="Application" factoryName="Application">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<option name="MAIN_CLASS_NAME" />
|
||||
<option name="VM_PARAMETERS" />
|
||||
<option name="PROGRAM_PARAMETERS" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="ENABLE_SWING_INSPECTOR" value="false" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<module name="" />
|
||||
<envs />
|
||||
<method />
|
||||
</configuration>
|
||||
<configuration default="true" type="Remote" factoryName="Remote">
|
||||
<option name="USE_SOCKET_TRANSPORT" value="true" />
|
||||
<option name="SERVER_MODE" value="false" />
|
||||
<option name="SHMEM_ADDRESS" value="javadebug" />
|
||||
<option name="HOST" value="localhost" />
|
||||
<option name="PORT" value="5005" />
|
||||
<method />
|
||||
</configuration>
|
||||
<configuration default="true" type="TestNG" factoryName="TestNG">
|
||||
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
|
||||
<module name="" />
|
||||
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
|
||||
<option name="ALTERNATIVE_JRE_PATH" />
|
||||
<option name="SUITE_NAME" />
|
||||
<option name="PACKAGE_NAME" />
|
||||
<option name="MAIN_CLASS_NAME" />
|
||||
<option name="METHOD_NAME" />
|
||||
<option name="GROUP_NAME" />
|
||||
<option name="TEST_OBJECT" value="CLASS" />
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" />
|
||||
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
|
||||
<option name="OUTPUT_DIRECTORY" />
|
||||
<option name="ANNOTATION_TYPE" />
|
||||
<option name="ENV_VARIABLES" />
|
||||
<option name="PASS_PARENT_ENVS" value="true" />
|
||||
<option name="TEST_SEARCH_SCOPE">
|
||||
<value defaultName="singleModule" />
|
||||
</option>
|
||||
<option name="USE_DEFAULT_REPORTERS" value="false" />
|
||||
<option name="PROPERTIES_FILE" />
|
||||
<envs />
|
||||
<properties />
|
||||
<listeners />
|
||||
<method />
|
||||
</configuration>
|
||||
<configuration name="<template>" type="Applet" default="true" selected="false">
|
||||
<option name="MAIN_CLASS_NAME" />
|
||||
<option name="HTML_FILE_NAME" />
|
||||
<option name="HTML_USED" value="false" />
|
||||
<option name="WIDTH" value="400" />
|
||||
<option name="HEIGHT" value="300" />
|
||||
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy" />
|
||||
<option name="VM_PARAMETERS" />
|
||||
</configuration>
|
||||
<configuration name="<template>" type="JUnit" default="true" selected="false">
|
||||
<option name="MAIN_CLASS_NAME" />
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<option name="PARAMETERS" />
|
||||
<option name="WORKING_DIRECTORY" value="$MODULE_DIR$" />
|
||||
</configuration>
|
||||
<configuration name="<template>" type="#org.jetbrains.idea.devkit.run.PluginConfigurationType" default="true" selected="false">
|
||||
<option name="VM_PARAMETERS" value="-Xmx512m -Xms256m -XX:MaxPermSize=250m -ea" />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="ShelveChangesManager" show_recycled="false">
|
||||
<option name="remove_strategy" value="false" />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="dfd94b2a-6d7a-49b9-9f27-735b64fff405" name="Default" comment="" />
|
||||
<created>1524884716802</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1524884716802</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="ToolWindowManager">
|
||||
<frame x="-7" y="0" width="1932" height="1047" extended-state="0" />
|
||||
<layout>
|
||||
<window_info id="PlantUML" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="FindBugs-IDEA" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Palette	" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Image Layers" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Capture Analysis" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
|
||||
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Capture Tool" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Designer" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Project" active="true" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.24959914" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
|
||||
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Theme Preview" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" />
|
||||
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Code Iris" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Captures" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
|
||||
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||
</layout>
|
||||
</component>
|
||||
<component name="VcsContentAnnotationSettings">
|
||||
<option name="myLimit" value="2678400000" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager />
|
||||
<watches-manager />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<resourceExtensions />
|
||||
<wildcardResourcePatterns>
|
||||
<entry name="!?*.java" />
|
||||
<entry name="!?*.form" />
|
||||
<entry name="!?*.class" />
|
||||
<entry name="!?*.groovy" />
|
||||
<entry name="!?*.scala" />
|
||||
<entry name="!?*.flex" />
|
||||
<entry name="!?*.kt" />
|
||||
<entry name="!?*.clj" />
|
||||
<entry name="!?*.aj" />
|
||||
</wildcardResourcePatterns>
|
||||
<annotationProcessing>
|
||||
<profile default="true" name="Default" enabled="false">
|
||||
<processorPath useClasspath="true" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,3 @@
|
||||
<component name="CopyrightManager">
|
||||
<settings default="" />
|
||||
</component>
|
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="LOCAL" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="D:\programs\Android\Android Studio\gradle\gradle-2.14.1" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EntryPointsManager">
|
||||
<entry_points version="2.0" />
|
||||
</component>
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="JDK" 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,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/Tbot.iml" filepath="$PROJECT_DIR$/Tbot.iml" />
|
||||
<module fileurl="file://C:\Users\wshuo\Documents\se15\src\AndroidApp\Tbot\Tbot.iml" filepath="C:\Users\wshuo\Documents\se15\src\AndroidApp\Tbot\Tbot.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/Tbot-app.iml" filepath="$PROJECT_DIR$/app/Tbot-app.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1 @@
|
||||
/build
|
@ -0,0 +1,33 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
buildToolsVersion "26.0.2"
|
||||
defaultConfig {
|
||||
applicationId "me.wshuo.tbot"
|
||||
minSdkVersion 18
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||
exclude group: 'com.android.support', module: 'support-annotations'
|
||||
})
|
||||
compile 'com.android.support:appcompat-v7:26.1.0'
|
||||
compile 'com.android.support:design:26.1.0'
|
||||
compile 'com.android.support:support-v4:26.1.0'
|
||||
compile 'com.android.support:support-vector-drawable:26.1.0'
|
||||
testCompile 'junit:junit:4.12'
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in D:\programs\Android\sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
@ -0,0 +1,27 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumentation test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext(){
|
||||
throw new MyOwnRuntimeException("My Message"); //write by ljw
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("me.wshuo.tbot", appContext.getPackageName());
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="me.wshuo.tbot">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="空巢老人看护小助手"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".Login">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".User"
|
||||
android:label="个人中心" />
|
||||
<activity
|
||||
android:name=".Register"
|
||||
android:label="用户注册" />
|
||||
<activity
|
||||
android:name=".Resetpwd"
|
||||
android:label="密码修改" />
|
||||
<activity android:name=".Welcome" />
|
||||
<activity android:name=".IPConnect" />
|
||||
<activity android:name=".Menu" />
|
||||
<activity android:name=".Falldown" />
|
||||
<activity android:name=".AudioCall" />
|
||||
<activity android:name=".MotionControl" />
|
||||
<activity android:name=".PreferenceSettings" />
|
||||
<activity android:name=".VideoObserve" />
|
||||
<activity android:name=".MedicineAlert" />
|
||||
<activity
|
||||
android:name=".Settings"
|
||||
android:label="@string/title_activity_settings"></activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,109 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
|
||||
* to be used with AppCompat.
|
||||
*/
|
||||
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
|
||||
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
getDelegate().installViewFactory();
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
getDelegate().onPostCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public ActionBar getSupportActionBar() {
|
||||
return getDelegate().getSupportActionBar();
|
||||
}
|
||||
|
||||
public void setSupportActionBar(@Nullable Toolbar toolbar) {
|
||||
getDelegate().setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuInflater getMenuInflater() {
|
||||
return getDelegate().getMenuInflater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(@LayoutRes int layoutResID) {
|
||||
getDelegate().setContentView(layoutResID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
getDelegate().setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().addContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
getDelegate().onPostResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTitleChanged(CharSequence title, int color) {
|
||||
super.onTitleChanged(title, color);
|
||||
getDelegate().setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
getDelegate().onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getDelegate().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
getDelegate().onDestroy();
|
||||
}
|
||||
|
||||
public void invalidateOptionsMenu() {
|
||||
getDelegate().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private AppCompatDelegate getDelegate() {
|
||||
if (mDelegate == null) {
|
||||
mDelegate = AppCompatDelegate.create(this, null);
|
||||
}
|
||||
return mDelegate;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class AudioCall extends AppCompatActivity {
|
||||
|
||||
public Button btnAudioReturn;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_audio_call);
|
||||
btnAudioReturn = (Button) findViewById(R.id.btnAudioReturn);
|
||||
btnAudioReturn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent_Audio_to_Menu = new Intent(AudioCall.this, Menu.class);
|
||||
startActivity(intent_Audio_to_Menu);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {//当点击后退键时执行该函数
|
||||
|
||||
super.onBackPressed();
|
||||
Intent intent_Audio_to_Menu = new Intent(AudioCall.this, Menu.class);
|
||||
startActivity(intent_Audio_to_Menu);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class Falldown extends AppCompatActivity {
|
||||
|
||||
public Button btnFallReturn;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_falldown);
|
||||
btnFallReturn = (Button) findViewById(R.id.btnFallReturn);
|
||||
btnFallReturn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent_Fall_to_Menu = new Intent(Falldown.this, Menu.class);
|
||||
startActivity(intent_Fall_to_Menu);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {//当点击后退键时执行该函数
|
||||
|
||||
super.onBackPressed();
|
||||
Intent intent_Fall_to_Menu = new Intent(Falldown.this, Menu.class);
|
||||
startActivity(intent_Fall_to_Menu);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class IPConnect extends AppCompatActivity {
|
||||
|
||||
public Button btnIPConnect;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_ipconnect);
|
||||
btnIPConnect = (Button) findViewById(R.id.btnIPConnect);
|
||||
btnIPConnect.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent_IP_to_Menu = new Intent(IPConnect.this, Menu.class);
|
||||
startActivity(intent_IP_to_Menu);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {//当点击后退键时执行该函数
|
||||
|
||||
super.onBackPressed();
|
||||
Intent intent_IP_to_Menu = new Intent(IPConnect.this, Menu.class);
|
||||
startActivity(intent_IP_to_Menu);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class Login extends Activity { //登录界面活动
|
||||
|
||||
public int pwdresetFlag=0;
|
||||
private EditText mAccount; //用户名编辑
|
||||
private EditText mPwd; //密码编辑
|
||||
private Button mRegisterButton; //注册按钮
|
||||
private Button mLoginButton; //登录按钮
|
||||
private Button mCancleButton; //注销按钮
|
||||
private CheckBox mRememberCheck;
|
||||
|
||||
private SharedPreferences login_sp;
|
||||
private String userNameValue,passwordValue;
|
||||
|
||||
private View loginView; //登录
|
||||
private View loginSuccessView;
|
||||
private TextView loginSuccessShow;
|
||||
private TextView mChangepwdText;
|
||||
private UserDataManager mUserDataManager; //用户数据管理类
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.login);
|
||||
//通过id找到相应的控件
|
||||
mAccount = (EditText) findViewById(R.id.login_edit_account);
|
||||
mPwd = (EditText) findViewById(R.id.login_edit_pwd);
|
||||
mRegisterButton = (Button) findViewById(R.id.login_btn_register);
|
||||
mLoginButton = (Button) findViewById(R.id.login_btn_login);
|
||||
mCancleButton = (Button) findViewById(R.id.login_btn_cancle);
|
||||
loginView=findViewById(R.id.login_view);
|
||||
loginSuccessView=findViewById(R.id.login_success_view);
|
||||
loginSuccessShow=(TextView) findViewById(R.id.login_success_show);
|
||||
|
||||
mChangepwdText = (TextView) findViewById(R.id.login_text_change_pwd);
|
||||
|
||||
mRememberCheck = (CheckBox) findViewById(R.id.Login_Remember);
|
||||
|
||||
login_sp = getSharedPreferences("userInfo", 0);
|
||||
//String name=login_sp.getString("USER_NAME", "");
|
||||
String pwd = getEncryptedPass();//write by ljw
|
||||
String pwd =login_sp.getString("PASSWORD", "");
|
||||
|
||||
boolean choseRemember =login_sp.getBoolean("mRememberCheck", false);
|
||||
boolean choseAutoLogin =login_sp.getBoolean("mAutologinCheck", false);
|
||||
//如果上次选了记住密码,那进入登录页面也自动勾选记住密码,并填上用户名和密码
|
||||
if(choseRemember){
|
||||
mAccount.setText(name);
|
||||
mPwd.setText(pwd);
|
||||
mRememberCheck.setChecked(true);
|
||||
}
|
||||
|
||||
mRegisterButton.setOnClickListener(mListener); //采用OnClickListener方法设置不同按钮按下之后的监听事件
|
||||
mLoginButton.setOnClickListener(mListener);
|
||||
mCancleButton.setOnClickListener(mListener);
|
||||
mChangepwdText.setOnClickListener(mListener);
|
||||
|
||||
ImageView image = (ImageView) findViewById(R.id.logo); //使用ImageView显示logo
|
||||
image.setImageResource(R.drawable.logo);
|
||||
|
||||
if (mUserDataManager == null) {
|
||||
mUserDataManager = new UserDataManager(this);
|
||||
mUserDataManager.openDataBase(); //建立本地数据库
|
||||
}
|
||||
}
|
||||
OnClickListener mListener = new OnClickListener() { //不同按钮按下的监听事件选择
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.login_btn_register: //登录界面的注册按钮
|
||||
Intent intent_Login_to_Register = new Intent(Login.this,Register.class) ; //切换Login Activity至User Activity
|
||||
startActivity(intent_Login_to_Register);
|
||||
finish();
|
||||
break;
|
||||
case R.id.login_btn_login: //登录界面的登录按钮
|
||||
login();
|
||||
break;
|
||||
case R.id.login_btn_cancle: //登录界面的注销按钮
|
||||
cancel();
|
||||
break;
|
||||
case R.id.login_text_change_pwd: //登录界面的密码重置按钮
|
||||
Intent intent_Login_to_reset = new Intent(Login.this,Resetpwd.class) ; //切换Login Activity至User Activity
|
||||
startActivity(intent_Login_to_reset);
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void login() {//登录按钮监听事件
|
||||
if (isUserNameAndPwdValid()) {
|
||||
String userName = mAccount.getText().toString().trim();//获取当前输入的用户名和密码信息
|
||||
String userPwd = mPwd.getText().toString().trim();
|
||||
SharedPreferences.Editor editor =login_sp.edit();
|
||||
int result=mUserDataManager.findUserByNameAndPwd(userName, userPwd);
|
||||
if(result==1){//返回1说明用户名和密码均正确
|
||||
//保存用户名和密码
|
||||
editor.putString("USER_NAME", userName);
|
||||
editor.putString("PASSWORD", userPwd);
|
||||
//是否记住密码
|
||||
if(mRememberCheck.isChecked()){
|
||||
editor.putBoolean("mRememberCheck", true);
|
||||
}else{
|
||||
editor.putBoolean("mRememberCheck", false);
|
||||
}
|
||||
editor.apply();
|
||||
Intent intent = new Intent(Login.this,Welcome.class) ;//切换Login Activity至User Activity
|
||||
startActivity(intent);
|
||||
finish();
|
||||
Toast.makeText(this, getString(R.string.login_success),Toast.LENGTH_SHORT).show();//登录成功提示
|
||||
}else if(result==0){
|
||||
Toast.makeText(this, getString(R.string.login_fail),Toast.LENGTH_SHORT).show(); //登录失败提示
|
||||
}
|
||||
}
|
||||
}
|
||||
public void cancel() { //注销
|
||||
if (isUserNameAndPwdValid()) {
|
||||
String userName = mAccount.getText().toString().trim(); //获取当前输入的用户名和密码信息
|
||||
String userPwd = mPwd.getText().toString().trim();
|
||||
int result=mUserDataManager.findUserByNameAndPwd(userName, userPwd);
|
||||
if(result==1){ //返回1说明用户名和密码均正确
|
||||
// Intent intent = new Intent(Login.this,User.class) ; //切换Login Activity至User Activity
|
||||
// startActivity(intent);
|
||||
Toast.makeText(this, getString(R.string.cancel_success),Toast.LENGTH_SHORT).show();//登录成功提示
|
||||
mPwd.setText("");
|
||||
mAccount.setText("");
|
||||
mUserDataManager.deleteUserDatabyname(userName);
|
||||
}else if(result==0){
|
||||
Toast.makeText(this, getString(R.string.cancel_fail),Toast.LENGTH_SHORT).show(); //登录失败提示
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isUserNameAndPwdValid() {
|
||||
if (mAccount.getText().toString().trim().equals("")) {
|
||||
Toast.makeText(this, getString(R.string.account_empty),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
} else if (mPwd.getText().toString().trim().equals("")) {
|
||||
Toast.makeText(this, getString(R.string.pwd_empty),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
if (mUserDataManager == null) {
|
||||
mUserDataManager = new UserDataManager(this);
|
||||
mUserDataManager.openDataBase();
|
||||
}
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (mUserDataManager != null) {
|
||||
mUserDataManager.closeDataBase();
|
||||
mUserDataManager = null;
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class MedicineAlert extends AppCompatActivity {
|
||||
|
||||
public Button btnAlertReturn;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_medicine_alert);
|
||||
btnAlertReturn = (Button) findViewById(R.id.btnAlertReturn);
|
||||
btnAlertReturn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent_Alert_to_Menu = new Intent(MedicineAlert.this, Menu.class);
|
||||
startActivity(intent_Alert_to_Menu);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void onBackPressed() {//当点击后退键时执行该函数
|
||||
|
||||
super.onBackPressed();
|
||||
Intent intent_Alert_to_Menu = new Intent(MedicineAlert.this, Menu.class);
|
||||
startActivity(intent_Alert_to_Menu);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class Menu extends AppCompatActivity {
|
||||
|
||||
//write by ljw 没必要改
|
||||
public Button btnMenuReturn;
|
||||
public Button getBtnMenuReturn(){return btnMenuReturn;}
|
||||
public void setBtnMenuReturn(Button btnMenuReturn){this.btnMenuReturn=btnMenuReturn;}
|
||||
|
||||
public Button btnFall;
|
||||
public Button getBtnFall(){return btnFall;}
|
||||
public void setBtnFall(Button btnFall){this.btnFall=btnFall;}
|
||||
|
||||
public Button btnAlert;
|
||||
public Button getBtnAlert(){return btnAlert;}
|
||||
public void setBtnAlert(Button btnAlert){this.btnAlert=btnAlert;}
|
||||
|
||||
public Button btnAudio;
|
||||
public Button getBtnAudio(){return btnAudio;}
|
||||
public void setBtnAudio(Button btnAudio){this.btnAudio=btnAudio;}
|
||||
|
||||
public Button btnVideo;
|
||||
public Button getBtnVideo(){return btnVideo;}
|
||||
public void setBtnVideo(Button btnVideo){this.btnVideo=btnVideo;}
|
||||
|
||||
public Button btnSetting;
|
||||
public Button getBtnSetting(){return btnSetting;}
|
||||
public void setBtnSetting(Button btnSetting){this.btnSetting=btnSetting;}
|
||||
|
||||
public Button btnControl;
|
||||
public Button getBtnControl(){return btnControl;}
|
||||
public void setBtnControl(Button btnControl){this.btnControl=btnControl;}
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_menu);
|
||||
btnMenuReturn = (Button) findViewById(R.id.btnMenuReturn);
|
||||
btnFall = (Button)findViewById(R.id.btnFall);
|
||||
btnAlert = (Button)findViewById(R.id.btnAlert);
|
||||
btnAudio = (Button)findViewById(R.id.btnAudio);
|
||||
btnVideo = (Button)findViewById(R.id.btnVideo);
|
||||
btnSetting = (Button)findViewById(R.id.btnSetting);
|
||||
btnControl = (Button)findViewById(R.id.btnControl);
|
||||
|
||||
btnMenuReturn.setOnClickListener(mListener);
|
||||
btnFall.setOnClickListener(mListener);
|
||||
btnAlert.setOnClickListener(mListener);
|
||||
btnAudio.setOnClickListener(mListener);
|
||||
btnVideo.setOnClickListener(mListener);
|
||||
btnSetting.setOnClickListener(mListener);
|
||||
btnControl.setOnClickListener(mListener);
|
||||
}
|
||||
|
||||
View.OnClickListener mListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.btnMenuReturn:
|
||||
Intent intent_Return = new Intent(Menu.this, IPConnect.class);
|
||||
startActivity(intent_Return);
|
||||
finish();
|
||||
break;
|
||||
case R.id.btnFall:
|
||||
Intent intent_Falldown = new Intent(Menu.this, Falldown.class);
|
||||
startActivity(intent_Falldown);
|
||||
finish();
|
||||
break;
|
||||
case R.id.btnAlert:
|
||||
Intent intent_Alert = new Intent(Menu.this, MedicineAlert.class);
|
||||
startActivity(intent_Alert);
|
||||
finish();
|
||||
break;
|
||||
case R.id.btnAudio:
|
||||
Intent intent_Audio = new Intent(Menu.this, AudioCall.class);
|
||||
startActivity(intent_Audio);
|
||||
finish();
|
||||
break;
|
||||
case R.id.btnVideo:
|
||||
Intent intent_Video = new Intent(Menu.this, VideoObserve.class);
|
||||
startActivity(intent_Video);
|
||||
finish();
|
||||
break;
|
||||
case R.id.btnSetting:
|
||||
Intent intent_Setting = new Intent(Menu.this, Settings.class);
|
||||
startActivity(intent_Setting);
|
||||
finish();
|
||||
break;
|
||||
case R.id.btnControl:
|
||||
Intent intent_Control = new Intent(Menu.this, MotionControl.class);
|
||||
startActivity(intent_Control);
|
||||
finish();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class MotionControl extends AppCompatActivity {
|
||||
|
||||
public Button btnMotionReturn;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_motion_control);
|
||||
btnMotionReturn = (Button) findViewById(R.id.btnMotionReturn);
|
||||
btnMotionReturn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent_Motion_to_Menu = new Intent(MotionControl.this, Menu.class);
|
||||
startActivity(intent_Motion_to_Menu);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void onBackPressed() {//当点击后退键时执行该函数
|
||||
|
||||
super.onBackPressed();
|
||||
Intent intent_Motion_to_Menu = new Intent(MotionControl.this, Menu.class);
|
||||
startActivity(intent_Motion_to_Menu);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class PreferenceSettings extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_preference_settings);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class Register extends AppCompatActivity {
|
||||
private EditText mAccount; //用户名编辑
|
||||
private EditText mPwd; //密码编辑
|
||||
private EditText mPwdCheck; //密码编辑
|
||||
private Button mSureButton; //确定按钮
|
||||
private Button mCancelButton; //取消按钮
|
||||
private UserDataManager mUserDataManager; //用户数据管理类
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.register);
|
||||
mAccount = (EditText) findViewById(R.id.resetpwd_edit_name);
|
||||
mPwd = (EditText) findViewById(R.id.resetpwd_edit_pwd_old);
|
||||
mPwdCheck = (EditText) findViewById(R.id.resetpwd_edit_pwd_new);
|
||||
|
||||
mSureButton = (Button) findViewById(R.id.register_btn_sure);
|
||||
mCancelButton = (Button) findViewById(R.id.register_btn_cancel);
|
||||
|
||||
mSureButton.setOnClickListener(m_register_Listener); //注册界面两个按钮的监听事件
|
||||
mCancelButton.setOnClickListener(m_register_Listener);
|
||||
|
||||
if (mUserDataManager == null) {
|
||||
mUserDataManager = new UserDataManager(this);
|
||||
mUserDataManager.openDataBase(); //建立本地数据库
|
||||
}
|
||||
|
||||
}
|
||||
View.OnClickListener m_register_Listener = new View.OnClickListener() { //不同按钮按下的监听事件选择
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.register_btn_sure: //确认按钮的监听事件
|
||||
register_check();
|
||||
break;
|
||||
case R.id.register_btn_cancel: //取消按钮的监听事件,由注册界面返回登录界面
|
||||
Intent intent_Register_to_Login = new Intent(Register.this,Login.class) ; //切换User Activity至Login Activity
|
||||
startActivity(intent_Register_to_Login);
|
||||
finish();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
public void register_check() { //确认按钮的监听事件
|
||||
if (isUserNameAndPwdValid()) {
|
||||
String userName = mAccount.getText().toString().trim();
|
||||
String userPwd = mPwd.getText().toString().trim();
|
||||
String userPwdCheck = mPwdCheck.getText().toString().trim();
|
||||
//检查用户是否存在
|
||||
int count=mUserDataManager.findUserByName(userName);
|
||||
//用户已经存在时返回,给出提示文字
|
||||
if(count>0){
|
||||
Toast.makeText(this, getString(R.string.name_already_exist, userName),Toast.LENGTH_SHORT).show();
|
||||
return ;
|
||||
}
|
||||
if(userPwd.equals(userPwdCheck)==false){ //两次密码输入不一样
|
||||
Toast.makeText(this, getString(R.string.pwd_not_the_same),Toast.LENGTH_SHORT).show();
|
||||
return ;
|
||||
} else {
|
||||
UserData mUser = new UserData(userName, userPwd);
|
||||
mUserDataManager.openDataBase();
|
||||
long flag = mUserDataManager.insertUserData(mUser); //新建用户信息
|
||||
if (flag == -1) {
|
||||
Toast.makeText(this, getString(R.string.register_fail),Toast.LENGTH_SHORT).show();
|
||||
}else{
|
||||
Toast.makeText(this, getString(R.string.register_success),Toast.LENGTH_SHORT).show();
|
||||
Intent intent_Register_to_Login = new Intent(Register.this,Login.class) ; //切换User Activity至Login Activity
|
||||
startActivity(intent_Register_to_Login);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public boolean isUserNameAndPwdValid() {
|
||||
//write by ljw
|
||||
String myString = null;
|
||||
if (mAccount.getText().toString().trim().equals(myString)) {
|
||||
Toast.makeText(this, getString(R.string.account_empty),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
} else if (mPwd.getText().toString().trim().equals(myString)) {
|
||||
Toast.makeText(this, getString(R.string.pwd_empty),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}else if(mPwdCheck.getText().toString().trim().equals(myString)) {
|
||||
Toast.makeText(this, getString(R.string.pwd_check_empty),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,283 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.RingtonePreference;
|
||||
import android.text.TextUtils;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link PreferenceActivity} that presents a set of application settings. On
|
||||
* handset devices, settings are presented as a single list. On tablets,
|
||||
* settings are split by category, with category headers shown to the left of
|
||||
* the list of settings.
|
||||
* <p>
|
||||
* See <a href="http://developer.android.com/design/patterns/settings.html">
|
||||
* Android Design: Settings</a> for design guidelines and the <a
|
||||
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
|
||||
* API Guide</a> for more information on developing a Settings UI.
|
||||
*/
|
||||
public class Settings extends AppCompatPreferenceActivity {
|
||||
/**
|
||||
* A preference value change listener that updates the preference's summary
|
||||
* to reflect its new value.
|
||||
*/
|
||||
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object value) {
|
||||
String stringValue = value.toString();
|
||||
|
||||
if (preference instanceof ListPreference) {
|
||||
// For list preferences, look up the correct display value in
|
||||
// the preference's 'entries' list.
|
||||
ListPreference listPreference = (ListPreference) preference;
|
||||
int index = listPreference.findIndexOfValue(stringValue);
|
||||
|
||||
// Set the summary to reflect the new value.
|
||||
preference.setSummary(
|
||||
index >= 0
|
||||
? listPreference.getEntries()[index]
|
||||
: null);
|
||||
|
||||
} else if (preference instanceof RingtonePreference) {
|
||||
// For ringtone preferences, look up the correct display value
|
||||
// using RingtoneManager.
|
||||
if (TextUtils.isEmpty(stringValue)) {
|
||||
// Empty values correspond to 'silent' (no ringtone).
|
||||
preference.setSummary(R.string.pref_ringtone_silent);
|
||||
|
||||
} else {
|
||||
Ringtone ringtone = RingtoneManager.getRingtone(
|
||||
preference.getContext(), Uri.parse(stringValue));
|
||||
|
||||
if (ringtone == null) {
|
||||
// Clear the summary if there was a lookup error.
|
||||
preference.setSummary(null);
|
||||
} else {
|
||||
// Set the summary to reflect the new ringtone display
|
||||
// name.
|
||||
String name = ringtone.getTitle(preference.getContext());
|
||||
preference.setSummary(name);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// For all other preferences, set the summary to the value's
|
||||
// simple string representation.
|
||||
preference.setSummary(stringValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper method to determine if the device has an extra-large screen. For
|
||||
* example, 10" tablets are extra-large.
|
||||
*/
|
||||
private static boolean isXLargeTablet(Context context) {
|
||||
return (context.getResources().getConfiguration().screenLayout
|
||||
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a preference's summary to its value. More specifically, when the
|
||||
* preference's value is changed, its summary (line of text below the
|
||||
* preference title) is updated to reflect the value. The summary is also
|
||||
* immediately updated upon calling this method. The exact display format is
|
||||
* dependent on the type of preference.
|
||||
*
|
||||
* @see #sBindPreferenceSummaryToValueListener
|
||||
*/
|
||||
private static void bindPreferenceSummaryToValue(Preference preference) {
|
||||
// Set the listener to watch for value changes.
|
||||
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||
|
||||
// Trigger the listener immediately with the preference's
|
||||
// current value.
|
||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(preference.getContext())
|
||||
.getString(preference.getKey(), ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setupActionBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the {@link android.app.ActionBar}, if the API is available.
|
||||
*/
|
||||
private void setupActionBar() {
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
// Show the Up button in the action bar.
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean onIsMultiPane() {
|
||||
return isXLargeTablet(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void onBuildHeaders(List<Header> target) {
|
||||
loadHeadersFromResource(R.xml.pref_headers, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method stops fragment injection in malicious applications.
|
||||
* Make sure to deny any unknown fragments here.
|
||||
*/
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
//write by ljw
|
||||
if (PreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
if (GeneralPreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
if (DataSyncPreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
if (NotificationPreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
/*
|
||||
return PreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| GeneralPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| DataSyncPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| NotificationPreferenceFragment.class.getName().equals(fragmentName);
|
||||
*/
|
||||
}
|
||||
/* if (PreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
if (GeneralPreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
if (DataSyncPreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
if (NotificationPreferenceFragment.equals(fragmentName.class().getName()))
|
||||
return true;
|
||||
write by ljw
|
||||
*/
|
||||
/**
|
||||
* This fragment shows general preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class GeneralPreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_general);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// to their values. When their values change, their summaries are
|
||||
// updated to reflect the new value, per the Android Design
|
||||
// guidelines.
|
||||
bindPreferenceSummaryToValue(findPreference("example_text"));
|
||||
bindPreferenceSummaryToValue(findPreference("example_list"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
startActivity(new Intent(getActivity(), Settings.class));
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows notification preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class NotificationPreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_notification);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// to their values. When their values change, their summaries are
|
||||
// updated to reflect the new value, per the Android Design
|
||||
// guidelines.
|
||||
bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
startActivity(new Intent(getActivity(), Settings.class));
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows data and sync preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class DataSyncPreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_data_sync);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// to their values. When their values change, their summaries are
|
||||
// updated to reflect the new value, per the Android Design
|
||||
// guidelines.
|
||||
bindPreferenceSummaryToValue(findPreference("sync_frequency"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
startActivity(new Intent(getActivity(), Settings.class));
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {//当点击后退键时执行该函数
|
||||
|
||||
super.onBackPressed();
|
||||
Intent intent_Setting_to_Menu = new Intent(Settings.this, Menu.class);
|
||||
startActivity(intent_Setting_to_Menu);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class User extends AppCompatActivity {
|
||||
private Button mReturnButton; //没有使用到
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.user);
|
||||
mReturnButton = (Button)findViewById(R.id.returnback);
|
||||
|
||||
}
|
||||
public void back_to_login(View view) {
|
||||
//setContentView(R.layout.login);
|
||||
Intent intent3 = new Intent(User.this,Login.class) ;
|
||||
startActivity(intent3);
|
||||
finish();
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package me.wshuo.tbot;
|
||||
/**
|
||||
* Created by FoolishFan on 2016/7/14.
|
||||
*/
|
||||
|
||||
public class UserData {
|
||||
private String userName; //用户名
|
||||
private String userPwd; //用户密码
|
||||
private int userId; //用户ID号
|
||||
public int pwdresetFlag=0;
|
||||
//获取用户名
|
||||
public String getUserName() { //获取用户名
|
||||
return userName;
|
||||
}
|
||||
//设置用户名
|
||||
public void setUserName(String userName) { //输入用户名
|
||||
this.userName = userName;
|
||||
}
|
||||
//获取用户密码
|
||||
public String getUserPwd() { //获取用户密码
|
||||
return userPwd;
|
||||
}
|
||||
//设置用户密码
|
||||
public void setUserPwd(String userPwd) { //输入用户密码
|
||||
this.userPwd = userPwd;
|
||||
}
|
||||
//获取用户id
|
||||
public int getUserId() { //获取用户ID号
|
||||
return userId;
|
||||
}
|
||||
//设置用户id
|
||||
public void setUserId(int userId) { //设置用户ID号
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
/* public UserData(String userName, String userPwd, int userId) { //用户信息
|
||||
super();
|
||||
this.userName = userName;
|
||||
this.userPwd = userPwd;
|
||||
this.userId = userId;
|
||||
}*/
|
||||
|
||||
public UserData(String userName, String userPwd) { //这里只采用用户名和密码
|
||||
super();
|
||||
this.userName = userName;
|
||||
this.userPwd = userPwd;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class VideoObserve extends AppCompatActivity {
|
||||
|
||||
public Button btnVideoReturn;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_video_observe);
|
||||
btnVideoReturn = (Button)findViewById(R.id.btnVideoReturn);
|
||||
btnVideoReturn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent_Video_to_Menu = new Intent(VideoObserve.this, Menu.class);
|
||||
startActivity(intent_Video_to_Menu);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {//当点击后退键时执行该函数
|
||||
|
||||
super.onBackPressed();
|
||||
Intent intent_Video_to_Menu = new Intent(VideoObserve.this, Menu.class);
|
||||
startActivity(intent_Video_to_Menu);
|
||||
finish();
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
public class Welcome extends AppCompatActivity {
|
||||
|
||||
public Button btnWelcome;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_welcome);
|
||||
btnWelcome = (Button)findViewById(R.id.btnWelcome);
|
||||
btnWelcome.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent_Welcome_to_IPConnect = new Intent(Welcome.this, IPConnect.class);
|
||||
startActivity(intent_Welcome_to_IPConnect);
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape>
|
||||
<gradient android:startColor="#FFFF00" android:endColor="#C0C0C0"
|
||||
android:angle="0"/>
|
||||
<stroke android:width="4dip" android:color="#b0000000"/>
|
||||
<corners android:radius="15dp"/>
|
||||
<padding android:left="10dp" android:top="10dp" android:right="10dp"
|
||||
android:bottom="10dp"/>
|
||||
|
||||
</shape>
|
||||
|
||||
</item>
|
||||
<item android:state_focused="true">
|
||||
<shape>
|
||||
<gradient android:startColor="#FFFFFFFF" android:endColor="#00000000"
|
||||
android:angle="270"/>
|
||||
<stroke android:width="4dip" android:color="#00000000"/>
|
||||
<corners android:radius="15dp"/>
|
||||
<padding android:left="10dp" android:top="10dp" android:right="10dp"
|
||||
android:bottom="10dp"/>
|
||||
|
||||
</shape>
|
||||
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<gradient android:startColor="#FFFFFFFF" android:endColor="#00000000"
|
||||
android:angle="90"/>
|
||||
<stroke android:width="4dip" android:color="#00000000"/>
|
||||
<corners android:radius="15dp"/>
|
||||
<padding android:left="10dp" android:top="10dp" android:right="10dp"
|
||||
android:bottom="10dp"/>
|
||||
|
||||
</shape>
|
||||
|
||||
</item>
|
||||
</selector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z" />
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z" />
|
||||
</vector>
|
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-.25 1.97,-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01.25,-1.97.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z" />
|
||||
</vector>
|
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="me.wshuo.tbot.AudioCall">
|
||||
|
||||
|
||||
|
||||
<TextView
|
||||
android:text="TextView"
|
||||
android:layout_width="75dp"
|
||||
android:layout_height="25dp"
|
||||
android:id="@+id/textView10"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
tools:text="语音通信" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="250dp"
|
||||
android:id="@+id/imageView"
|
||||
android:layout_alignWithParentIfMissing="false"
|
||||
android:background="@android:color/background_dark"
|
||||
android:layout_marginTop="27dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true" />
|
||||
|
||||
<Button
|
||||
android:text="拨打"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="18dp"
|
||||
android:layout_marginStart="18dp"
|
||||
android:layout_marginTop="34dp"
|
||||
android:id="@+id/button12"
|
||||
android:elevation="0dp"
|
||||
android:background="@android:color/holo_green_light"
|
||||
android:layout_below="@+id/imageView"
|
||||
android:layout_alignLeft="@+id/imageView"
|
||||
android:layout_alignStart="@+id/imageView" />
|
||||
|
||||
<Button
|
||||
android:text="挂断"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button13"
|
||||
android:background="@android:color/holo_red_light"
|
||||
android:layout_alignBaseline="@+id/button12"
|
||||
android:layout_alignBottom="@+id/button12"
|
||||
android:layout_alignRight="@+id/imageView"
|
||||
android:layout_alignEnd="@+id/imageView"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginEnd="16dp" />
|
||||
|
||||
<Button
|
||||
android:text="返回"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="30dp"
|
||||
android:id="@+id/btnAudioReturn"
|
||||
android:bottomLeftRadius="20dp"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<Switch
|
||||
android:text="扬声器"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/switch1"
|
||||
android:layout_below="@+id/button12"
|
||||
android:layout_alignLeft="@+id/textView10"
|
||||
android:layout_alignStart="@+id/textView10"
|
||||
android:layout_marginTop="30dp" />
|
||||
|
||||
<Switch
|
||||
android:text="录 音"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/switch2"
|
||||
android:layout_below="@+id/switch1"
|
||||
android:layout_alignRight="@+id/switch1"
|
||||
android:layout_alignEnd="@+id/switch1" />
|
||||
|
||||
<Switch
|
||||
android:text="静 音"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/switch3"
|
||||
android:layout_below="@+id/switch2"
|
||||
android:layout_alignRight="@+id/switch2"
|
||||
android:layout_alignEnd="@+id/switch2" />
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_falldown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:orientation="vertical"
|
||||
tools:context="me.wshuo.tbot.Falldown">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:text="返回"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btnFallReturn"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="摔倒检测功能设置"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/textView4"
|
||||
android:textSize="30dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<Switch
|
||||
android:text="检测老人摔倒"
|
||||
android:layout_width="wrap_content"
|
||||
android:textSize="20dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/switch1"
|
||||
|
||||
android:layout_marginTop="30dp" />
|
||||
|
||||
<Switch
|
||||
android:text="通过电话报警"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/switch2"
|
||||
android:textSize="20dp"
|
||||
android:layout_below="@+id/switch1" />
|
||||
|
||||
<Switch
|
||||
android:text="通过短信报警"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/switch3"
|
||||
android:textSize="20dp"
|
||||
android:layout_below="@+id/switch2"
|
||||
/>
|
||||
</LinearLayout>
|
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_ipconnect"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="me.wshuo.tbot.IPConnect">
|
||||
|
||||
<TextView
|
||||
android:text="连接到您的机器人:"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textSize="30dp"
|
||||
android:id="@+id/tvIPConnect"
|
||||
/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ip"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etIPConnect"
|
||||
android:hint="请输入机器人ip"
|
||||
android:layout_alignParentLeft="true"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_width="300dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnIPConnect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@+id/etIPConnect"
|
||||
android:background="#00bbff"
|
||||
android:text="确定"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_medicine_alert"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:orientation="vertical"
|
||||
tools:context="me.wshuo.tbot.MedicineAlert">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:text="返回"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/btnAlertReturn"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:text="设置服药提醒时间"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/textView3"
|
||||
android:textSize="30dp"
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<DatePicker
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/datePicker3" />
|
||||
|
||||
<TimePicker
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/timePicker2" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_menu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:background="#e2ebee"
|
||||
android:orientation="vertical"
|
||||
tools:context="me.wshuo.tbot.Menu">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation = "horizontal">
|
||||
|
||||
<TextView
|
||||
android:text="功能菜单"
|
||||
android:layout_width="272dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ff00b7"
|
||||
android:gravity="center"
|
||||
android:textSize="30dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toEndOf="@+id/btnMenuReturn" />
|
||||
|
||||
<Button
|
||||
android:text="返回"
|
||||
android:layout_width="121dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#0073ff"
|
||||
android:id="@+id/btnMenuReturn"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="摔倒检测"
|
||||
android:id="@+id/btnFall"/>
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="服药提醒"
|
||||
android:id="@+id/btnAlert"/>
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="语音通信"
|
||||
android:id="@+id/btnAudio"/>
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="视频监控"
|
||||
android:id="@+id/btnVideo"/>
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="个人设置"
|
||||
android:id="@+id/btnSetting"/>
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="控制移动"
|
||||
android:id="@+id/btnControl"/>
|
||||
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="me.wshuo.tbot.MotionControl">
|
||||
|
||||
<TextView
|
||||
android:text="控制移动"
|
||||
android:layout_width="75dp"
|
||||
android:layout_height="25dp"
|
||||
android:id="@+id/textView2"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:text="返回"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="30dp"
|
||||
android:id="@+id/btnMotionReturn"
|
||||
android:bottomLeftRadius="20dp"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="250dp"
|
||||
android:id="@+id/logo"
|
||||
android:layout_alignWithParentIfMissing="false"
|
||||
android:background="@android:color/background_dark"
|
||||
android:layout_marginTop="27dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button11"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="#17ce33"
|
||||
android:layout_marginTop="22dp"
|
||||
android:layout_below="@+id/logo"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:text="前" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button6"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="#17ce33"
|
||||
android:layout_below="@+id/button2"
|
||||
android:layout_alignLeft="@+id/button11"
|
||||
android:layout_alignStart="@+id/button11"
|
||||
android:text="后" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button7"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="#17ce33"
|
||||
android:layout_below="@+id/button11"
|
||||
android:layout_toLeftOf="@+id/textView2"
|
||||
android:layout_toStartOf="@+id/textView2"
|
||||
android:text="左" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button2"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:background="#17ce33"
|
||||
android:layout_below="@+id/button11"
|
||||
android:layout_toRightOf="@+id/textView2"
|
||||
android:layout_toEndOf="@+id/textView2"
|
||||
android:text="右" />
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_preference_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="me.wshuo.tbot.PreferenceSettings">
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,132 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="me.wshuo.tbot.VideoObserve">
|
||||
|
||||
<SeekBar
|
||||
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="100"
|
||||
android:progress="3"
|
||||
android:layout_alignTop="@+id/textView6"
|
||||
android:layout_alignLeft="@+id/button3"
|
||||
android:layout_alignStart="@+id/button3"
|
||||
android:id="@+id/seekBar3" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="250dp"
|
||||
android:id="@+id/logo"
|
||||
android:layout_alignWithParentIfMissing="false"
|
||||
android:background="@android:color/background_dark"
|
||||
android:layout_marginTop="27dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true" />
|
||||
|
||||
<Button
|
||||
android:text="开启"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/logo"
|
||||
android:layout_alignLeft="@+id/logo"
|
||||
android:layout_alignStart="@+id/logo"
|
||||
android:layout_marginLeft="37dp"
|
||||
android:layout_marginStart="37dp"
|
||||
android:layout_marginTop="31dp"
|
||||
android:id="@+id/button3"
|
||||
android:elevation="0dp"
|
||||
android:background="@android:color/holo_green_light" />
|
||||
|
||||
<TextView
|
||||
android:text="视频监控"
|
||||
android:layout_width="75dp"
|
||||
android:layout_height="25dp"
|
||||
android:id="@+id/textView5"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:text="返回"
|
||||
android:layout_width="45dp"
|
||||
android:layout_height="30dp"
|
||||
android:id="@+id/btnVideoReturn"
|
||||
android:bottomLeftRadius="20dp"
|
||||
android:background="@android:color/holo_red_dark"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<TextView
|
||||
android:text="亮 度"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="19dp"
|
||||
android:id="@+id/textView7"
|
||||
android:layout_below="@+id/textView6"
|
||||
android:layout_alignLeft="@+id/textView6"
|
||||
android:layout_alignStart="@+id/textView6"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="清晰度"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/textView8"
|
||||
android:layout_below="@+id/textView7"
|
||||
android:layout_alignLeft="@+id/textView7"
|
||||
android:layout_alignStart="@+id/textView7"
|
||||
android:layout_marginTop="18dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="音 量"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="17dp"
|
||||
android:id="@+id/textView6"
|
||||
android:layout_below="@+id/button3"
|
||||
|
||||
/>
|
||||
|
||||
<SeekBar
|
||||
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="100"
|
||||
android:progress="3"
|
||||
android:id="@+id/seekBar"
|
||||
android:layout_alignBottom="@+id/textView7"
|
||||
android:layout_alignLeft="@+id/seekBar3"
|
||||
android:layout_alignStart="@+id/seekBar3" />
|
||||
|
||||
<SeekBar
|
||||
style="@style/Widget.AppCompat.SeekBar.Discrete"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="100"
|
||||
android:progress="3"
|
||||
android:id="@+id/seekBar2"
|
||||
android:layout_alignTop="@+id/textView8"
|
||||
android:layout_alignLeft="@+id/seekBar"
|
||||
android:layout_alignStart="@+id/seekBar" />
|
||||
|
||||
<Button
|
||||
android:text="关闭"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/button4"
|
||||
android:background="@android:color/holo_red_light"
|
||||
android:layout_alignBaseline="@+id/button3"
|
||||
android:layout_alignBottom="@+id/button3"
|
||||
android:layout_toRightOf="@+id/button3"
|
||||
android:layout_toEndOf="@+id/button3" />
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_welcome"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:background="#e2ebee"
|
||||
android:orientation="vertical"
|
||||
tools:context="me.wshuo.tbot.Welcome">
|
||||
|
||||
<TextView
|
||||
android:text="@string/welcome"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#ff00b7"
|
||||
android:gravity="center"
|
||||
android:textSize="30dp"
|
||||
|
||||
android:id="@+id/tvWelcome" />
|
||||
|
||||
<TextView
|
||||
android:text="@string/introduction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#1a1c1b"
|
||||
android:gravity="center"
|
||||
android:textSize="25dp"
|
||||
android:id="@+id/tvIntroduction" />
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="58dp"
|
||||
android:src="@drawable/w1" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="47dp"
|
||||
android:text="摔倒检测"
|
||||
android:textSize="40dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="58dp"
|
||||
android:src="@drawable/w2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="47dp"
|
||||
android:text="服药提醒"
|
||||
android:textSize="40dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="58dp"
|
||||
android:src="@drawable/w3" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="47dp"
|
||||
android:text="语音通信"
|
||||
android:textSize="40dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="58dp"
|
||||
android:src="@drawable/w4" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="47dp"
|
||||
android:text="视频监控"
|
||||
android:textSize="40dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="68dp"
|
||||
android:layout_height="58dp"
|
||||
android:src="@drawable/w1" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="47dp"
|
||||
android:text="移动控制"
|
||||
android:textSize="40dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Great, let's go!"
|
||||
android:background="#00bbff"
|
||||
android:id="@+id/btnWelcome"/>
|
||||
</LinearLayout>
|
@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="me.wshuo.tbot.Login">
|
||||
<RelativeLayout
|
||||
android:id="@+id/login_view"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="800dp"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="注册"
|
||||
android:id="@+id/login_btn_register"
|
||||
android:onClick="resetpwd"
|
||||
android:textColor="#ffffff"
|
||||
android:background="#e52525"
|
||||
android:textSize="20dp"
|
||||
android:layout_below="@+id/login_btn_login"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="10dp" />
|
||||
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="登录"
|
||||
android:id="@+id/login_btn_login"
|
||||
android:onClick="finish_login"
|
||||
android:background="#545bcb"
|
||||
android:textSize="20dp"
|
||||
android:textColor="#ffffff"
|
||||
android:layout_below="@+id/login_edit_pwd"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="52dp" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="150dp"
|
||||
android:id="@+id/logo"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignWithParentIfMissing="false"
|
||||
android:background="#ffffff" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/login_edit_pwd"
|
||||
android:drawableLeft="@android:drawable/ic_lock_idle_lock"
|
||||
android:hint="请输入您的密码"
|
||||
android:layout_below="@+id/login_edit_account"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<EditText
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPersonName"
|
||||
android:id="@+id/login_edit_account"
|
||||
android:drawableLeft="@android:drawable/ic_menu_myplaces"
|
||||
android:hint="请输入您的用户名"
|
||||
android:layout_below="@+id/logo"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="20dp" />
|
||||
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="注销"
|
||||
android:id="@+id/login_btn_cancle"
|
||||
android:textSize="20dp"
|
||||
android:layout_below="@+id/login_btn_register"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:textColor="#ffffff"
|
||||
android:background="#3a1313" />
|
||||
|
||||
<CheckBox
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="20dp"
|
||||
android:text="记住密码"
|
||||
android:id="@+id/Login_Remember"
|
||||
android:layout_below="@+id/login_edit_pwd"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:checked="false"
|
||||
android:textSize="15dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="20dp"
|
||||
android:text="修改密码"
|
||||
android:id="@+id/login_text_change_pwd"
|
||||
android:layout_below="@+id/login_edit_pwd"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:textSize="15dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/login_success_view"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginLeft="15.0px"
|
||||
android:layout_marginRight="15.0px"
|
||||
android:layout_marginTop="62.0px"
|
||||
android:background="#ff3f3f3f"
|
||||
android:paddingBottom="10.0px"
|
||||
android:paddingTop="21.0px"
|
||||
android:visibility="gone" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/login_success_show"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:textColor="#ff3f3f3f"
|
||||
android:textSize="20.0dip" />
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:weightSum="1">
|
||||
|
||||
<EditText
|
||||
android:drawableLeft="@android:drawable/ic_menu_myplaces"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPersonName"
|
||||
android:ems="10"
|
||||
android:id="@+id/resetpwd_edit_name"
|
||||
android:layout_alignParentTop="true"
|
||||
android:hint="请输入您的用户名"
|
||||
android:layout_alignLeft="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignStart="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignRight="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignEnd="@+id/resetpwd_edit_pwd_new" />
|
||||
|
||||
<!--
|
||||
|
||||
<EditText android:id="@+id/edt_operator_name" style="@style/syncEditText"
|
||||
android:hint="@string/hint_operator_name" />
|
||||
<ImageView android:id="@+id/syncOperatorImg" style="@style/syncImageView"
|
||||
android:layout_alignLeft="@+id/edt_operator_name"
|
||||
android:layout_alignTop="@+id/edt_operator_name"
|
||||
android:layout_alignBottom="@+id/edt_operator_name" android:src="@drawable/sync_operator" />
|
||||
-->
|
||||
|
||||
<EditText
|
||||
android:drawableLeft="@android:drawable/ic_lock_idle_lock"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/resetpwd_edit_pwd_old"
|
||||
android:hint="请输入您的密码"
|
||||
android:layout_below="@+id/resetpwd_edit_name"
|
||||
android:layout_alignRight="@+id/resetpwd_edit_name"
|
||||
android:layout_alignEnd="@+id/resetpwd_edit_name"
|
||||
android:layout_alignLeft="@+id/resetpwd_edit_name"
|
||||
android:layout_alignStart="@+id/resetpwd_edit_name" />
|
||||
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="取消"
|
||||
android:id="@+id/register_btn_cancel"
|
||||
android:textSize="20dp"
|
||||
android:background="#f71818"
|
||||
android:layout_below="@+id/register_btn_sure"
|
||||
android:layout_alignLeft="@+id/register_btn_sure"
|
||||
android:layout_alignStart="@+id/register_btn_sure"
|
||||
android:layout_marginTop="10dp" />
|
||||
|
||||
<EditText
|
||||
android:drawableLeft="@android:drawable/ic_lock_idle_lock"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_below="@+id/resetpwd_edit_pwd_old"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:hint="请确认您的密码" />
|
||||
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="确定"
|
||||
android:id="@+id/register_btn_sure"
|
||||
android:textSize="20dp"
|
||||
android:background="#1cf718"
|
||||
android:layout_below="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignLeft="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignStart="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_marginTop="20dp" />
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:weightSum="1">
|
||||
|
||||
<EditText
|
||||
android:drawableLeft="@android:drawable/ic_menu_myplaces"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPersonName"
|
||||
android:ems="10"
|
||||
android:id="@+id/resetpwd_edit_name"
|
||||
android:layout_alignParentTop="true"
|
||||
android:hint="请输入您的用户名"
|
||||
android:layout_alignLeft="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignStart="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignRight="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignEnd="@+id/resetpwd_edit_pwd_new" />
|
||||
|
||||
<!--
|
||||
|
||||
<EditText android:id="@+id/edt_operator_name" style="@style/syncEditText"
|
||||
android:hint="@string/hint_operator_name" />
|
||||
<ImageView android:id="@+id/syncOperatorImg" style="@style/syncImageView"
|
||||
android:layout_alignLeft="@+id/edt_operator_name"
|
||||
android:layout_alignTop="@+id/edt_operator_name"
|
||||
android:layout_alignBottom="@+id/edt_operator_name" android:src="@drawable/sync_operator" />
|
||||
-->
|
||||
|
||||
<EditText
|
||||
android:drawableLeft="@android:drawable/ic_lock_idle_lock"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/resetpwd_edit_pwd_old"
|
||||
android:hint="请输入您的旧密码"
|
||||
android:layout_below="@+id/resetpwd_edit_name"
|
||||
android:layout_alignRight="@+id/resetpwd_edit_name"
|
||||
android:layout_alignEnd="@+id/resetpwd_edit_name"
|
||||
android:layout_alignLeft="@+id/resetpwd_edit_name"
|
||||
android:layout_alignStart="@+id/resetpwd_edit_name" />
|
||||
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="取消"
|
||||
android:id="@+id/resetpwd_btn_cancel"
|
||||
android:textSize="20dp"
|
||||
android:onClick="not_to_reset"
|
||||
android:background="#f71818"
|
||||
android:layout_below="@+id/resetpwd_btn_sure"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_marginTop="20dp" />
|
||||
|
||||
<EditText
|
||||
android:drawableLeft="@android:drawable/ic_lock_idle_lock"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_below="@+id/resetpwd_edit_pwd_old"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:hint="请确认您的新密码"
|
||||
/>
|
||||
|
||||
<Button
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="确定"
|
||||
android:id="@+id/resetpwd_btn_sure"
|
||||
android:textSize="20dp"
|
||||
android:onClick="sure_to_reset"
|
||||
android:background="#1cf718"
|
||||
android:layout_marginTop="92dp"
|
||||
android:layout_below="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
<EditText
|
||||
android:drawableLeft="@android:drawable/ic_lock_idle_lock"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="60dp"
|
||||
android:inputType="textPassword"
|
||||
android:ems="10"
|
||||
android:id="@+id/resetpwd_edit_pwd_check"
|
||||
android:hint="请输入您的新密码"
|
||||
android:layout_below="@+id/resetpwd_edit_pwd_new"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true" />
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="您好,欢迎回来!"
|
||||
android:id="@+id/textView"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:textSize="16dp" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="退出"
|
||||
android:id="@+id/returnback"
|
||||
android:layout_below="@+id/textView"
|
||||
android:layout_marginTop="36dp"
|
||||
android:onClick="back_to_login"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="20dp"
|
||||
android:background="#d95b5b"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true" />
|
||||
|
||||
</RelativeLayout>
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
@ -0,0 +1,103 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="hint_operator_name">你好</string>
|
||||
<string name="hello">Hello World, Date!</string>
|
||||
<string name="app_name">Tbot</string>
|
||||
<string name="strInputAccHint">输入帐号</string>
|
||||
<string name="strAccInputLabel">帐号</string>
|
||||
<string name="strPswInputLabel">密码</string>
|
||||
<string name="login">登 录</string>
|
||||
<string name="register">注 册</string>
|
||||
<string name="cancel">取 消</string>
|
||||
<string name="account_empty">用户名为空,请重新输入!</string>
|
||||
<string name="pwd_empty">密码为空,请重新输入!</string>
|
||||
<string name="pwd_not_fit_user">密码不正确,请重新输入!</string>
|
||||
<string name="pwd_new_empty">新密码为空,请重新输入!</string>
|
||||
<string name="pwd_check_empty">密码确认为空,请重新输入!</string>
|
||||
<string name="pwd_not_the_same">密码确认不正确,请重新输入密码!</string>
|
||||
<string name="register_fail">注册用户失败,请重新尝试!</string>
|
||||
<string name="register_success">注册成功!</string>
|
||||
<string name="resetpwd_fail">密码修改失败,请重新尝试!</string>
|
||||
<string name="resetpwd_success">密码修改成功!</string>
|
||||
<string name="login_success">登陆成功!</string>
|
||||
<string name="cancel_success">注销成功!</string>
|
||||
<string name="login_fail">登录失败!请输入正确的用户名与密码!</string>
|
||||
<string name="cancel_fail">注销失败!请输入正确的用户名与密码!</string>
|
||||
<string name="name_already_exist">用户名【<xliff:g example="tom" id="id1">%1$s</xliff:g>】已存在,请重新输入!</string>
|
||||
<string name="name_not_exist">用户名【<xliff:g example="tom" id="id1">%1$s</xliff:g>】不存在,请重新输入!</string>
|
||||
<string name="user_login_success">用户:<xliff:g example="tom" id="id2">%1$s</xliff:g>登录,欢迎光临!</string>
|
||||
<string name="welcome">欢迎!</string>
|
||||
<string name="introduction">空巢老人看护小助手将会帮助您:</string>
|
||||
<string name="title_activity_settings">设置</string>
|
||||
|
||||
<!-- Strings related to Settings -->
|
||||
|
||||
<!-- Example General settings -->
|
||||
<string name="pref_header_general">General</string>
|
||||
|
||||
<string name="pref_title_social_recommendations">Enable social recommendations</string>
|
||||
<string name="pref_description_social_recommendations">Recommendations for people to contact
|
||||
based on your message history
|
||||
</string>
|
||||
|
||||
<string name="pref_title_display_name">Display name</string>
|
||||
<string name="pref_default_display_name">John Smith</string>
|
||||
|
||||
<string name="pref_title_add_friends_to_messages">Add friends to messages</string>
|
||||
<string-array name="pref_example_list_titles">
|
||||
<item>Always</item>
|
||||
<item>When possible</item>
|
||||
<item>Never</item>
|
||||
</string-array>
|
||||
<string-array name="pref_example_list_values">
|
||||
<item>1</item>
|
||||
<item>0</item>
|
||||
<item>-1</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Example settings for Data & Sync -->
|
||||
<string name="pref_header_data_sync">Data & sync</string>
|
||||
|
||||
<string name="pref_title_sync_frequency">Sync frequency</string>
|
||||
<string-array name="pref_sync_frequency_titles">
|
||||
<item>15 minutes</item>
|
||||
<item>30 minutes</item>
|
||||
<item>1 hour</item>
|
||||
<item>3 hours</item>
|
||||
<item>6 hours</item>
|
||||
<item>Never</item>
|
||||
</string-array>
|
||||
<string-array name="pref_sync_frequency_values">
|
||||
<item>15</item>
|
||||
<item>30</item>
|
||||
<item>60</item>
|
||||
<item>180</item>
|
||||
<item>360</item>
|
||||
<item>-1</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="list_preference_entries">
|
||||
<item>Entry 1</item>
|
||||
<item>Entry 2</item>
|
||||
<item>Entry 3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="list_preference_entry_values">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="multi_select_list_preference_default_value" />
|
||||
|
||||
<string name="pref_title_system_sync_settings">System sync settings</string>
|
||||
|
||||
<!-- Example settings for Notifications -->
|
||||
<string name="pref_header_notifications">Notifications</string>
|
||||
|
||||
<string name="pref_title_new_message_notifications">New message notifications</string>
|
||||
|
||||
<string name="pref_title_ringtone">Ringtone</string>
|
||||
<string name="pref_ringtone_silent">Silent</string>
|
||||
|
||||
<string name="pref_title_vibrate">Vibrate</string>
|
||||
</resources>
|
@ -0,0 +1,11 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
@ -0,0 +1,21 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
|
||||
dismiss it. -->
|
||||
<!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
|
||||
<ListPreference
|
||||
android:defaultValue="180"
|
||||
android:entries="@array/pref_sync_frequency_titles"
|
||||
android:entryValues="@array/pref_sync_frequency_values"
|
||||
android:key="sync_frequency"
|
||||
android:negativeButtonText="@null"
|
||||
android:positiveButtonText="@null"
|
||||
android:title="@string/pref_title_sync_frequency" />
|
||||
|
||||
<!-- This preference simply launches an intent when selected. Use this UI sparingly, per
|
||||
design guidelines. -->
|
||||
<Preference android:title="@string/pref_title_system_sync_settings">
|
||||
<intent android:action="android.settings.SYNC_SETTINGS" />
|
||||
</Preference>
|
||||
|
||||
</PreferenceScreen>
|
@ -0,0 +1,33 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="example_switch"
|
||||
android:summary="@string/pref_description_social_recommendations"
|
||||
android:title="@string/pref_title_social_recommendations" />
|
||||
|
||||
<!-- NOTE: EditTextPreference accepts EditText attributes. -->
|
||||
<!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. -->
|
||||
<EditTextPreference
|
||||
android:capitalize="words"
|
||||
android:defaultValue="@string/pref_default_display_name"
|
||||
android:inputType="textCapWords"
|
||||
android:key="example_text"
|
||||
android:maxLines="1"
|
||||
android:selectAllOnFocus="true"
|
||||
android:singleLine="true"
|
||||
android:title="@string/pref_title_display_name" />
|
||||
|
||||
<!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
|
||||
dismiss it. -->
|
||||
<!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
|
||||
<ListPreference
|
||||
android:defaultValue="-1"
|
||||
android:entries="@array/pref_example_list_titles"
|
||||
android:entryValues="@array/pref_example_list_values"
|
||||
android:key="example_list"
|
||||
android:negativeButtonText="@null"
|
||||
android:positiveButtonText="@null"
|
||||
android:title="@string/pref_title_add_friends_to_messages" />
|
||||
|
||||
</PreferenceScreen>
|
@ -0,0 +1,20 @@
|
||||
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- These settings headers are only used on tablets. -->
|
||||
|
||||
<header
|
||||
android:fragment="me.wshuo.tbot.Settings$GeneralPreferenceFragment"
|
||||
android:icon="@drawable/ic_info_black_24dp"
|
||||
android:title="@string/pref_header_general" />
|
||||
|
||||
<header
|
||||
android:fragment="me.wshuo.tbot.Settings$NotificationPreferenceFragment"
|
||||
android:icon="@drawable/ic_notifications_black_24dp"
|
||||
android:title="@string/pref_header_notifications" />
|
||||
|
||||
<header
|
||||
android:fragment="me.wshuo.tbot.Settings$DataSyncPreferenceFragment"
|
||||
android:icon="@drawable/ic_sync_black_24dp"
|
||||
android:title="@string/pref_header_data_sync" />
|
||||
|
||||
</preference-headers>
|
@ -0,0 +1,27 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- A 'parent' preference, which enables/disables child preferences (below)
|
||||
when checked/unchecked. -->
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="notifications_new_message"
|
||||
android:title="@string/pref_title_new_message_notifications" />
|
||||
|
||||
<!-- Allows the user to choose a ringtone in the 'notification' category. -->
|
||||
<!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
|
||||
<!-- NOTE: RingtonePreference's summary should be set to its value by the activity code. -->
|
||||
<RingtonePreference
|
||||
android:defaultValue="content://settings/system/notification_sound"
|
||||
android:dependency="notifications_new_message"
|
||||
android:key="notifications_new_message_ringtone"
|
||||
android:ringtoneType="notification"
|
||||
android:title="@string/pref_title_ringtone" />
|
||||
|
||||
<!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:dependency="notifications_new_message"
|
||||
android:key="notifications_new_message_vibrate"
|
||||
android:title="@string/pref_title_vibrate" />
|
||||
|
||||
</PreferenceScreen>
|
@ -0,0 +1,17 @@
|
||||
package me.wshuo.tbot;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
@ -0,0 +1,6 @@
|
||||
#Mon Dec 28 10:00:20 PST 2015
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
@ -0,0 +1,90 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
@ -0,0 +1 @@
|
||||
include ':app'
|
@ -0,0 +1,14 @@
|
||||
bin
|
||||
gen
|
||||
libs
|
||||
build
|
||||
build.xml
|
||||
local.properties
|
||||
proguard-project.txt
|
||||
.gradle
|
||||
|
||||
# These are Android Studio files, might be worth including these later.
|
||||
.idea
|
||||
*.iml
|
||||
*.log
|
||||
build-log.xml
|
@ -0,0 +1,74 @@
|
||||
### Documentation
|
||||
|
||||
See [rosjava_core](https://github.com/rosjava/rosjava_core) readme.
|
||||
|
||||
### Master Branch
|
||||
|
||||
The master branch is currently dependant on stable hydro branches of the other rosjava and android
|
||||
core repositories (too much movement there to depend on master). Also dependant on the master branch of
|
||||
android_apps.
|
||||
|
||||
### Rosinstaller
|
||||
|
||||
|
||||
* Master: https://raw.github.com/rosjava/rosjava/master/android_apps.rosinstall
|
||||
* Hydro: https://raw.github.com/rosjava/rosjava/hydro/android_apps.rosinstall
|
||||
=======
|
||||
Android Remocons
|
||||
================
|
||||
|
||||
Remocons for pairing and concert modes as well as some simple apps for testing.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
This is primarily to get the rosjava build tools. If you are using precise, you don't need this step (it will get it from debs).
|
||||
|
||||
```
|
||||
> yujin_init_workspace -j5 ~/rosjava rosjava
|
||||
> cd ~/rosjava
|
||||
> yujin_init_build .
|
||||
> yujin_make --install-rosdeps
|
||||
> yujin_make
|
||||
```
|
||||
|
||||
This will use the maven repos for the core rosjava and android repos.
|
||||
|
||||
```
|
||||
> yujin_init_workspace -j5 ~/rocon_rosjava rocon-rosjava
|
||||
> cd ~/rocon_rosjava
|
||||
> yujin_init_build --underlays=~/rosjava/devel .
|
||||
> yujin_make --install-rosdeps
|
||||
> yujin_make
|
||||
```
|
||||
|
||||
Android core (only necessary on indigo):
|
||||
|
||||
```
|
||||
> yujin_init_workspace -j5 ~/android_core android-core
|
||||
> cd ~/android_core
|
||||
> yujin_init_build --underlays=~/rosjava/devel .
|
||||
> yujin_make
|
||||
```
|
||||
|
||||
The android interactions::
|
||||
|
||||
```
|
||||
> yujin_init_workspace -j5 ~/android_interactions
|
||||
> cd ~/android_interactions
|
||||
> yujin_init_build --underlays="~/android_core/devel;~/rocon_rosjava/devel;~/rosjava/devel" .
|
||||
> cd ~/android_interactions/src
|
||||
# to compile the rocon android apps as well
|
||||
> wstool set rocon_android_apps --git https://github.com/robotics-in-concert/rocon_android_apps.git --version=headless_launcher_update
|
||||
> wstool update -j5
|
||||
> yujin_make
|
||||
> . .bashrc
|
||||
```
|
||||
|
||||
Launch the android studio, compile the ```rocon_nfc_writer``` and ```headless_launcher```
|
||||
|
||||
Other README's
|
||||
==============
|
||||
|
||||
* rocon_nfc_writer/README.md
|
||||
* headless_launcher/README.md
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Daniel Stonier
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = '4.1'
|
||||
}
|
||||
|
||||
buildscript {
|
||||
apply from: project.file("./buildscript2.gradle")
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
// classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
subprojects {
|
||||
/*
|
||||
* The android plugin configures a few things:
|
||||
*
|
||||
* - local deployment repository : where it dumps the jars and packaged artifacts)
|
||||
* - local maven repositories : where it finds your locally installed/built artifacts)
|
||||
* - external maven repositories : where it goes looking if it can't find dependencies locally
|
||||
* - android build tools version : which version we use across the board
|
||||
*
|
||||
* To modify, or add repos to the default external maven repositories list, pull request against this code:
|
||||
*
|
||||
* https://github.com/rosjava/rosjava_bootstrap/blob/indigo/gradle_plugins/src/main/groovy/org/ros/gradle_plugins/RosPlugin.groovy#L31
|
||||
*
|
||||
* To modify the build tools version, pull request against this code:
|
||||
*
|
||||
* https://github.com/rosjava/rosjava_bootstrap/blob/indigo/gradle_plugins/src/main/groovy/org/ros/gradle_plugins/RosAndroid.groovy#L14
|
||||
*/
|
||||
apply plugin: 'ros-android'
|
||||
|
||||
afterEvaluate { project ->
|
||||
android {
|
||||
// Exclude a few files that are duplicated across our dependencies and
|
||||
// prevent packaging Android applications.
|
||||
packagingOptions {
|
||||
exclude "META-INF/LICENSE.txt"
|
||||
exclude "META-INF/NOTICE.txt"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
defaultTasks 'assembleRelease', 'uploadArchives'
|
||||
|
||||
|
||||
repositories {
|
||||
google()
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
rootProject.buildscript {
|
||||
apply from: project.file("./buildscript2.gradle")
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:3.0.1"
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
rootProject.buildscript {
|
||||
String rosMavenPath = System.getenv("ROS_MAVEN_PATH")
|
||||
String rosMavenRepository = System.getenv("ROS_MAVEN_REPOSITORY")
|
||||
repositories {
|
||||
if (rosMavenPath != null) {
|
||||
rosMavenPath.tokenize(":").each { path ->
|
||||
maven {
|
||||
// We can't use uri() here because we aren't running inside something
|
||||
// that implements the Script interface.
|
||||
url "file:${path}"
|
||||
}
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "http://repository.springsource.com/maven/bundles/release"
|
||||
}
|
||||
maven {
|
||||
url "http://repository.springsource.com/maven/bundles/external"
|
||||
}
|
||||
if (rosMavenRepository != null) {
|
||||
maven {
|
||||
url rosMavenRepository
|
||||
}
|
||||
}
|
||||
maven {
|
||||
url "https://github.com/rosjava/rosjava_mvn_repo/raw/master"
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.ros.rosjava_bootstrap:gradle_plugins:0.3.0"
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Jorge Santos.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
dependencies {
|
||||
compile 'org.ros.android_core:android_10:0.2.1'
|
||||
compile 'org.ros.rosjava_core:rosjava:0.2.2'
|
||||
compile 'org.ros.rosjava_bootstrap:message_generation:0.3.0'
|
||||
compile 'org.ros.rosjava_messages:diagnostic_msgs:1.11.9'
|
||||
compile 'org.ros.rosjava_messages:rocon_interaction_msgs:0.7.12'
|
||||
compile 'org.ros.rosjava_messages:rocon_std_msgs:0.7.12'
|
||||
compile 'org.ros.rosjava_messages:rocon_app_manager_msgs:0.7.12'
|
||||
compile 'com.github.rosjava.android_extras:gingerbread:0.2.0'
|
||||
compile 'com.github.rosjava.android_extras:zxing:0.2.0'
|
||||
compile 'com.github.rosjava.zeroconf_jmdns_suite:jmdns:0.2.1'
|
||||
compile 'com.github.robotics_in_concert.rocon_rosjava_core:rosjava_utils:0.2.0'
|
||||
compile 'com.github.robotics_in_concert.rocon_rosjava_core:master_info:0.2.0'
|
||||
compile 'com.github.robotics_in_concert.rocon_rosjava_core:rocon_interactions:0.2.0'
|
||||
compile fileTree(include: 'snakeyaml*.jar', dir: '../external_libraries')
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 26
|
||||
defaultConfig {
|
||||
minSdkVersion 18
|
||||
targetSdkVersion 26
|
||||
versionCode 1
|
||||
versionName "1.0.1"
|
||||
}
|
||||
buildToolsVersion '26.0.2'
|
||||
}
|
||||
|
||||
defaultTasks 'assembleRelease'
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2011, Willow Garage, Inc.
|
||||
* Copyright (c) 2013, OSRF.
|
||||
* Copyright (c) 2013, Yujin Robot.
|
||||
*
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.util.Patterns;
|
||||
|
||||
import com.github.rosjava.android_apps.application_management.AppManager;
|
||||
import com.github.rosjava.android_apps.application_management.ConcertDescription;
|
||||
import com.github.rosjava.android_apps.application_management.RosAppActivity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
|
||||
/**
|
||||
* A rewrite of robot_remocon/AppLauncher that...
|
||||
* - works with concerts
|
||||
* - can start web apps
|
||||
* - headless; only reports error codes
|
||||
*
|
||||
* @author jorge@yujinrobot.com (Jorge Santos Simon)
|
||||
*/
|
||||
public class AppLauncher {
|
||||
|
||||
public enum Result {
|
||||
SUCCESS,
|
||||
NOT_INSTALLED,
|
||||
CANNOT_CONNECT,
|
||||
MALFORMED_URI,
|
||||
CONNECT_TIMEOUT,
|
||||
OTHER_ERROR;
|
||||
|
||||
public String message;
|
||||
|
||||
Result withMsg(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a client app for the given concert app.
|
||||
*/
|
||||
static public Result launch(final Activity parent, final ConcertDescription concert,
|
||||
final concert_msgs.RemoconApp app) {
|
||||
|
||||
Log.i("AppLaunch", "launching concert app " + app.getDisplayName() + " on service " + app.getServiceName());
|
||||
|
||||
// On android apps, app name will be an intent action, while for web apps it will be its URL
|
||||
if (Patterns.WEB_URL.matcher(app.getName()).matches() == true) {
|
||||
return launchWebApp(parent, concert, app);
|
||||
}
|
||||
else {
|
||||
return launchAndroidApp(parent, concert, app);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Launch a client android app for the given concert app.
|
||||
*/
|
||||
static private Result launchAndroidApp(final Activity parent, final ConcertDescription concert,
|
||||
final concert_msgs.RemoconApp app) {
|
||||
|
||||
// Create the Intent from rapp's name, pass it parameters and remaps and start it
|
||||
String appName = app.getName();
|
||||
Intent intent = new Intent(appName);
|
||||
|
||||
// Copy all app data to "extra" data in the intent.
|
||||
intent.putExtra(AppManager.PACKAGE + "." + RosAppActivity.AppMode.CONCERT + "_app_name", appName);
|
||||
intent.putExtra(ConcertDescription.UNIQUE_KEY, concert);
|
||||
intent.putExtra("RemoconActivity", "com.github.rosjava.android_remocons.concert_remocon.ConcertRemocon"); // TODO must be a RoconConstant!
|
||||
intent.putExtra("Parameters", app.getParameters()); // YAML-formatted string
|
||||
|
||||
// Remappings come as a messages list that make YAML parser crash, so we must digest if for him
|
||||
if ((app.getRemappings() != null) && (app.getRemappings().size() > 0)) {
|
||||
String remaps = "{";
|
||||
for (rocon_std_msgs.Remapping remap: app.getRemappings())
|
||||
remaps += remap.getRemapFrom() + ": " + remap.getRemapTo() + ", ";
|
||||
remaps = remaps.substring(0, remaps.length() - 2) + "}";
|
||||
intent.putExtra("Remappings", remaps);
|
||||
}
|
||||
|
||||
// intent.putExtra("runningNodes", runningNodes);
|
||||
// intent.putExtra("PairedManagerActivity", "com.github.concertics_in_concert.rocon_android.concert_remocon.ConcertRemocon");
|
||||
|
||||
try {
|
||||
Log.i("AppLaunch", "trying to start activity (action: " + appName + " )");
|
||||
parent.startActivity(intent);
|
||||
return Result.SUCCESS;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.i("AppLaunch", "activity not found for action: " + appName);
|
||||
}
|
||||
return Result.NOT_INSTALLED.withMsg("Android app not installed");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Launch a client web app for the given concert app.
|
||||
*/
|
||||
static private Result launchWebApp(final Activity parent, final ConcertDescription concert,
|
||||
final concert_msgs.RemoconApp app) {
|
||||
try
|
||||
{
|
||||
// Validate the URL before starting anything
|
||||
URL appURL = new URL(app.getName());
|
||||
|
||||
AsyncTask<URL, Void, String> asyncTask = new AsyncTask<URL, Void, String>() {
|
||||
@Override
|
||||
protected String doInBackground(URL... urls) {
|
||||
try {
|
||||
HttpURLConnection urlConnection = (HttpURLConnection)urls[0].openConnection();
|
||||
int responseCode = urlConnection.getResponseCode();
|
||||
urlConnection.disconnect();
|
||||
return urlConnection.getResponseMessage();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
}.execute(appURL);
|
||||
String result = asyncTask.get(5, TimeUnit.SECONDS);
|
||||
if (result == null || (result.startsWith("OK") == false && result.startsWith("ok") == false)) {
|
||||
return Result.CANNOT_CONNECT.withMsg(result);
|
||||
}
|
||||
|
||||
// We pass concert URL, parameters and remaps as URL parameters
|
||||
String appUriStr = app.getName();
|
||||
appUriStr += "?" + "MasterURI=" + concert.getMasterUri();
|
||||
if ((app.getParameters() != null) && (app.getParameters().length() > 0)) {
|
||||
appUriStr += "&" + "params=" + URLEncoder.encode(app.getParameters());
|
||||
}
|
||||
|
||||
// Remappings come as a messages list that make YAML parser crash, so we must digest if for him
|
||||
// TODO Single quotes seem to be necessary, but I didn't confirm yet
|
||||
if ((app.getRemappings() != null) && (app.getRemappings().size() > 0)) {
|
||||
String remaps = "{";
|
||||
for (rocon_std_msgs.Remapping remap: app.getRemappings())
|
||||
remaps += "\'" + remap.getRemapFrom() + "\':\'" + remap.getRemapTo() + "\',";
|
||||
remaps = remaps.substring(0, remaps.length() - 1) + "}";
|
||||
appUriStr += "&" + "remaps=" + URLEncoder.encode(remaps);
|
||||
}
|
||||
|
||||
appURL = new URL(appUriStr);
|
||||
appURL.toURI(); // throws URISyntaxException if fails; probably a redundant check
|
||||
Uri appURI = Uri.parse(appUriStr);
|
||||
|
||||
// Create an action view intent and pass rapp's name + extra information as URI
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, appURI);
|
||||
|
||||
Log.i("AppLaunch", "trying to start web app (URI: " + appUriStr + ")");
|
||||
parent.startActivity(intent);
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
return Result.MALFORMED_URI.withMsg("Cannot convert URL into URI. " + e.getMessage());
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
return Result.MALFORMED_URI.withMsg("App URL is not valid. " + e.getMessage());
|
||||
}
|
||||
catch (ActivityNotFoundException e) {
|
||||
// This cannot happen for a web site, right? must mean that I have no web browser!
|
||||
return Result.NOT_INSTALLED.withMsg("Activity not found for view action??? muoia???");
|
||||
}
|
||||
catch (TimeoutException e)
|
||||
{
|
||||
return Result.CONNECT_TIMEOUT.withMsg("Timeout waiting for app");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return Result.OTHER_ERROR.withMsg(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Copyright (C) 2013, OSRF.
|
||||
* Copyright (c) 2013, Yujin Robot.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import com.github.rosjava.android_apps.application_management.MasterId;
|
||||
|
||||
import org.ros.address.InetAddressFactory;
|
||||
import org.ros.android.NodeMainExecutorService;
|
||||
import org.ros.exception.RosRuntimeException;
|
||||
import org.ros.exception.ServiceNotFoundException;
|
||||
import org.ros.internal.node.client.ParameterClient;
|
||||
import org.ros.internal.node.server.NodeIdentifier;
|
||||
import org.ros.namespace.GraphName;
|
||||
import org.ros.node.AbstractNodeMain;
|
||||
import org.ros.node.ConnectedNode;
|
||||
import org.ros.node.Node;
|
||||
import org.ros.node.NodeConfiguration;
|
||||
import org.ros.node.service.ServiceClient;
|
||||
import org.ros.node.service.ServiceResponseListener;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import rocon_interaction_msgs.GetApp;
|
||||
import rocon_interaction_msgs.GetAppRequest;
|
||||
import rocon_interaction_msgs.GetAppResponse;
|
||||
import rocon_interaction_msgs.GetRolesAndApps;
|
||||
import rocon_interaction_msgs.GetRolesAndAppsRequest;
|
||||
import rocon_interaction_msgs.GetRolesAndAppsResponse;
|
||||
import rocon_interaction_msgs.RequestInteraction;
|
||||
import rocon_interaction_msgs.RequestInteractionRequest;
|
||||
import rocon_interaction_msgs.RequestInteractionResponse;
|
||||
|
||||
import static com.github.rosjava.android_remocons.common_tools.rocon.Constants.ANDROID_PLATFORM_INFO;
|
||||
|
||||
/**
|
||||
* This class has been derived from RobotAppsManager in android_apps/application_management.
|
||||
* The original is quite messy, and this is not much better, so maybe needs extra refactoring.
|
||||
* Also RobotAppsManager claims that it can be executed once, but I modified this to execute
|
||||
* arbitrary times. It looks to work, but mechanism is a bit brittle.
|
||||
* Apps manager implements the services and topics required to interact with the concert roles
|
||||
* manager. Typically to use this class its a three step process:
|
||||
*
|
||||
* 1) instantiate and provide a general failure handler
|
||||
* 2) provide a callback via one of the setupXXXservice methods
|
||||
* 3) call the service you want; there's a public method per service
|
||||
* 4) wait for service response.
|
||||
*
|
||||
* Essentially you are creating a node when creating an instance, and rosjava isolates each
|
||||
* service/topic to each 'node'.
|
||||
*
|
||||
* @author jorge@yujinrobot.com (Jorge Santos Simon)
|
||||
*/
|
||||
public class AppsManager extends AbstractNodeMain {
|
||||
public interface FailureHandler {
|
||||
/**
|
||||
* Called on failure with a short description of why it failed, like "exception" or "timeout".
|
||||
*/
|
||||
void handleFailure(String reason);
|
||||
}
|
||||
|
||||
// unique identifier to key string variables between activities.
|
||||
// TODO I make it compatible current apps; not needed if we rewrite as concert apps
|
||||
public static final String PACKAGE = com.github.rosjava.android_apps.application_management.AppManager.PACKAGE;
|
||||
|
||||
public enum Action {
|
||||
NONE, GET_APPS_FOR_ROLE, GET_APP_INFO, REQUEST_APP_USE
|
||||
};
|
||||
|
||||
private int app_hash;
|
||||
private String role;
|
||||
private Action action = Action.NONE;
|
||||
private rocon_interaction_msgs.RemoconApp app;
|
||||
private ConnectNodeThread connectThread;
|
||||
private ConnectedNode connectedNode;
|
||||
private NodeMainExecutorService nodeMainExecutorService;
|
||||
private FailureHandler failureCallback;
|
||||
private ServiceResponseListener<RequestInteractionResponse> requestServiceResponseListener;
|
||||
private ServiceResponseListener<GetRolesAndAppsResponse> getAppsServiceResponseListener;
|
||||
private ServiceResponseListener<GetAppResponse> appInfoServiceResponseListener;
|
||||
|
||||
|
||||
public AppsManager(FailureHandler failureCallback) {
|
||||
this.failureCallback = failureCallback;
|
||||
}
|
||||
|
||||
public void setupRequestService(ServiceResponseListener<RequestInteractionResponse> serviceResponseListener) {
|
||||
this.requestServiceResponseListener = serviceResponseListener;
|
||||
}
|
||||
|
||||
public void setupGetAppsService(ServiceResponseListener<GetRolesAndAppsResponse> serviceResponseListener) {
|
||||
this.getAppsServiceResponseListener = serviceResponseListener;
|
||||
}
|
||||
|
||||
public void setupAppInfoService(ServiceResponseListener<GetAppResponse> serviceResponseListener) {
|
||||
this.appInfoServiceResponseListener = serviceResponseListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
super.finalize();
|
||||
shutdown(); // TODO not warrantied to be called, so user should call shutdown; finalize works at all in Android??? I think no....
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
if (nodeMainExecutorService != null)
|
||||
nodeMainExecutorService.shutdownNodeMain(this);
|
||||
else
|
||||
Log.w("AppsMng", "Shutting down an uninitialized apps manager");
|
||||
}
|
||||
|
||||
public void getAppsForRole(final MasterId masterId, final String role) {
|
||||
this.action = Action.GET_APPS_FOR_ROLE;
|
||||
this.role = role;
|
||||
|
||||
// If this is the first action requested, we need a connected node, what must be done in a different thread
|
||||
// The requested action will be executed once we have a connected node (this object itself) in onStart method
|
||||
if (this.connectedNode == null) {
|
||||
Log.d("AppsMng", "First action requested (" + this.action + "). Starting node...");
|
||||
new ConnectNodeThread(masterId).start();
|
||||
}
|
||||
else {
|
||||
// But we don't need all this if we already executed an action, and so have a connected node. Anyway,
|
||||
// we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
getAppsForRole();
|
||||
return null;
|
||||
}
|
||||
}.execute(); // TODO: can we use this to incorporate a timeout to service calls?
|
||||
}
|
||||
}
|
||||
|
||||
public void requestAppUse(final MasterId masterId, final String role, final rocon_interaction_msgs.RemoconApp app) {
|
||||
this.action = Action.REQUEST_APP_USE;
|
||||
this.role = role;
|
||||
this.app = app;
|
||||
|
||||
// If this is the first action requested, we need a connected node, what must be done in a different thread
|
||||
// The requested action will be executed once we have a connected node (this object itself) in onStart method
|
||||
if (this.connectedNode == null) {
|
||||
Log.d("AppsMng", "First action requested (" + this.action + "). Starting node...");
|
||||
new ConnectNodeThread(masterId).start();
|
||||
}
|
||||
else {
|
||||
// But we don't need all this if we already executed an action, and so have a connected node. Anyway,
|
||||
// we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
requestAppUse();
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public void getAppInfo(final MasterId masterId, final int hash) {
|
||||
this.action = Action.GET_APP_INFO;
|
||||
this.app_hash = hash;
|
||||
|
||||
// If this is the first action requested, we need a connected node, what must be done in a different thread
|
||||
// The requested action will be executed once we have a connected node (this object itself) in onStart method
|
||||
if (this.connectedNode == null) {
|
||||
Log.d("AppsMng", "First action requested (" + this.action + "). Starting node...");
|
||||
new ConnectNodeThread(masterId).start();
|
||||
}
|
||||
else {
|
||||
// But we don't need all this if we already executed an action, and so have a connected node. Anyway,
|
||||
// we must execute any action asynchronously to avoid the android.os.NetworkOnMainThreadException
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
getAppInfo();
|
||||
return null;
|
||||
}
|
||||
}.execute(); // TODO: can we use this to incorporate a timeout to service calls?
|
||||
}
|
||||
}
|
||||
|
||||
private void getAppsForRole() {
|
||||
// call get_roles_and_apps concert service
|
||||
ServiceClient<GetRolesAndAppsRequest, GetRolesAndAppsResponse> srvClient;
|
||||
try {
|
||||
Log.d("AppsMng", "List apps service client created [" + GET_ROLES_AND_APPS_SRV + "]");
|
||||
srvClient = connectedNode.newServiceClient(GET_ROLES_AND_APPS_SRV, GetRolesAndApps._TYPE);
|
||||
} catch (ServiceNotFoundException e) {
|
||||
Log.w("AppsMng", "List apps service not found [" + GET_ROLES_AND_APPS_SRV + "]");
|
||||
throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
|
||||
}
|
||||
final GetRolesAndAppsRequest request = srvClient.newMessage();
|
||||
|
||||
request.getRoles().add(role);
|
||||
request.setPlatformInfo(ANDROID_PLATFORM_INFO);
|
||||
|
||||
srvClient.call(request, getAppsServiceResponseListener);
|
||||
Log.d("AppsMng", "List apps service call done [" + GET_ROLES_AND_APPS_SRV + "]");
|
||||
}
|
||||
|
||||
private void requestAppUse() {
|
||||
// call request_interaction concert service
|
||||
ServiceClient<RequestInteractionRequest, RequestInteractionResponse> srvClient;
|
||||
try {
|
||||
Log.d("AppsMng", "Request app service client created [" + REQUEST_INTERACTION_SRV + "]");
|
||||
srvClient = connectedNode.newServiceClient(REQUEST_INTERACTION_SRV, RequestInteraction._TYPE);
|
||||
} catch (ServiceNotFoundException e) {
|
||||
Log.w("AppsMng", "Request app service not found [" + REQUEST_INTERACTION_SRV + "]");
|
||||
throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
|
||||
}
|
||||
final RequestInteractionRequest request = srvClient.newMessage();
|
||||
|
||||
request.setRole(role);
|
||||
request.setApplication(app.getName());
|
||||
request.setServiceName(app.getServiceName());
|
||||
request.setPlatformInfo(ANDROID_PLATFORM_INFO);
|
||||
|
||||
srvClient.call(request, requestServiceResponseListener);
|
||||
Log.d("AppsMng", "Request app service call done [" + REQUEST_INTERACTION_SRV + "]");
|
||||
}
|
||||
|
||||
private void getAppInfo() {
|
||||
// call get_app concert service
|
||||
ServiceClient<GetAppRequest, GetAppResponse> srvClient;
|
||||
try {
|
||||
Log.d("AppsMng", "Get app info service client created [" + GET_APP_INFO_SRV + "]");
|
||||
srvClient = connectedNode.newServiceClient(GET_APP_INFO_SRV, GetApp._TYPE);
|
||||
} catch (ServiceNotFoundException e) {
|
||||
Log.w("AppsMng", "Get app info not found [" + GET_APP_INFO_SRV + "]");
|
||||
throw new RosRuntimeException(e); // TODO we should recover from this calling onFailure on listener
|
||||
}
|
||||
final GetAppRequest request = srvClient.newMessage();
|
||||
|
||||
request.setHash(app_hash);
|
||||
request.setPlatformInfo(ANDROID_PLATFORM_INFO);
|
||||
|
||||
srvClient.call(request, appInfoServiceResponseListener);
|
||||
Log.d("AppsMng", "Get app info service call done [" + GET_APP_INFO_SRV + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a thread to get a node connected with the given masterId. Returns immediately
|
||||
* TODO: what happens if the thread is already running??? kill it first and then start a new one?
|
||||
*/
|
||||
private class ConnectNodeThread extends Thread {
|
||||
private MasterId masterId;
|
||||
|
||||
public ConnectNodeThread(MasterId masterId) {
|
||||
this.masterId = masterId;
|
||||
setDaemon(true);
|
||||
setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
failureCallback.handleFailure("exception: " + ex.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
URI concertUri = new URI(masterId.getMasterUri());
|
||||
|
||||
// Check if the concert exists by looking for concert name parameter
|
||||
// getParam throws when it can't find the parameter.
|
||||
ParameterClient paramClient = new ParameterClient(
|
||||
NodeIdentifier.forNameAndUri("/concert_checker", concertUri.toString()), concertUri);
|
||||
String name = (String) paramClient.getParam(GraphName.of(CONCERT_NAME_PARAM)).getResult();
|
||||
Log.i("ConcertRemocon", "Concert " + name + " found; retrieve additional information");
|
||||
|
||||
nodeMainExecutorService = new NodeMainExecutorService();
|
||||
NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(
|
||||
InetAddressFactory.newNonLoopback().getHostAddress(), concertUri);
|
||||
nodeMainExecutorService.execute(AppsManager.this, nodeConfiguration.setNodeName("apps_manager_node"));
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
Log.w("AppsMng", "invalid concert URI [" + masterId.getMasterUri() + "][" + e.toString() + "]");
|
||||
failureCallback.handleFailure("invalid concert URI");
|
||||
} catch (RuntimeException e) {
|
||||
// thrown if concert could not be found in the getParam call (from java.net.ConnectException)
|
||||
Log.w("AppsMng", "could not find concert [" + masterId.getMasterUri() + "][" + e.toString() + "]");
|
||||
failureCallback.handleFailure(e.toString());
|
||||
} catch (Throwable e) {
|
||||
Log.w("AppsMng", "exception while creating node in concert checker for URI " + masterId.getMasterUri(), e);
|
||||
failureCallback.handleFailure(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphName getDefaultNodeName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Node started by NodeMainExecutor.execute(). Execute the requested action.
|
||||
*
|
||||
* @param connectedNode
|
||||
*/
|
||||
@Override
|
||||
public void onStart(final ConnectedNode connectedNode) {
|
||||
if (this.connectedNode != null) {
|
||||
Log.e("AppsMng", "App manager re-started before previous shutdown; ignoring...");
|
||||
return;
|
||||
}
|
||||
|
||||
this.connectedNode = connectedNode;
|
||||
|
||||
Log.d("AppsMng", "onStart() - " + action);
|
||||
|
||||
switch (action) {
|
||||
case NONE:
|
||||
Log.w("AppsMng", "Node started without specifying an action");
|
||||
break;
|
||||
case REQUEST_APP_USE:
|
||||
requestAppUse();
|
||||
break;
|
||||
case GET_APPS_FOR_ROLE:
|
||||
getAppsForRole();
|
||||
break;
|
||||
case GET_APP_INFO:
|
||||
getAppInfo();
|
||||
break;
|
||||
default:
|
||||
Log.w("AppsMng", "Unrecogniced action requested: " + action);
|
||||
}
|
||||
|
||||
Log.d("AppsMng", "Done");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(Node node) {
|
||||
Log.d("AppsMng", "Shutdown connected node...");
|
||||
super.onShutdown(node);
|
||||
|
||||
// Required so we get reconnected the next time
|
||||
this.connectedNode = null;
|
||||
Log.d("AppsMng", "Done; shutdown apps manager node main");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdownComplete(Node node) {
|
||||
super.onShutdownComplete(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Node node, Throwable throwable) {
|
||||
super.onError(node, throwable);
|
||||
|
||||
Log.e("AppsMng", node.getName().toString() + " node error: " + throwable.getMessage());
|
||||
failureCallback.handleFailure(node.getName().toString() + " node error: " + throwable.toString());
|
||||
}
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2011, Willow Garage, Inc.
|
||||
* Copyright (c) 2013, OSRF.
|
||||
* Copyright (c) 2013, Yujin Robot.
|
||||
*
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.github.rosjava.android_apps.application_management.ConcertDescription;
|
||||
import com.github.rosjava.android_apps.application_management.MasterId;
|
||||
|
||||
import org.ros.address.InetAddressFactory;
|
||||
import org.ros.android.NodeMainExecutorService;
|
||||
import org.ros.internal.node.client.ParameterClient;
|
||||
import org.ros.internal.node.server.NodeIdentifier;
|
||||
import org.ros.namespace.GraphName;
|
||||
import org.ros.node.NodeConfiguration;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.github.rosjava.android_remocons.common_tools.RoconConstants.*;
|
||||
|
||||
/**
|
||||
* Threaded ROS-concert checker. Runs a thread which checks for a valid ROS
|
||||
* concert and sends back a {@link ConcertDescription} (with concert name and type)
|
||||
* on success or a failure reason on failure.
|
||||
*
|
||||
* @author hersh@willowgarage.com
|
||||
* @author murase@jsk.imi.i.u-tokyo.ac.jp (Kazuto Murase)
|
||||
* @author jorge@yujinrobot.com (Jorge Santos Simon)
|
||||
*/
|
||||
public class ConcertChecker {
|
||||
public interface ConcertDescriptionReceiver {
|
||||
/**
|
||||
* Called on success with a description of the concert that got checked.
|
||||
*/
|
||||
void receive(ConcertDescription concertDescription);
|
||||
}
|
||||
|
||||
public interface FailureHandler {
|
||||
/**
|
||||
* Called on failure with a short description of why it failed, like
|
||||
* "exception" or "timeout".
|
||||
*/
|
||||
void handleFailure(String reason);
|
||||
}
|
||||
|
||||
private CheckerThread checkerThread;
|
||||
private ConcertDescriptionReceiver foundConcertCallback;
|
||||
private FailureHandler failureCallback;
|
||||
|
||||
/**
|
||||
* Constructor. Should not take any time.
|
||||
*/
|
||||
public ConcertChecker(ConcertDescriptionReceiver foundConcertCallback, FailureHandler failureCallback) {
|
||||
this.foundConcertCallback = foundConcertCallback;
|
||||
this.failureCallback = failureCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the checker thread with the given masterId. If the thread is
|
||||
* already running, kill it first and then start anew. Returns immediately.
|
||||
*/
|
||||
public void beginChecking(MasterId masterId) {
|
||||
stopChecking();
|
||||
if (masterId.getMasterUri() == null) {
|
||||
failureCallback.handleFailure("empty concert URI");
|
||||
return;
|
||||
}
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI(masterId.getMasterUri());
|
||||
} catch (URISyntaxException e) {
|
||||
failureCallback.handleFailure("invalid concert URI");
|
||||
return;
|
||||
}
|
||||
checkerThread = new CheckerThread(masterId, uri);
|
||||
checkerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the checker thread.
|
||||
*/
|
||||
public void stopChecking() {
|
||||
if (checkerThread != null && checkerThread.isAlive()) {
|
||||
checkerThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private class CheckerThread extends Thread {
|
||||
private URI concertUri;
|
||||
private MasterId masterId;
|
||||
|
||||
public CheckerThread(MasterId masterId, URI concertUri) {
|
||||
this.concertUri = concertUri;
|
||||
this.masterId = masterId;
|
||||
setDaemon(true);
|
||||
// don't require callers to explicitly kill all the old checker threads.
|
||||
setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
failureCallback.handleFailure("exception: " + ex.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Check if the concert exists by looking for concert name parameter
|
||||
// getParam throws when it can't find the parameter.
|
||||
ParameterClient paramClient = new ParameterClient(
|
||||
NodeIdentifier.forNameAndUri("/concert_checker", concertUri.toString()), concertUri);
|
||||
String name = (String) paramClient.getParam(GraphName.of(CONCERT_NAME_PARAM)).getResult();
|
||||
Log.i("ConcertRemocon", "Concert " + name + " found; retrieve additional information");
|
||||
|
||||
NodeMainExecutorService nodeMainExecutorService = new NodeMainExecutorService();
|
||||
NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(
|
||||
InetAddressFactory.newNonLoopback().getHostAddress(), concertUri);
|
||||
|
||||
// Check for the concert information topic
|
||||
ListenerNode<concert_msgs.ConcertInfo> readInfoTopic =
|
||||
new ListenerNode(CONCERT_INFO_TOPIC, concert_msgs.ConcertInfo._TYPE);
|
||||
nodeMainExecutorService.execute(readInfoTopic, nodeConfiguration.setNodeName("read_info_node"));
|
||||
readInfoTopic.waitForResponse();
|
||||
|
||||
|
||||
concert_msgs.ConcertInfo concertInfo = readInfoTopic.getLastMessage();
|
||||
|
||||
String concertName = concertInfo.getName();
|
||||
String concertDesc = concertInfo.getDescription();
|
||||
rocon_std_msgs.Icon concertIcon = concertInfo.getIcon();
|
||||
|
||||
if (name.equals(concertName) == false)
|
||||
Log.w("ConcertRemocon", "Concert names from parameter and topic differs; we use the later");
|
||||
|
||||
// Check for the concert roles topic
|
||||
ListenerNode<concert_msgs.Roles> readRolesTopic =
|
||||
new ListenerNode(CONCERT_ROLES_TOPIC, concert_msgs.Roles._TYPE);
|
||||
nodeMainExecutorService.execute(readRolesTopic, nodeConfiguration.setNodeName("concert_roles_node"));
|
||||
readRolesTopic.waitForResponse();
|
||||
|
||||
nodeMainExecutorService.shutdownNodeMain(readInfoTopic);
|
||||
nodeMainExecutorService.shutdownNodeMain(readRolesTopic);
|
||||
|
||||
// configure concert description
|
||||
Date timeLastSeen = new Date();
|
||||
ConcertDescription description = new ConcertDescription(masterId, concertName, concertDesc, concertIcon, timeLastSeen);
|
||||
Log.i("ConcertRemocon", "Concert is available");
|
||||
description.setConnectionStatus(ConcertDescription.OK);
|
||||
description.setUserRoles(readRolesTopic.getLastMessage());
|
||||
foundConcertCallback.receive(description);
|
||||
return;
|
||||
} catch (RuntimeException e) {
|
||||
// thrown if concert could not be found in the getParam call (from java.net.ConnectException)
|
||||
Log.w("ConcertRemocon", "could not find concert [" + concertUri + "][" + e.toString() + "]");
|
||||
failureCallback.handleFailure(e.toString());
|
||||
} catch (Throwable e) {
|
||||
Log.w("ConcertRemocon", "exception while creating node in concert checker for URI " + concertUri, e);
|
||||
failureCallback.handleFailure(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package com.github.rosjava.android_remocons.common_tools;
|
||||
|
||||
import org.ros.android.MessageCallable;
|
||||
import org.ros.exception.RosException;
|
||||
import org.ros.exception.RosRuntimeException;
|
||||
import org.ros.message.MessageListener;
|
||||
import org.ros.namespace.GraphName;
|
||||
import org.ros.node.AbstractNodeMain;
|
||||
import org.ros.node.ConnectedNode;
|
||||
import org.ros.node.topic.Subscriber;
|
||||
|
||||
|
||||
/**
|
||||
* Created by jorge on 10/30/13.
|
||||
*/
|
||||
public class ListenerNode<T> extends AbstractNodeMain
|
||||
{
|
||||
private String topicName;
|
||||
private String messageType;
|
||||
private T lastMessage;
|
||||
private MessageCallable<String, T> callable;
|
||||
|
||||
public ListenerNode(String topic, String type)
|
||||
{
|
||||
topicName = topic;
|
||||
messageType = type;
|
||||
}
|
||||
|
||||
public T getLastMessage()
|
||||
{
|
||||
return lastMessage;
|
||||
}
|
||||
|
||||
public void setTopicName(String topicName)
|
||||
{
|
||||
this.topicName = topicName;
|
||||
}
|
||||
|
||||
public void setMessageType(String messageType)
|
||||
{
|
||||
this.messageType = messageType;
|
||||
}
|
||||
|
||||
public void setMessageToStringCallable(MessageCallable<String, T> callable)
|
||||
{
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphName getDefaultNodeName() {
|
||||
return GraphName.of("get_" + topicName + "_node");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(ConnectedNode connectedNode) {
|
||||
Subscriber<T> subscriber = connectedNode.newSubscriber(topicName, messageType);
|
||||
subscriber.addMessageListener(new MessageListener<T>() {
|
||||
@Override
|
||||
public void onNewMessage(final T message) {
|
||||
lastMessage = message;
|
||||
if (callable != null) {
|
||||
callable.call(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to block until subscriber receives the first message.
|
||||
*
|
||||
* @throws org.ros.exception.RosException : when it times out waiting for the service.
|
||||
*/
|
||||
public void waitForResponse() throws RosException {
|
||||
int count = 0;
|
||||
while ( lastMessage == null ) {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
} catch (Exception e) {
|
||||
throw new RosRuntimeException(e);
|
||||
}
|
||||
if ( count == 20 ) { // timeout.
|
||||
throw new RosException("timed out waiting for topic messages");
|
||||
}
|
||||
count = count + 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,356 @@
|
||||
package com.github.rosjava.android_remocons.common_tools;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentFilter.MalformedMimeTypeException;
|
||||
import android.nfc.FormatException;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.Tag;
|
||||
import android.nfc.TagLostException;
|
||||
import android.nfc.tech.IsoDep;
|
||||
import android.nfc.tech.MifareClassic;
|
||||
import android.nfc.tech.MifareUltralight;
|
||||
import android.nfc.tech.Ndef;
|
||||
import android.nfc.tech.NdefFormatable;
|
||||
import android.nfc.tech.NfcA;
|
||||
import android.nfc.tech.NfcB;
|
||||
import android.nfc.tech.NfcF;
|
||||
import android.nfc.tech.NfcV;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
public class NfcManager {
|
||||
|
||||
private Context mContext = null;
|
||||
private PendingIntent mPendingIntent = null;
|
||||
private IntentFilter[] mFilters;
|
||||
private String[][] mTechList;
|
||||
private Intent mPassedIntent = null;
|
||||
private NfcAdapter mNfcAdapter = null;
|
||||
private String mCurrentNdefString = "";
|
||||
|
||||
public NfcManager(Context context) {
|
||||
mContext = context;
|
||||
mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
|
||||
|
||||
Intent targetIntent = new Intent(mContext, mContext.getClass());
|
||||
targetIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
mPendingIntent = PendingIntent.getActivity(mContext, 0, targetIntent, 0);
|
||||
|
||||
IntentFilter filter_1 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
|
||||
IntentFilter filter_2 = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);
|
||||
IntentFilter filter_3 = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
|
||||
|
||||
try {
|
||||
filter_1.addDataType("*/*");
|
||||
filter_2.addDataType("*/*");
|
||||
filter_3.addDataType("*/*");
|
||||
} catch (MalformedMimeTypeException e) {
|
||||
throw new RuntimeException("fail", e);
|
||||
}
|
||||
|
||||
mFilters = new IntentFilter[]{filter_1, filter_2, filter_3};
|
||||
mTechList = new String[][]{new String[]{NfcF.class.getName()},
|
||||
new String[]{MifareClassic.class.getName()},
|
||||
new String[]{NfcA.class.getName()},
|
||||
new String[]{NfcB.class.getName()},
|
||||
new String[]{NfcV.class.getName()},
|
||||
new String[]{Ndef.class.getName()},
|
||||
new String[]{NdefFormatable.class.getName()},
|
||||
new String[]{MifareUltralight.class.getName()},
|
||||
new String[]{IsoDep.class.getName()}};
|
||||
}
|
||||
|
||||
public boolean checkNfcStatus() {
|
||||
return mNfcAdapter.isEnabled();
|
||||
}
|
||||
|
||||
public boolean changeNfcStatus(boolean enable) {
|
||||
|
||||
if (mNfcAdapter == null) return false;
|
||||
|
||||
boolean success = false;
|
||||
Class<?> nfcManagerClass = null;
|
||||
Method setNfcEnabled = null, setNfcDisabled = null;
|
||||
|
||||
if (enable) {
|
||||
try {
|
||||
nfcManagerClass = Class.forName(mNfcAdapter.getClass().getName());
|
||||
setNfcEnabled = nfcManagerClass.getDeclaredMethod("enable");
|
||||
setNfcEnabled.setAccessible(true);
|
||||
success = (Boolean) setNfcEnabled.invoke(mNfcAdapter);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
nfcManagerClass = Class.forName(mNfcAdapter.getClass().getName());
|
||||
setNfcDisabled = nfcManagerClass.getDeclaredMethod("disable");
|
||||
setNfcDisabled.setAccessible(true);
|
||||
success = (Boolean) setNfcDisabled.invoke(mNfcAdapter);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean enableForegroundDispatch() {
|
||||
if (mNfcAdapter != null) {
|
||||
mNfcAdapter.enableForegroundDispatch((Activity) mContext, mPendingIntent, mFilters, mTechList);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean disableForegroundDispatch() {
|
||||
|
||||
if (mNfcAdapter != null) {
|
||||
mNfcAdapter.disableForegroundDispatch((Activity) mContext);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onNewIntent(Intent intent) {
|
||||
|
||||
mPassedIntent = intent;
|
||||
String action = mPassedIntent.getAction();
|
||||
|
||||
Toast.makeText(mContext, action, Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) ||
|
||||
NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) ||
|
||||
NfcAdapter.ACTION_NDEF_DISCOVERED.equalsIgnoreCase(action))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public String processTag() {
|
||||
|
||||
if (mPassedIntent == null) return "NFC Tag is not discovered.";
|
||||
|
||||
Parcelable[] rawMsgs = mPassedIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
|
||||
|
||||
if (rawMsgs == null) {
|
||||
return "NDEF Message is null";
|
||||
}
|
||||
|
||||
mCurrentNdefString = "";
|
||||
|
||||
NdefMessage[] msgs = new NdefMessage[rawMsgs.length];
|
||||
|
||||
for (int i = 0; i < rawMsgs.length; i++) {
|
||||
msgs[i] = (NdefMessage) rawMsgs[i];
|
||||
mCurrentNdefString += ndefMessageToString(msgs[i]);
|
||||
}
|
||||
|
||||
return mCurrentNdefString;
|
||||
}
|
||||
|
||||
public String ndefMessageToString(NdefMessage message) {
|
||||
|
||||
String ndefString = "";
|
||||
NdefRecord[] ndef_records = message.getRecords();
|
||||
|
||||
ndefString += "**Num of NdefRecord : " + ndef_records.length + "\n";
|
||||
|
||||
for (int i = 0; i < ndef_records.length; i++) {
|
||||
String temp = "**Record No. " + i + "\n";
|
||||
byte[] type = ndef_records[i].getType();
|
||||
byte[] id = ndef_records[i].getId();
|
||||
byte[] pl = ndef_records[i].getPayload();
|
||||
byte[] arr = ndef_records[i].toByteArray();
|
||||
|
||||
temp = temp + "- TNF=" + ndef_records[i].getTnf() +
|
||||
"\n - TYPE=" + Util.getHexString(type, type.length) + " " + new String(type) +
|
||||
"\n - ID=" + Util.getHexString(id, id.length) + " " + new String(id) +
|
||||
"\n - PayLoad=" + Util.getHexString(pl, pl.length) + " " + new String(pl) +
|
||||
"\n - ByteArray=" + Util.getHexString(arr, arr.length) + " " + new String(arr) + "\n";
|
||||
|
||||
ndefString += temp;
|
||||
}
|
||||
|
||||
return ndefString;
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
|
||||
if (mPassedIntent == null)
|
||||
return null;
|
||||
|
||||
Parcelable[] rawMsgs = mPassedIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
|
||||
|
||||
if (rawMsgs == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NdefMessage[] msgs = new NdefMessage[rawMsgs.length];
|
||||
|
||||
for (int i = 0; i < rawMsgs.length; i++) {
|
||||
msgs[i] = (NdefMessage) rawMsgs[i];
|
||||
}
|
||||
|
||||
NdefRecord[] records = msgs[0].getRecords();
|
||||
if (records.length > 0)
|
||||
return records[0].getPayload();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private NdefRecord createTextRecord(String text, Locale locale, boolean encodeInUtf8) {
|
||||
|
||||
final byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
|
||||
final Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
|
||||
final byte[] textBytes = text.getBytes(utfEncoding);
|
||||
final int utfBit = encodeInUtf8 ? 0 : (1 << 7);
|
||||
final char status = (char) (utfBit + langBytes.length);
|
||||
final byte[] data = Util.concat(new byte[]{(byte) status}, langBytes, textBytes);
|
||||
|
||||
return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
|
||||
}
|
||||
|
||||
public boolean writeTextNdefMessage(String payload, boolean isAAR) {
|
||||
NdefRecord record = createTextRecord(payload, Locale.KOREAN, true);
|
||||
NdefMessage msg = null;
|
||||
if (isAAR)
|
||||
msg = new NdefMessage(new NdefRecord[]{record, NdefRecord.createApplicationRecord(mContext.getPackageName())});
|
||||
else
|
||||
msg = new NdefMessage(new NdefRecord[]{record});
|
||||
|
||||
return writeNdefMessage(msg);
|
||||
}
|
||||
|
||||
public boolean writeTextNdefMessage(byte[] payload, String AAR) {
|
||||
final byte[] langBytes = Locale.KOREAN.getLanguage().getBytes(Charset.forName("US-ASCII"));
|
||||
final Charset utfEncoding = Charset.forName("UTF-8");
|
||||
final int utfBit = 0;
|
||||
final char status = (char) (utfBit + langBytes.length);
|
||||
final byte[] data = Util.concat(new byte[]{(byte) status}, langBytes, payload);
|
||||
|
||||
NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
|
||||
NdefMessage msg = null;
|
||||
if (AAR != null)
|
||||
msg = new NdefMessage(new NdefRecord[]{record, NdefRecord.createApplicationRecord(AAR)});
|
||||
else
|
||||
msg = new NdefMessage(new NdefRecord[]{record});
|
||||
|
||||
return writeNdefMessage(msg);
|
||||
}
|
||||
|
||||
public boolean writeUriNdefMessage(String payload, String AAR) {
|
||||
|
||||
NdefRecord record = new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI,
|
||||
NdefRecord.RTD_URI, new byte[0], payload.getBytes());
|
||||
NdefMessage msg = null;
|
||||
if (AAR != null)
|
||||
msg = new NdefMessage(new NdefRecord[]{record, NdefRecord.createApplicationRecord(AAR)});
|
||||
else
|
||||
msg = new NdefMessage(new NdefRecord[]{record});
|
||||
|
||||
return writeNdefMessage(msg);
|
||||
}
|
||||
|
||||
public boolean writeMimeNdefMessage(String payload, String AAR) {
|
||||
|
||||
NdefRecord record = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
|
||||
("application/" + mContext.getPackageName()).getBytes(), new byte[0], payload.getBytes());
|
||||
NdefMessage msg = null;
|
||||
if (AAR != null)
|
||||
msg = new NdefMessage(new NdefRecord[]{record, NdefRecord.createApplicationRecord(AAR)});
|
||||
else
|
||||
msg = new NdefMessage(new NdefRecord[]{record});
|
||||
|
||||
return writeNdefMessage(msg);
|
||||
}
|
||||
|
||||
// This needs API 16 and we will not use it by now, so commented
|
||||
// public boolean writeCustomNdefMessage(String payload, String AAR) {
|
||||
//
|
||||
// NdefRecord record = NdefRecord.createExternal("Yujin Robot", "Cafe demo", payload.getBytes());
|
||||
// NdefMessage msg = null ;
|
||||
// if (AAR != null)
|
||||
// msg = new NdefMessage(new NdefRecord[] {record, NdefRecord.createApplicationRecord(AAR)});
|
||||
// else
|
||||
// msg = new NdefMessage(new NdefRecord[] {record});
|
||||
//
|
||||
// return writeNdefMessage(msg);
|
||||
// }
|
||||
|
||||
public boolean writeNdefMessage(NdefMessage message) {
|
||||
|
||||
if (mPassedIntent == null) return false;
|
||||
|
||||
Tag tag = mPassedIntent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
|
||||
Ndef ndefTag = Ndef.get(tag);
|
||||
|
||||
try {
|
||||
ndefTag.connect();
|
||||
} catch (IOException e) {
|
||||
Log.e("NfcWriter", "Connect to tag failed. " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ndefTag.writeNdefMessage(message);
|
||||
} catch (TagLostException e) {
|
||||
Log.e("NfcWriter", "The tag left the field. " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
Log.e("NfcWriter", "Message writing failure. " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} catch (FormatException e) {
|
||||
Log.e("NfcWriter", "Malformed NDEF message. " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
ndefTag.close();
|
||||
} catch (IOException e) {
|
||||
Log.e("NfcWriter", "Close tag failed. " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.github.rosjava.android_remocons.common_tools"
|
||||
>
|
||||
</manifest>
|
@ -0,0 +1,15 @@
|
||||
package com.github.rosjava.android_remocons.common_tools.apps;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Just to provide a get method with default value to LinkedHashMap
|
||||
* Created by jorge on 11/26/13.
|
||||
*/
|
||||
public class AppParameters extends LinkedHashMap<String, Object> {
|
||||
public AppParameters() { super(); } // Required by snake yaml
|
||||
|
||||
public Object get(String key, Object def) {
|
||||
return super.containsKey(key) ? super.get(key) : def;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.github.rosjava.android_remocons.common_tools.apps;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Just to provide a get method with default value to LinkedHashMap
|
||||
* Created by jorge on 11/26/13.
|
||||
*/
|
||||
public class AppRemappings extends LinkedHashMap<String, String> {
|
||||
public String get(String from) {
|
||||
return super.containsKey(from) ? super.get(from) : from;
|
||||
}
|
||||
|
||||
public String get(String from, String to) {
|
||||
return super.containsKey(from) ? super.get(from) : to;
|
||||
}
|
||||
}
|
@ -0,0 +1,115 @@
|
||||
package com.github.rosjava.android_remocons.common_tools.apps;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.github.rosjava.android_remocons.common_tools.master.MasterDescription;
|
||||
|
||||
import org.ros.master.client.MasterStateClient;
|
||||
import org.ros.master.client.SystemState;
|
||||
import org.ros.master.client.TopicSystemState;
|
||||
import org.ros.namespace.GraphName;
|
||||
import org.ros.namespace.NameResolver;
|
||||
import org.ros.node.AbstractNodeMain;
|
||||
import org.ros.node.ConnectedNode;
|
||||
|
||||
public class MasterNameResolver extends AbstractNodeMain {
|
||||
|
||||
private MasterDescription currentMaster;
|
||||
private NameResolver masterNameResolver;
|
||||
private GraphName masterName;
|
||||
private ConnectedNode connectedNode;
|
||||
private boolean resolved = false;
|
||||
|
||||
public MasterNameResolver() {
|
||||
}
|
||||
|
||||
public void setMaster(MasterDescription currentMaster) {
|
||||
this.currentMaster = currentMaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphName getDefaultNodeName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setMasterName(String name) {
|
||||
this.masterName = GraphName.of(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the master name as is that will be resolved when actually
|
||||
* connected to a node.
|
||||
*
|
||||
* @return the name, e.g. 'turtlebot'.
|
||||
*/
|
||||
public String getMasterName() {
|
||||
return this.masterName.toString();
|
||||
}
|
||||
|
||||
public void resetMasterName(String name) {
|
||||
masterNameResolver = connectedNode.getResolver().newChild(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The resolved master namespace (after connecting to a master).
|
||||
*
|
||||
* Warning: Do not call this before actually starting the resolver,
|
||||
* or else it will return a null object.
|
||||
*
|
||||
* todo : get this to throw an exception if null
|
||||
*
|
||||
* @return the master name resolver
|
||||
*/
|
||||
public NameResolver getMasterNameSpace() {
|
||||
return masterNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to block until the resolver finishes its job.
|
||||
* i.e. after an execute is called to run the onStart method
|
||||
* below.
|
||||
*
|
||||
* Note - BLOCKING call!
|
||||
*/
|
||||
public void waitForResolver() {
|
||||
while (!resolved) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (Exception e) {
|
||||
Log.w("MasterRemocon", "Master name waitForResolver caught an arbitrary exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Resolves the namespace under which master apps can be started
|
||||
* and stopped. Sometimes this will already have been provided
|
||||
* via setMaster() by managed applications (e.g. remocons) which
|
||||
* use the MasterChecker.
|
||||
*
|
||||
* In other cases, such as standalone application we do a simple
|
||||
* parameter lookup, falling back to a default if provided.
|
||||
*/
|
||||
public void onStart(final ConnectedNode connectedNode) {
|
||||
this.connectedNode = connectedNode;
|
||||
if (currentMaster != null) {
|
||||
masterName = GraphName.of(currentMaster.getAppsNameSpace());
|
||||
} else {
|
||||
// This is duplicated in PlatformInfoServiceClient and could be better stored somewhere, but it's not much.
|
||||
MasterStateClient masterClient = new MasterStateClient(this.connectedNode, this.connectedNode.getMasterUri());
|
||||
SystemState systemState = masterClient.getSystemState();
|
||||
for (TopicSystemState topic : systemState.getTopics()) {
|
||||
String topicName = topic.getTopicName();
|
||||
GraphName graph_name = GraphName.of(topicName);
|
||||
if ( graph_name.getBasename().toString().equals("app_list") ) {
|
||||
masterName = graph_name.getParent().toRelative();
|
||||
Log.i("ApplicationManagement", "Configuring master namespace resolver [" + masterName + "]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
masterNameResolver = connectedNode.getResolver().newChild(masterName);
|
||||
resolved = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (C) 2013 OSRF.
|
||||
* Copyright (c) 2013, Yujin Robot.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools.apps;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.github.robotics_in_concert.rocon_rosjava_core.rocon_interactions.InteractionMode;
|
||||
import com.github.rosjava.android_remocons.common_tools.dashboards.Dashboard;
|
||||
import com.github.rosjava.android_remocons.common_tools.master.MasterDescription;
|
||||
import com.github.rosjava.android_remocons.common_tools.rocon.Constants;
|
||||
|
||||
import org.ros.address.InetAddressFactory;
|
||||
import org.ros.android.RosActivity;
|
||||
import org.ros.exception.RosRuntimeException;
|
||||
import org.ros.namespace.NameResolver;
|
||||
import org.ros.node.NodeConfiguration;
|
||||
import org.ros.node.NodeMainExecutor;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* @author murase@jsk.imi.i.u-tokyo.ac.jp (Kazuto Murase)
|
||||
* @author jorge@yujinrobot.com (Jorge Santos Simon)
|
||||
*
|
||||
* Modified to work in standalone, paired (robot) and concert modes.
|
||||
* Also now handles parameters and remappings.
|
||||
*/
|
||||
public abstract class RosAppActivity extends RosActivity {
|
||||
|
||||
/*
|
||||
By default we assume the ros app activity is launched independently. The following attribute is
|
||||
used to identify when it has instead been launched by a controlling application (e.g. remocons)
|
||||
in paired, one-to-one, or concert mode.
|
||||
*/
|
||||
private InteractionMode appMode = InteractionMode.STANDALONE;
|
||||
private String masterAppName = null;
|
||||
private String defaultMasterAppName = null;
|
||||
private String defaultMasterName = "";
|
||||
private String androidApplicationName; // descriptive helper only
|
||||
private String remoconActivity = null; // The remocon activity to start when finishing this app
|
||||
// e.g. com.github.rosjava.android_remocons.robot_remocon.RobotRemocon
|
||||
private Serializable remoconExtraData = null; // Extra data for remocon (something inheriting from MasterDescription)
|
||||
|
||||
private int dashboardResourceId = 0;
|
||||
private int mainWindowId = 0;
|
||||
private Dashboard dashboard = null;
|
||||
private NodeConfiguration nodeConfiguration;
|
||||
private NodeMainExecutor nodeMainExecutor;
|
||||
protected MasterNameResolver masterNameResolver;
|
||||
protected MasterDescription masterDescription;
|
||||
|
||||
// By now params and remaps are only available for concert apps; that is, appMode must be CONCERT
|
||||
protected AppParameters params = new AppParameters();
|
||||
protected AppRemappings remaps = new AppRemappings();
|
||||
|
||||
protected void setDashboardResource(int resource) {
|
||||
dashboardResourceId = resource;
|
||||
}
|
||||
|
||||
protected void setMainWindowResource(int resource) {
|
||||
mainWindowId = resource;
|
||||
}
|
||||
|
||||
protected void setDefaultMasterName(String name) {
|
||||
defaultMasterName = name;
|
||||
}
|
||||
|
||||
protected void setDefaultAppName(String name) {
|
||||
defaultMasterAppName = name;
|
||||
}
|
||||
|
||||
protected void setCustomDashboardPath(String path) {
|
||||
dashboard.setCustomDashboardPath(path);
|
||||
}
|
||||
|
||||
protected RosAppActivity(String notificationTicker, String notificationTitle) {
|
||||
super(notificationTicker, notificationTitle);
|
||||
this.androidApplicationName = notificationTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (mainWindowId == 0) {
|
||||
Log.e("RosApp",
|
||||
"You must set the dashboard resource ID in your RosAppActivity");
|
||||
return;
|
||||
}
|
||||
if (dashboardResourceId == 0) {
|
||||
Log.e("RosApp",
|
||||
"You must set the dashboard resource ID in your RosAppActivity");
|
||||
return;
|
||||
}
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
setContentView(mainWindowId);
|
||||
|
||||
masterNameResolver = new MasterNameResolver();
|
||||
|
||||
if (defaultMasterName != null) {
|
||||
masterNameResolver.setMasterName(defaultMasterName);
|
||||
}
|
||||
|
||||
// FAKE concert remocon invocation
|
||||
// MasterId mid = new MasterId("http://192.168.10.129:11311", "http://192.168.10.129:11311", "DesertStorm3", "WEP2", "yujin0610");
|
||||
// MasterDescription md = MasterDescription.createUnknown(mid);
|
||||
// getIntent().putExtra(MasterDescription.UNIQUE_KEY, md);
|
||||
// getIntent().putExtra(AppManager.PACKAGE + ".concert_app_name", "KKKK");
|
||||
// getIntent().putExtra("PairedManagerActivity", "com.github.rosjava.android_remocons.rocon_remocon.Remocon");
|
||||
// getIntent().putExtra("ChooserURI", "http://192.168.10.129:11311");
|
||||
// getIntent().putExtra("Parameters", "{pickup_point: pickup}");
|
||||
// getIntent().putExtra("Remappings", "{ 'cmd_vel':'/robot_teleop/cmd_vel', 'image_color':'/robot_teleop/image_color/compressed_throttle' }");
|
||||
|
||||
// FAKE robot remocon invocation
|
||||
// MasterId mid = new MasterId("http://192.168.10.211:11311", "http://192.168.10.167:11311", "DesertStorm3", "WEP2", "yujin0610");
|
||||
// MasterDescription md = MasterDescription.createUnknown(mid);
|
||||
// md.setMasterName("grieg");
|
||||
// md.setMasterType("turtlebot");
|
||||
// getIntent().putExtra(MasterDescription.UNIQUE_KEY, md);
|
||||
// getIntent().putExtra(AppManager.PACKAGE + ".paired_app_name", "KKKK");
|
||||
// getIntent().putExtra("PairedManagerActivity", "com.github.rosjava.android_remocons.robot_remocon.RobotRemocon");
|
||||
//// getIntent().putExtra("RemoconURI", "http://192.168.10.129:11311");
|
||||
// getIntent().putExtra("Parameters", "{pickup_point: pickup}");
|
||||
// getIntent().putExtra("Remappings", "{ 'cmd_vel':'/robot_teleop/cmd_vel', 'image_color':'/robot_teleop/image_color/compressed_throttle' }");
|
||||
|
||||
|
||||
for (InteractionMode mode : InteractionMode.values()) {
|
||||
// The remocon specifies its type in the app name extra content string, useful information for the app
|
||||
masterAppName = getIntent().getStringExtra(Constants.ACTIVITY_SWITCHER_ID + "." + mode + "_app_name");
|
||||
if (masterAppName != null) {
|
||||
appMode = mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (masterAppName == null) {
|
||||
// App name extra content key not present on intent; no remocon started the app, so we are standalone app
|
||||
Log.e("RosApp", "We are running as standalone :(");
|
||||
masterAppName = defaultMasterAppName;
|
||||
appMode = InteractionMode.STANDALONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Managed app; take from the intent all the fancy stuff remocon put there for us
|
||||
|
||||
// Extract parameters and remappings from a YAML-formatted strings; translate into hash maps
|
||||
// We create empty maps if the strings are missing to avoid continuous if ! null checks
|
||||
Yaml yaml = new Yaml();
|
||||
|
||||
String paramsStr = getIntent().getStringExtra("Parameters");
|
||||
String remapsStr = getIntent().getStringExtra("Remappings");
|
||||
|
||||
Log.d("RosApp", "Parameters: " + paramsStr);
|
||||
Log.d("RosApp", "Remappings: " + remapsStr);
|
||||
|
||||
try {
|
||||
if ((paramsStr != null) && (! paramsStr.isEmpty())) {
|
||||
LinkedHashMap<String, Object> paramsList = (LinkedHashMap<String, Object>)yaml.load(paramsStr);
|
||||
if (paramsList != null) {
|
||||
params.putAll(paramsList);
|
||||
Log.d("RosApp", "Parameters: " + paramsStr);
|
||||
}
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
Log.e("RosApp", "Cannot cast parameters yaml string to a hash map (" + paramsStr + ")");
|
||||
throw new RosRuntimeException("Cannot cast parameters yaml string to a hash map (" + paramsStr + ")");
|
||||
}
|
||||
|
||||
try {
|
||||
if ((remapsStr != null) && (! remapsStr.isEmpty())) {
|
||||
LinkedHashMap<String, String> remapsList = (LinkedHashMap<String, String>)yaml.load(remapsStr);
|
||||
if (remapsList != null) {
|
||||
remaps.putAll(remapsList);
|
||||
Log.d("RosApp", "Remappings: " + remapsStr);
|
||||
}
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
Log.e("RosApp", "Cannot cast parameters yaml string to a hash map (" + remapsStr + ")");
|
||||
throw new RosRuntimeException("Cannot cast parameters yaml string to a hash map (" + remapsStr + ")");
|
||||
}
|
||||
|
||||
remoconActivity = getIntent().getStringExtra("RemoconActivity");
|
||||
|
||||
// Master description is mandatory on managed apps, as it contains master URI
|
||||
if (getIntent().hasExtra(MasterDescription.UNIQUE_KEY)) {
|
||||
// Keep a non-casted copy of the master description, so we don't lose the inheriting object
|
||||
// when switching back to the remocon. Not fully sure why this works and not if casting
|
||||
remoconExtraData = getIntent().getSerializableExtra(MasterDescription.UNIQUE_KEY);
|
||||
|
||||
try {
|
||||
masterDescription =
|
||||
(MasterDescription) getIntent().getSerializableExtra(MasterDescription.UNIQUE_KEY);
|
||||
} catch (ClassCastException e) {
|
||||
Log.e("RosApp", "Master description expected on intent on " + appMode + " mode");
|
||||
throw new RosRuntimeException("Master description expected on intent on " + appMode + " mode");
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO how should I handle these things? try to go back to remocon? Show a message?
|
||||
Log.e("RosApp", "Master description missing on intent on " + appMode + " mode");
|
||||
throw new RosRuntimeException("Master description missing on intent on " + appMode + " mode");
|
||||
}
|
||||
}
|
||||
|
||||
if (dashboard == null) {
|
||||
dashboard = new Dashboard(this);
|
||||
dashboard.setView((LinearLayout) findViewById(dashboardResourceId),
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(NodeMainExecutor nodeMainExecutor) {
|
||||
this.nodeMainExecutor = nodeMainExecutor;
|
||||
nodeConfiguration = NodeConfiguration.newPublic(InetAddressFactory
|
||||
.newNonLoopback().getHostAddress(), getMasterUri());
|
||||
|
||||
if (appMode == InteractionMode.STANDALONE) {
|
||||
dashboard.setRobotName(masterNameResolver.getMasterName());
|
||||
}
|
||||
else {
|
||||
masterNameResolver.setMaster(masterDescription);
|
||||
dashboard.setRobotName(masterDescription.getMasterName()); // TODO dashboard not working for concerted apps (Issue #32)
|
||||
|
||||
if (appMode == InteractionMode.PAIRED) {
|
||||
dashboard.setRobotName(masterDescription.getMasterType());
|
||||
}
|
||||
}
|
||||
|
||||
// Run master namespace resolver
|
||||
nodeMainExecutor.execute(masterNameResolver, nodeConfiguration.setNodeName("masterNameResolver"));
|
||||
masterNameResolver.waitForResolver();
|
||||
|
||||
nodeMainExecutor.execute(dashboard, nodeConfiguration.setNodeName("dashboard"));
|
||||
}
|
||||
|
||||
protected NameResolver getMasterNameSpace() {
|
||||
return masterNameResolver.getMasterNameSpace();
|
||||
}
|
||||
|
||||
protected void onAppTerminate() {
|
||||
RosAppActivity.this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
new AlertDialog.Builder(RosAppActivity.this)
|
||||
.setTitle("App Termination")
|
||||
.setMessage(
|
||||
"The application has terminated on the server, so the client is exiting.")
|
||||
.setCancelable(false)
|
||||
.setNeutralButton("Exit",
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
RosAppActivity.this.finish();
|
||||
}
|
||||
}).create().show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startMasterChooser() {
|
||||
if (appMode == InteractionMode.STANDALONE) {
|
||||
super.startMasterChooser();
|
||||
} else {
|
||||
try {
|
||||
nodeMainExecutorService.setMasterUri(new URI(masterDescription.getMasterUri()));
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
RosAppActivity.this.init(nodeMainExecutorService);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
} catch (URISyntaxException e) {
|
||||
// Remocon cannot be such a bastard to send as a wrong URI...
|
||||
throw new RosRuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void releaseMasterNameResolver() {
|
||||
nodeMainExecutor.shutdownNodeMain(masterNameResolver);
|
||||
}
|
||||
|
||||
protected void releaseDashboardNode() {
|
||||
nodeMainExecutor.shutdownNodeMain(dashboard);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this ros app activity should be responsible for
|
||||
* starting and stopping a paired master application.
|
||||
*
|
||||
* This responsibility is relinquished if the application
|
||||
* is controlled from a remocon, but required if the
|
||||
* android application is connecting and running directly.
|
||||
*
|
||||
* @return boolean : true if it needs to be managed.
|
||||
*/
|
||||
private boolean managePairedRobotApplication() {
|
||||
return ((appMode == InteractionMode.STANDALONE) && (masterAppName != null));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (appMode != InteractionMode.STANDALONE) { // i.e. it's a managed app
|
||||
Log.i("RosApp", "app terminating and returning control to the remocon.");
|
||||
// Restart the remocon, supply it with the necessary information and stop this activity
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(Constants.ACTIVITY_SWITCHER_ID + "." + appMode + "_app_name", "AppChooser");
|
||||
intent.putExtra(MasterDescription.UNIQUE_KEY, remoconExtraData);
|
||||
intent.setAction(remoconActivity);
|
||||
intent.addCategory("android.intent.category.DEFAULT");
|
||||
startActivity(intent);
|
||||
finish();
|
||||
} else {
|
||||
Log.i("RosApp", "backpress processing for RosAppActivity");
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2011, Willow Garage, Inc.
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools.dashboards;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.ros.namespace.GraphName;
|
||||
import org.ros.node.ConnectedNode;
|
||||
import org.ros.node.Node;
|
||||
import org.ros.node.NodeMain;
|
||||
|
||||
public class Dashboard implements NodeMain {
|
||||
public interface DashboardInterface {
|
||||
/**
|
||||
* Set the ROS Node to use to get status data and connect it up.
|
||||
* Disconnects the previous node if there was one.
|
||||
*/
|
||||
public void onStart(ConnectedNode connectedNode);
|
||||
|
||||
public void onShutdown(Node node);
|
||||
}
|
||||
|
||||
private static final String defaultDashboardPath = "com.github.rosjava.android_remocons.common_tools.dashboards.DefaultDashboard";
|
||||
private static final String turtlebotDashboardPath = "com.github.turtlebot.turtlebot_android.turtlebot_core.dashboards.TurtlebotDashboard";
|
||||
private static final String pr2DashboardPath = "com.ros.pr2.apps.core_components.Pr2Dashboard";
|
||||
|
||||
private DashboardInterface dashboard;
|
||||
private Activity activity;
|
||||
private ViewGroup view;
|
||||
private ViewGroup.LayoutParams lparams;
|
||||
private static String robotName;
|
||||
private static String customDashboardPath;
|
||||
|
||||
public Dashboard(Activity activity) {
|
||||
dashboard = null;
|
||||
this.activity = activity;
|
||||
this.view = null;
|
||||
this.lparams = null;
|
||||
}
|
||||
|
||||
public void setView(ViewGroup view, ViewGroup.LayoutParams lparams) {
|
||||
if (view == null) {
|
||||
Log.e("Dashboard", "Null view for dashboard");
|
||||
}
|
||||
this.view = view;
|
||||
this.lparams = lparams;
|
||||
}
|
||||
|
||||
public void setRobotName(String name) {
|
||||
robotName = name;
|
||||
}
|
||||
|
||||
public void setCustomDashboardPath(String path) {
|
||||
this.customDashboardPath = path;
|
||||
}
|
||||
|
||||
private static DashboardInterface createDashboard(Class dashClass,
|
||||
Context context) {
|
||||
ClassLoader classLoader = Dashboard.class.getClassLoader();
|
||||
Object[] args = new Object[1];
|
||||
DashboardInterface result = null;
|
||||
args[0] = context;
|
||||
try {
|
||||
Class contextClass = Class.forName("android.content.Context");
|
||||
result = (DashboardInterface) dashClass.getConstructor(contextClass).newInstance(args);
|
||||
} catch (Exception ex) {
|
||||
Log.e("Dashboard", "Error during dashboard instantiation:", ex);
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static DashboardInterface createDashboard(String className,
|
||||
Context context) {
|
||||
Class dashClass = null;
|
||||
try {
|
||||
dashClass = Class.forName(className);
|
||||
} catch (Exception ex) {
|
||||
Log.e("Dashboard", "Error during dashboard class loading:", ex);
|
||||
return null;
|
||||
}
|
||||
return createDashboard(dashClass, context);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically locate and create a dashboard.
|
||||
*/
|
||||
// TODO: deal with the custom robot dashboard
|
||||
private static DashboardInterface createDashboard(Context context) {
|
||||
if (customDashboardPath != null) {
|
||||
return createDashboard(customDashboardPath, context);
|
||||
// } else if (robotName.equals("turtlebot")) {
|
||||
// return createDashboard(turtlebotDashboardPath, context);
|
||||
// } else if (robotName.equals("pr2")) {
|
||||
// return createDashboard(pr2DashboardPath, context);
|
||||
} else {
|
||||
return createDashboard(defaultDashboardPath, context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Node arg0, Throwable arg1) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(final Node node) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DashboardInterface dash = dashboard;
|
||||
if (dash != null) {
|
||||
dash.onShutdown(node);
|
||||
view.removeView((View) dash);
|
||||
}
|
||||
dashboard = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdownComplete(Node node) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(ConnectedNode connectedNode) {
|
||||
if (dashboard != null) {
|
||||
// FIXME: should we re-start the dashboard? I think this is really
|
||||
// an error.
|
||||
return;
|
||||
}
|
||||
dashboard = Dashboard.createDashboard(activity);
|
||||
if (dashboard != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
DashboardInterface dash = dashboard;
|
||||
ViewGroup localView = view;
|
||||
if (dash != null && localView != null) {
|
||||
localView.addView((View) dash, lparams);
|
||||
} else if (dash == null) {
|
||||
Log.e("Dashboard",
|
||||
"Dashboard could not start: no dashboard");
|
||||
} else if (view == null) {
|
||||
Log.e("Dashboard", "Dashboard could not start: no view");
|
||||
} else {
|
||||
Log.e("Dashboard",
|
||||
"Dashboard could not start: no view or dashboard");
|
||||
}
|
||||
}
|
||||
});
|
||||
dashboard.onStart(connectedNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphName getDefaultNodeName() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2011, Willow Garage, Inc.
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools.dashboards;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.github.rosjava.android_extras.gingerbread.view.BatteryLevelView;
|
||||
import com.github.rosjava.android_remocons.common_tools.R;
|
||||
|
||||
import org.ros.exception.RosException;
|
||||
import org.ros.message.MessageListener;
|
||||
import org.ros.namespace.GraphName;
|
||||
import org.ros.namespace.NameResolver;
|
||||
import org.ros.node.ConnectedNode;
|
||||
import org.ros.node.Node;
|
||||
import org.ros.node.topic.Subscriber;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import diagnostic_msgs.DiagnosticArray;
|
||||
import diagnostic_msgs.DiagnosticStatus;
|
||||
import diagnostic_msgs.KeyValue;
|
||||
|
||||
public class DefaultDashboard extends LinearLayout implements Dashboard.DashboardInterface {
|
||||
private BatteryLevelView robotBattery;
|
||||
private BatteryLevelView laptopBattery;
|
||||
private ConnectedNode connectedNode;
|
||||
private Subscriber<DiagnosticArray> diagnosticSubscriber;
|
||||
private boolean powerOn = false;
|
||||
|
||||
public DefaultDashboard(Context context) {
|
||||
super(context);
|
||||
inflateSelf(context);
|
||||
}
|
||||
public DefaultDashboard(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
inflateSelf(context);
|
||||
}
|
||||
private void inflateSelf(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.default_dashboard, this);
|
||||
robotBattery = (BatteryLevelView) findViewById(R.id.robot_battery);
|
||||
laptopBattery = (BatteryLevelView) findViewById(R.id.laptop_battery);
|
||||
}
|
||||
/**
|
||||
* Set the ROS Node to use to get status data and connect it up. Disconnects
|
||||
* the previous node if there was one.
|
||||
*
|
||||
* @throws org.ros.exception.RosException
|
||||
*/
|
||||
/**
|
||||
* Populate view with new diagnostic data. This must be called in the UI
|
||||
* thread.
|
||||
*/
|
||||
private void handleDiagnosticArray(DiagnosticArray msg) {
|
||||
for(DiagnosticStatus status : msg.getStatus()) {
|
||||
if(status.getName().equals("/Power System/Battery")) {
|
||||
populateBatteryFromStatus(robotBattery, status);
|
||||
}
|
||||
if(status.getName().equals("/Power System/Laptop Battery")) {
|
||||
populateBatteryFromStatus(laptopBattery, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void populateBatteryFromStatus(BatteryLevelView view, DiagnosticStatus status) {
|
||||
HashMap<String, String> values = keyValueArrayToMap(status.getValues());
|
||||
try {
|
||||
float percent = 100 * Float.parseFloat(values.get("Charge (Ah)")) / Float.parseFloat(values.get("Capacity (Ah)"));
|
||||
view.setBatteryPercent((int) percent);
|
||||
// TODO: set color red/yellow/green based on level (maybe with
|
||||
// level-set
|
||||
// in XML)
|
||||
} catch(NumberFormatException ex) {
|
||||
// TODO: make battery level gray
|
||||
} catch(ArithmeticException ex) {
|
||||
// TODO: make battery level gray
|
||||
} catch(NullPointerException ex) {
|
||||
// Do nothing: data wasn't there.
|
||||
}
|
||||
try {
|
||||
view.setPluggedIn(Float.parseFloat(values.get("Current (A)")) > 0);
|
||||
} catch(NumberFormatException ex) {
|
||||
} catch(ArithmeticException ex) {
|
||||
} catch(NullPointerException ex) {
|
||||
}
|
||||
}
|
||||
private HashMap<String, String> keyValueArrayToMap(List<KeyValue> list) {
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
for(KeyValue kv : list) {
|
||||
map.put(kv.getKey(), kv.getValue());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShutdown(Node node) {
|
||||
if(diagnosticSubscriber != null) {
|
||||
diagnosticSubscriber.shutdown();
|
||||
}
|
||||
diagnosticSubscriber = null;
|
||||
connectedNode = null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart(ConnectedNode connectedNode) {
|
||||
|
||||
this.connectedNode = connectedNode;
|
||||
try {
|
||||
if(diagnosticSubscriber == null){
|
||||
|
||||
}
|
||||
diagnosticSubscriber = connectedNode.newSubscriber("diagnostics_agg", "diagnostic_msgs/DiagnosticArray");
|
||||
diagnosticSubscriber.addMessageListener(new MessageListener<DiagnosticArray>() {
|
||||
@Override
|
||||
public void onNewMessage(final DiagnosticArray message) {
|
||||
DefaultDashboard.this.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DefaultDashboard.this.handleDiagnosticArray(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
NameResolver resolver = connectedNode.getResolver().newChild(GraphName.of("/turtlebot_node"));
|
||||
} catch(Exception ex) {
|
||||
this.connectedNode = null;
|
||||
try {
|
||||
throw (new RosException(ex));
|
||||
} catch (RosException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Willow Garage, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the Willow Garage, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools.layouts;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Checkable;
|
||||
import android.widget.LinearLayout;
|
||||
/**
|
||||
* Simple extension of LinearLayout which trivially implements
|
||||
* Checkable interface and adds state_checked to drawable states when
|
||||
* appropriate.
|
||||
*/
|
||||
public class CheckableLinearLayout extends LinearLayout implements Checkable {
|
||||
private boolean checked = false;
|
||||
private static final int[] CHECKED_STATE_SET = {
|
||||
android.R.attr.state_checked
|
||||
};
|
||||
public CheckableLinearLayout(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
public CheckableLinearLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return checked;
|
||||
}
|
||||
@Override
|
||||
public void setChecked( boolean checked ) {
|
||||
if( this.checked != checked ) {
|
||||
this.checked = checked;
|
||||
refreshDrawableState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggle() {
|
||||
setChecked( !checked );
|
||||
}
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
|
||||
if( isChecked() ) {
|
||||
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
|
||||
}
|
||||
return drawableState;
|
||||
}
|
||||
}
|
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2011, Willow Garage, Inc.
|
||||
* Copyright (c) 2013, OSRF.
|
||||
* Copyright (c) 2013, Yujin Robot.
|
||||
*
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools.master;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.github.robotics_in_concert.rocon_rosjava_core.master_info.MasterInfo;
|
||||
import com.github.robotics_in_concert.rocon_rosjava_core.master_info.MasterInfoException;
|
||||
import com.github.robotics_in_concert.rocon_rosjava_core.rocon_interactions.InteractionsException;
|
||||
import com.github.robotics_in_concert.rocon_rosjava_core.rocon_interactions.RoconInteractions;
|
||||
import com.github.rosjava.android_remocons.common_tools.rocon.Constants;
|
||||
|
||||
import org.ros.address.InetAddressFactory;
|
||||
import org.ros.android.NodeMainExecutorService;
|
||||
import org.ros.internal.node.client.ParameterClient;
|
||||
import org.ros.internal.node.server.NodeIdentifier;
|
||||
import org.ros.internal.node.xmlrpc.XmlRpcTimeoutException;
|
||||
import org.ros.namespace.GraphName;
|
||||
import org.ros.node.NodeConfiguration;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Threaded ROS-concert checker. Runs a thread which checks for a valid ROS
|
||||
* concert and sends back a {@link RoconDescription} (with concert name and type)
|
||||
* on success or a failure reason on failure.
|
||||
*
|
||||
* @author hersh@willowgarage.com
|
||||
* @author murase@jsk.imi.i.u-tokyo.ac.jp (Kazuto Murase)
|
||||
* @author jorge@yujinrobot.com (Jorge Santos Simon)
|
||||
*/
|
||||
public class ConcertChecker {
|
||||
public interface ConcertDescriptionReceiver {
|
||||
/**
|
||||
* Called on success with a description of the concert that got checked.
|
||||
*/
|
||||
void receive(RoconDescription roconDescription);
|
||||
}
|
||||
|
||||
public interface FailureHandler {
|
||||
/**
|
||||
* Called on failure with a short description of why it failed, like
|
||||
* "exception" or "timeout".
|
||||
*/
|
||||
void handleFailure(String reason);
|
||||
}
|
||||
|
||||
private CheckerThread checkerThread;
|
||||
private ConcertDescriptionReceiver foundConcertCallback;
|
||||
private FailureHandler failureCallback;
|
||||
|
||||
/**
|
||||
* Constructor. Should not take any time.
|
||||
*/
|
||||
public ConcertChecker(ConcertDescriptionReceiver foundConcertCallback, FailureHandler failureCallback) {
|
||||
this.foundConcertCallback = foundConcertCallback;
|
||||
this.failureCallback = failureCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the checker thread with the given masterId. If the thread is
|
||||
* already running, kill it first and then start anew. Returns immediately.
|
||||
*/
|
||||
public void beginChecking(MasterId masterId) {
|
||||
stopChecking();
|
||||
if (masterId.getMasterUri() == null) {
|
||||
failureCallback.handleFailure("empty concert URI");
|
||||
return;
|
||||
}
|
||||
URI uri;
|
||||
try {
|
||||
uri = new URI(masterId.getMasterUri());
|
||||
} catch (URISyntaxException e) {
|
||||
failureCallback.handleFailure("invalid concert URI");
|
||||
return;
|
||||
}
|
||||
checkerThread = new CheckerThread(masterId, uri);
|
||||
checkerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the checker thread.
|
||||
*/
|
||||
public void stopChecking() {
|
||||
if (checkerThread != null && checkerThread.isAlive()) {
|
||||
checkerThread.interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
private class CheckerThread extends Thread {
|
||||
private URI concertUri;
|
||||
private MasterId masterId;
|
||||
|
||||
public CheckerThread(MasterId masterId, URI concertUri) {
|
||||
this.concertUri = concertUri;
|
||||
this.masterId = masterId;
|
||||
setDaemon(true);
|
||||
// don't require callers to explicitly kill all the old checker threads.
|
||||
setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
failureCallback.handleFailure("exception: " + ex.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Check if the master exists by looking for the rosversion parameter.
|
||||
// getParam throws when it can't find the parameter (DJS: what does it throw?).
|
||||
// Could get it to look for a hardcoded rocon parameter for extra guarantees
|
||||
// (e.g. /rocon/version) however we'd still have to do some checking below
|
||||
// when the info is there but interactions not.
|
||||
ParameterClient paramClient = new ParameterClient(
|
||||
NodeIdentifier.forNameAndUri("/concert_checker", concertUri.toString()), concertUri);
|
||||
String version = (String) paramClient.getParam(GraphName.of("/rosversion")).getResult();
|
||||
Log.i("Remocon", "r ros master found [" + version + "]");
|
||||
|
||||
NodeMainExecutorService nodeMainExecutorService = new NodeMainExecutorService();
|
||||
NodeConfiguration nodeConfiguration = NodeConfiguration.newPublic(
|
||||
InetAddressFactory.newNonLoopback().getHostAddress(), concertUri);
|
||||
|
||||
// Check for the concert information topic
|
||||
MasterInfo masterInfo = new MasterInfo();
|
||||
RoconInteractions roconInteractions = new RoconInteractions(Constants.ANDROID_PLATFORM_INFO.getUri());
|
||||
|
||||
nodeMainExecutorService.execute(
|
||||
masterInfo,
|
||||
nodeConfiguration.setNodeName("master_info_node")
|
||||
);
|
||||
masterInfo.waitForResponse(); // MasterInfoExc. on timeout, listener or ros runtime errors
|
||||
Log.i("Remocon", "master info found");
|
||||
nodeMainExecutorService.execute(
|
||||
roconInteractions,
|
||||
nodeConfiguration.setNodeName("rocon_interactions_node")
|
||||
);
|
||||
roconInteractions.waitForResponse(); // InteractionsExc. on timeout, service or ros runtime errors
|
||||
Log.i("Remocon", "rocon interactions found");
|
||||
|
||||
// configure concert description
|
||||
Date timeLastSeen = new Date();
|
||||
RoconDescription description = new RoconDescription(
|
||||
masterId,
|
||||
masterInfo.getName(),
|
||||
masterInfo.getDescription(),
|
||||
masterInfo.getIcon(),
|
||||
roconInteractions.getNamespace(),
|
||||
timeLastSeen);
|
||||
|
||||
description.setConnectionStatus(RoconDescription.OK);
|
||||
description.setUserRoles(roconInteractions.getRoles());
|
||||
foundConcertCallback.receive(description);
|
||||
|
||||
nodeMainExecutorService.shutdownNodeMain(masterInfo);
|
||||
nodeMainExecutorService.shutdownNodeMain(roconInteractions);
|
||||
return;
|
||||
} catch (XmlRpcTimeoutException e) {
|
||||
Log.w("Remocon", "timed out trying to connect to the master [" + concertUri + "][" + e.toString() + "]");
|
||||
failureCallback.handleFailure("Timed out trying to connect to the master. Is your network interface up?");
|
||||
} catch (RuntimeException e) {
|
||||
// thrown if there is no master at that url (from java.net.ConnectException)
|
||||
Log.w("Remocon", "connection refused. Is the master running? [" + concertUri + "][" + e.toString() + "]");
|
||||
failureCallback.handleFailure("Connection refused. Is the master running?");
|
||||
} catch (InteractionsException e) {
|
||||
Log.w("Remocon", "rocon interactions unavailable [" + concertUri + "][" + e.toString() + "]");
|
||||
failureCallback.handleFailure("Rocon interactions unavailable [" + e.toString() + "]");
|
||||
} catch (MasterInfoException e) {
|
||||
Log.w("Remocon", "master info unavailable [" + concertUri + "][" + e.toString() + "]");
|
||||
failureCallback.handleFailure("Rocon master info unavailable. Is your ROS_IP set? Is the rocon_master_info node running?");
|
||||
} catch (Throwable e) {
|
||||
Log.w("Remocon", "exception while creating node in concert checker for URI " + concertUri, e);
|
||||
failureCallback.handleFailure("unknown exception in the rocon checker [" + e.toString() + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Software License Agreement (BSD License)
|
||||
*
|
||||
* Copyright (c) 2013, Yujin Robot.
|
||||
*
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
* * Neither the name of Willow Garage, Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.github.rosjava.android_remocons.common_tools.master;
|
||||
|
||||
import org.jboss.netty.buffer.ChannelBuffer;
|
||||
import org.jboss.netty.buffer.ChannelBuffers;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import rocon_std_msgs.Icon;
|
||||
|
||||
/**
|
||||
* Mostly a clone of RobotDescription but generic enough to work also for concert apps.
|
||||
*
|
||||
* @author jorge@yujinrobot.com (Jorge Santos Simon)
|
||||
*/
|
||||
public class MasterDescription implements java.io.Serializable {
|
||||
// Unique identifier for keys passed between android apps.
|
||||
public static final String UNIQUE_KEY = "com.github.rosjava.android_remocons.master.MasterDescription";
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final String CONNECTING = "connecting...";
|
||||
public static final String OK = "ok";
|
||||
public static final String ERROR = "exception";
|
||||
public static final String WIFI = "invalid wifi";
|
||||
public static final String UNAVAILABLE = "unavailable"; // when the master app manager is busy serving another remote controller
|
||||
public static final String CONTROL = "not started";
|
||||
public static final String NAME_UNKNOWN = "Unknown";
|
||||
public static final String TYPE_UNKNOWN = "Unknown";
|
||||
|
||||
private MasterId masterId;
|
||||
private String masterName;
|
||||
private String masterType; // TODO this contains robot type, but in concerts don't make a lot of sense; move to RobotDescription?
|
||||
private String appsNameSpace;
|
||||
|
||||
/** Icon stored piecemeal because msg arrays (stored as jboss ChannelBuffers) can't be
|
||||
* dumped and reloaded by the snakeyaml library. */
|
||||
private String masterIconFormat;
|
||||
private byte[] masterIconData;
|
||||
private int masterIconDataOffset;
|
||||
private int masterIconDataLength;
|
||||
|
||||
private String connectionStatus;
|
||||
private Date timeLastSeen;
|
||||
|
||||
// TODO(kwc): add in canonicalization of masterName
|
||||
public MasterDescription() {
|
||||
}
|
||||
|
||||
public MasterDescription(MasterId masterId, String masterName, String masterType,
|
||||
Icon masterIcon, String appsNameSpace, Date timeLastSeen) {
|
||||
setMasterName(masterName);
|
||||
setMasterId(masterId);
|
||||
this.masterName = masterName;
|
||||
this.masterType = masterType;
|
||||
this.appsNameSpace = appsNameSpace;
|
||||
if (masterIcon != null) {
|
||||
this.masterIconFormat = masterIcon.getFormat();
|
||||
this.masterIconData = masterIcon.getData().array();
|
||||
this.masterIconDataOffset = masterIcon.getData().arrayOffset();
|
||||
this.masterIconDataLength = masterIcon.getData().readableBytes();
|
||||
}
|
||||
this.timeLastSeen = timeLastSeen;
|
||||
}
|
||||
|
||||
public void copyFrom(MasterDescription other) {
|
||||
masterId = other.masterId;
|
||||
masterName = other.masterName;
|
||||
masterType = other.masterType;
|
||||
appsNameSpace = other.appsNameSpace;
|
||||
masterIconFormat = other.masterIconFormat;
|
||||
masterIconData = other.masterIconData;
|
||||
masterIconDataOffset = other.masterIconDataOffset;
|
||||
masterIconDataLength = other.masterIconDataLength;
|
||||
connectionStatus = other.connectionStatus;
|
||||
timeLastSeen = other.timeLastSeen;
|
||||
}
|
||||
|
||||
public MasterId getMasterId() {
|
||||
return masterId;
|
||||
}
|
||||
|
||||
public String getAppsNameSpace() {
|
||||
return appsNameSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience accessor to dig into the master uri for this master.
|
||||
*
|
||||
* @return String : the ros master uri for this master.
|
||||
*/
|
||||
public String getMasterUri() {
|
||||
return masterId.getMasterUri();
|
||||
}
|
||||
|
||||
public void setMasterId(MasterId masterId) {
|
||||
// TODO: ensure the master id is sane.
|
||||
// if(false) {
|
||||
// throw new InvalidMasterDescriptionException("Empty Master URI");
|
||||
// }
|
||||
// TODO: validate
|
||||
this.masterId = masterId;
|
||||
}
|
||||
|
||||
public String getMasterName() {
|
||||
return masterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a human-friendly interpretation of the master name
|
||||
* <p/>
|
||||
* Often we use master names with a uuid suffix to ensure the master name in a
|
||||
* multi-master group is unique. This checks for the suffix and if found, strips it.
|
||||
* <p/>
|
||||
* It also converts '_'s to spaces and first character of each word to uppercase.
|
||||
*
|
||||
* @return human friendly string name
|
||||
*/
|
||||
public String getMasterFriendlyName() {
|
||||
String friendlyName = masterName;
|
||||
// The uuid is a 16 byte hash in hex format = 32 characters
|
||||
if (masterName.length() > 32) {
|
||||
String possibleUuidPart = masterName.substring(masterName.length() - 32);
|
||||
Pattern p = Pattern.compile("[^a-f0-9]");
|
||||
Matcher m = p.matcher(possibleUuidPart);
|
||||
if (!m.find()) {
|
||||
friendlyName = masterName.substring(0, masterName.length() - 32);
|
||||
}
|
||||
}
|
||||
friendlyName = friendlyName.replace('_', ' ');
|
||||
final StringBuilder result = new StringBuilder(friendlyName.length());
|
||||
String[] words = friendlyName.split("\\s");
|
||||
for (int i = 0, l = words.length; i < l; ++i) {
|
||||
if (i > 0) result.append(" ");
|
||||
result.append(Character.toUpperCase(words[i].charAt(0)))
|
||||
.append(words[i].substring(1));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public void setMasterName(String masterName) {
|
||||
// TODO: GraphName validation was removed. What replaced it?
|
||||
// if (!GraphName.validate(masterName)) {
|
||||
// throw new InvalidMasterDescriptionException("Bad master name: " +
|
||||
// masterName);
|
||||
// }
|
||||
this.masterName = masterName;
|
||||
}
|
||||
|
||||
public String getMasterType() {
|
||||
return masterType;
|
||||
}
|
||||
|
||||
public void setMasterType(String masterType) {
|
||||
this.masterType = masterType;
|
||||
}
|
||||
|
||||
public String getMasterIconFormat() {
|
||||
return masterIconFormat;
|
||||
}
|
||||
|
||||
public ChannelBuffer getMasterIconData() {
|
||||
if (masterIconData == null) {
|
||||
return null;
|
||||
} else {
|
||||
ChannelBuffer channelBuffer = ChannelBuffers.copiedBuffer(masterIconData, masterIconDataOffset, masterIconDataLength);
|
||||
return channelBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMasterIconFormat(String iconFormat) {
|
||||
this.masterIconFormat = iconFormat;
|
||||
}
|
||||
|
||||
public void setMasterIconData(ChannelBuffer iconData) {
|
||||
this.masterIconData = iconData.array();
|
||||
}
|
||||
|
||||
public void setMasterIcon(Icon masterIcon) {
|
||||
this.masterIconFormat = masterIcon.getFormat();
|
||||
this.masterIconData = masterIcon.getData().array();
|
||||
}
|
||||
|
||||
public String getConnectionStatus() {
|
||||
return connectionStatus;
|
||||
}
|
||||
|
||||
public void setConnectionStatus(String connectionStatus) {
|
||||
this.connectionStatus = connectionStatus;
|
||||
}
|
||||
|
||||
public Date getTimeLastSeen() {
|
||||
return timeLastSeen;
|
||||
}
|
||||
|
||||
public void setTimeLastSeen(Date timeLastSeen) {
|
||||
this.timeLastSeen = timeLastSeen;
|
||||
}
|
||||
|
||||
public boolean isUnknown() {
|
||||
return this.masterName.equals(NAME_UNKNOWN);
|
||||
}
|
||||
|
||||
// public static MasterDescription createUnknown(MasterId masterId) {
|
||||
// return new MasterDescription(masterId, NAME_UNKNOWN, TYPE_UNKNOWN, null, NAME_UNKNOWN, new Date());
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
// Return true if the objects are identical.
|
||||
// (This is just an optimization, not required for correctness.)
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
// Return false if the other object has the wrong type.
|
||||
// This type may be an interface depending on the interface's
|
||||
// specification.
|
||||
if (!(o instanceof MasterDescription)) {
|
||||
return false;
|
||||
}
|
||||
// Cast to the appropriate type.
|
||||
// This will succeed because of the instanceof, and lets us access
|
||||
// private fields.
|
||||
MasterDescription lhs = (MasterDescription) o;
|
||||
// Check each field. Primitive fields, reference fields, and nullable
|
||||
// reference
|
||||
// fields are all treated differently.
|
||||
return (masterId == null ? lhs.masterId == null : masterId.equals(lhs.masterId));
|
||||
}
|
||||
|
||||
// I need to override equals() so I'm also overriding hashCode() to match.
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Start with a non-zero constant.
|
||||
int result = 17;
|
||||
// Include a hash for each field checked by equals().
|
||||
result = 31 * result + (masterId == null ? 0 : masterId.hashCode());
|
||||
return result;
|
||||
}
|
||||
}
|