看护系统源代码

pzr
Logical 5 years ago
parent 5ca3b74432
commit e83b2665c4

@ -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="&lt;template&gt;" 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="&lt;template&gt;" 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="&lt;template&gt;" 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&#9;" 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,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,122 @@
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 Resetpwd extends AppCompatActivity {
private EditText mAccount; //用户名编辑
private EditText mPwd_old; //密码编辑
private EditText mPwd_new; //密码编辑
private EditText mPwdCheck; //密码编辑
private Button mSureButton; //确定按钮
private Button mCancelButton; //取消按钮
private UserDataManager mUserDataManager; //用户数据管理类
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.resetpwd);
// layout.setOrientation(RelativeLayout.VERTICAL).
mAccount = (EditText) findViewById(R.id.resetpwd_edit_name);
mPwd_old = (EditText) findViewById(R.id.resetpwd_edit_pwd_old);
mPwd_new = (EditText) findViewById(R.id.resetpwd_edit_pwd_new);
mPwdCheck = (EditText) findViewById(R.id.resetpwd_edit_pwd_check);
mSureButton = (Button) findViewById(R.id.resetpwd_btn_sure);
mCancelButton = (Button) findViewById(R.id.resetpwd_btn_cancel);
mSureButton.setOnClickListener(m_resetpwd_Listener); //注册界面两个按钮的监听事件
mCancelButton.setOnClickListener(m_resetpwd_Listener);
//mCancelButton.setOnClickListener(m_resetpwd_Listener);
if (mUserDataManager == null) {
mUserDataManager = new UserDataManager(this);
mUserDataManager.openDataBase(); //建立本地数据库
}
}
View.OnClickListener m_resetpwd_Listener = new View.OnClickListener() { //不同按钮按下的监听事件选择
public void onClick(View v) {
switch (v.getId()) {
case R.id.resetpwd_btn_sure: //确认按钮的监听事件
resetpwd_check();
break;
case R.id.resetpwd_btn_cancel: //取消按钮的监听事件,由注册界面返回登录界面
Intent intent_Resetpwd_to_Login = new Intent(Resetpwd.this,Login.class) ; //切换Resetpwd Activity至Login Activity
startActivity(intent_Resetpwd_to_Login);
finish();
break;
default:
break;
}
}
};
public void resetpwd_check() { //确认按钮的监听事件
if (isUserNameAndPwdValid()) {
String userName = mAccount.getText().toString().trim();
String userPwd_old = mPwd_old.getText().toString().trim();
String userPwd_new = mPwd_new.getText().toString().trim();
String userPwdCheck = mPwdCheck.getText().toString().trim();
int result=mUserDataManager.findUserByNameAndPwd(userName, userPwd_old);
if(result==1){ //返回1说明用户名和密码均正确,继续后续操作
if(userPwd_new.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_new);
mUserDataManager.openDataBase();
boolean flag = mUserDataManager.updateUserData(mUser);
if (flag == false) {
Toast.makeText(this, getString(R.string.resetpwd_fail),Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(this, getString(R.string.resetpwd_success),Toast.LENGTH_SHORT).show();
mUser.pwdresetFlag=1;
Intent intent_Register_to_Login = new Intent(Resetpwd.this,Login.class) ; //切换User Activity至Login Activity
startActivity(intent_Register_to_Login);
finish();
}
}
}else if(result==0){ //返回0说明用户名和密码不匹配重新输入
Toast.makeText(this, getString(R.string.pwd_not_fit_user),Toast.LENGTH_SHORT).show();
return;
}
}
}
public boolean isUserNameAndPwdValid() {
String myString = null;
String userName = mAccount.getText().toString().trim();
//检查用户是否存在
int count=mUserDataManager.findUserByName(userName);
//用户不存在时返回,给出提示文字
if(count<=0){
Toast.makeText(this, getString(R.string.name_not_exist, userName),Toast.LENGTH_SHORT).show();
return false;
}
if (mAccount.getText().toString().trim().equals(myString)) {
Toast.makeText(this, getString(R.string.account_empty),Toast.LENGTH_SHORT).show();
return false;
} else if (mPwd_old.getText().toString().trim().equals(myString)) {
Toast.makeText(this, getString(R.string.pwd_empty),Toast.LENGTH_SHORT).show();
return false;
} else if (mPwd_new.getText().toString().trim().equals(myString)) {
Toast.makeText(this, getString(R.string.pwd_new_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,157 @@
package me.wshuo.tbot;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class UserDataManager { //用户数据管理类
//一些宏定义和声明
private static final String TAG = "UserDataManager";
private static final String DB_NAME = "user_data";
private static final String TABLE_NAME = "users";
public static final String ID = "_id";
public static final String USER_NAME = "user_name";
//public static final String USER_PWD = "user_pwd";
public static final String USER_PWD = getEncryptedPass();//write by ljw
// public static final String SILENT = "silent";
// public static final String VIBRATE = "vibrate";
private static final int DB_VERSION = 2;
private Context mContext = null;
//创建用户book表
private static final String DB_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
+ ID + " integer primary key," + USER_NAME + " varchar,"
+ USER_PWD + " varchar" + ");";
private SQLiteDatabase mSQLiteDatabase = null;
private DataBaseManagementHelper mDatabaseHelper = null;
//DataBaseManagementHelper继承自SQLiteOpenHelper
private static class DataBaseManagementHelper extends SQLiteOpenHelper {
DataBaseManagementHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.i(TAG,"db.getVersion()="+db.getVersion());
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME + ";");
db.execSQL(DB_CREATE);
Log.i(TAG, "db.execSQL(DB_CREATE)");
Log.e(TAG, DB_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i(TAG, "DataBaseManagementHelper onUpgrade");
onCreate(db);
}
}
public UserDataManager(Context context) {
mContext = context;
Log.i(TAG, "UserDataManager construction!");
}
//打开数据库
public void openDataBase() throws SQLException {
mDatabaseHelper = new DataBaseManagementHelper(mContext);
mSQLiteDatabase = mDatabaseHelper.getWritableDatabase();
}
//关闭数据库
public void closeDataBase() throws SQLException {
mDatabaseHelper.close();
}
//添加新用户,即注册
public long insertUserData(UserData userData) {
String userName=userData.getUserName();
String userPwd=userData.getUserPwd();
ContentValues values = new ContentValues();
values.put(USER_NAME, userName);
values.put(USER_PWD, userPwd);
return mSQLiteDatabase.insert(TABLE_NAME, ID, values);
}
//更新用户信息,如修改密码
public boolean updateUserData(UserData userData) {
//int id = userData.getUserId();
String userName = userData.getUserName();
String userPwd = userData.getUserPwd();
ContentValues values = new ContentValues();
values.put(USER_NAME, userName);
values.put(USER_PWD, userPwd);
return mSQLiteDatabase.update(TABLE_NAME, values,null, null) > 0;
//return mSQLiteDatabase.update(TABLE_NAME, values, ID + "=" + id, null) > 0;
}
//
public Cursor fetchUserData(int id) throws SQLException {
Cursor mCursor = mSQLiteDatabase.query(false, TABLE_NAME, null, ID
+ "=" + id, null, null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst(); //找到了用户数据所在的条目,游标移动到该条数据开始处
}
return mCursor;
}
//
public Cursor fetchAllUserDatas() {
return mSQLiteDatabase.query(TABLE_NAME, null, null, null, null, null,
null);
}
//根据id删除用户
public boolean deleteUserData(int id) {
return mSQLiteDatabase.delete(TABLE_NAME, ID + "=" + id, null) > 0;
}
//根据用户名注销
public boolean deleteUserDatabyname(String name) {
return mSQLiteDatabase.delete(TABLE_NAME, USER_NAME + "=" + name, null) > 0;
}
//删除所有用户
public boolean deleteAllUserDatas() {
return mSQLiteDatabase.delete(TABLE_NAME, null, null) > 0;
}
//
public String getStringByColumnName(String columnName, int id) {
Cursor mCursor = fetchUserData(id);
int columnIndex = mCursor.getColumnIndex(columnName);
String columnValue = mCursor.getString(columnIndex);
mCursor.close();
return columnValue;
}
//
public boolean updateUserDataById(String columnName, int id,
String columnValue) {
ContentValues values = new ContentValues();
values.put(columnName, columnValue);
return mSQLiteDatabase.update(TABLE_NAME, values, ID + "=" + id, null) > 0;
}
//根据用户名找用户,可以判断注册时用户名是否已经存在
public int findUserByName(String userName){
Log.i(TAG,"findUserByName , userName="+userName);
int result=0;
Cursor mCursor=mSQLiteDatabase.query(TABLE_NAME, null, USER_NAME+"="+userName, null, null, null, null);
if(mCursor!=null){
result=mCursor.getCount();
mCursor.close();
Log.i(TAG,"findUserByName , result="+result);
}
return result;
}
//根据用户名和密码找用户,用于登录
public int findUserByNameAndPwd(String userName,String pwd){
Log.i(TAG,"findUserByNameAndPwd");
int result=0;
Cursor mCursor=mSQLiteDatabase.query(TABLE_NAME, null, USER_NAME+"="+userName+" and "+USER_PWD+"="+pwd,
null, null, null, null);
if(mCursor!=null){
result=mCursor.getCount();
mCursor.close();
Log.i(TAG,"findUserByNameAndPwd , result="+result);
}
return result;
}
}

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

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 &amp; 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,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;
}
}

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

Loading…
Cancel
Save