RosAppActivity.java
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 OSRF.
00003  * Copyright (c) 2013, Yujin Robot.
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
00006  * use this file except in compliance with the License. You may obtain a copy of
00007  * the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00014  * License for the specific language governing permissions and limitations under
00015  * the License.
00016  */
00017 
00018 package com.github.rosjava.android_remocons.common_tools.apps;
00019 
00020 import android.app.AlertDialog;
00021 import android.content.DialogInterface;
00022 import android.content.Intent;
00023 import android.os.AsyncTask;
00024 import android.os.Bundle;
00025 import android.util.Log;
00026 import android.view.Window;
00027 import android.view.WindowManager;
00028 import android.widget.LinearLayout;
00029 
00030 import com.github.robotics_in_concert.rocon_rosjava_core.rocon_interactions.InteractionMode;
00031 import com.github.rosjava.android_remocons.common_tools.dashboards.Dashboard;
00032 import com.github.rosjava.android_remocons.common_tools.master.MasterDescription;
00033 import com.github.rosjava.android_remocons.common_tools.rocon.Constants;
00034 
00035 import org.ros.address.InetAddressFactory;
00036 import org.ros.android.RosActivity;
00037 import org.ros.exception.RosRuntimeException;
00038 import org.ros.namespace.NameResolver;
00039 import org.ros.node.NodeConfiguration;
00040 import org.ros.node.NodeMainExecutor;
00041 import org.yaml.snakeyaml.Yaml;
00042 
00043 import java.io.Serializable;
00044 import java.net.URI;
00045 import java.net.URISyntaxException;
00046 import java.util.LinkedHashMap;
00047 
00055 public abstract class RosAppActivity extends RosActivity {
00056 
00057     /*
00058       By default we assume the ros app activity is launched independently. The following attribute is
00059       used to identify when it has instead been launched by a controlling application (e.g. remocons)
00060       in paired, one-to-one, or concert mode.
00061      */
00062     private InteractionMode appMode = InteractionMode.STANDALONE;
00063         private String masterAppName = null;
00064         private String defaultMasterAppName = null;
00065         private String defaultMasterName = "";
00066     private String androidApplicationName; // descriptive helper only
00067     private String remoconActivity = null;  // The remocon activity to start when finishing this app
00068                                             // e.g. com.github.rosjava.android_remocons.robot_remocon.RobotRemocon
00069     private Serializable remoconExtraData = null; // Extra data for remocon (something inheriting from MasterDescription)
00070 
00071         private int dashboardResourceId = 0;
00072         private int mainWindowId = 0;
00073         private Dashboard dashboard = null;
00074         private NodeConfiguration nodeConfiguration;
00075         private NodeMainExecutor nodeMainExecutor;
00076         protected MasterNameResolver masterNameResolver;
00077     protected MasterDescription masterDescription;
00078 
00079     // By now params and remaps are only available for concert apps; that is, appMode must be CONCERT
00080     protected AppParameters params = new AppParameters();
00081     protected AppRemappings remaps = new AppRemappings();
00082 
00083     protected void setDashboardResource(int resource) {
00084                 dashboardResourceId = resource;
00085         }
00086 
00087         protected void setMainWindowResource(int resource) {
00088                 mainWindowId = resource;
00089         }
00090 
00091         protected void setDefaultMasterName(String name) {
00092                 defaultMasterName = name;
00093         }
00094 
00095         protected void setDefaultAppName(String name) {
00096         defaultMasterAppName = name;
00097         }
00098 
00099         protected void setCustomDashboardPath(String path) {
00100                 dashboard.setCustomDashboardPath(path);
00101         }
00102 
00103         protected RosAppActivity(String notificationTicker, String notificationTitle) {
00104                 super(notificationTicker, notificationTitle);
00105         this.androidApplicationName = notificationTitle;
00106         }
00107 
00108         @Override
00109         public void onCreate(Bundle savedInstanceState) {
00110                 super.onCreate(savedInstanceState);
00111 
00112                 if (mainWindowId == 0) {
00113                         Log.e("RosApp",
00114                                         "You must set the dashboard resource ID in your RosAppActivity");
00115                         return;
00116                 }
00117                 if (dashboardResourceId == 0) {
00118                         Log.e("RosApp",
00119                                         "You must set the dashboard resource ID in your RosAppActivity");
00120                         return;
00121                 }
00122 
00123                 requestWindowFeature(Window.FEATURE_NO_TITLE);
00124                 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
00125                                 WindowManager.LayoutParams.FLAG_FULLSCREEN);
00126                 setContentView(mainWindowId);
00127 
00128                 masterNameResolver = new MasterNameResolver();
00129 
00130                 if (defaultMasterName != null) {
00131                         masterNameResolver.setMasterName(defaultMasterName);
00132                 }
00133 
00134 // FAKE concert remocon invocation
00135 //        MasterId mid = new MasterId("http://192.168.10.129:11311", "http://192.168.10.129:11311", "DesertStorm3", "WEP2", "yujin0610");
00136 //        MasterDescription  md = MasterDescription.createUnknown(mid);
00137 //        getIntent().putExtra(MasterDescription.UNIQUE_KEY, md);
00138 //        getIntent().putExtra(AppManager.PACKAGE + ".concert_app_name", "KKKK");
00139 //        getIntent().putExtra("PairedManagerActivity", "com.github.rosjava.android_remocons.rocon_remocon.Remocon");
00140 //        getIntent().putExtra("ChooserURI", "http://192.168.10.129:11311");
00141 //        getIntent().putExtra("Parameters", "{pickup_point: pickup}");
00142 //        getIntent().putExtra("Remappings", "{ 'cmd_vel':'/robot_teleop/cmd_vel', 'image_color':'/robot_teleop/image_color/compressed_throttle' }");
00143 
00144 // FAKE robot remocon invocation
00145 //        MasterId mid = new MasterId("http://192.168.10.211:11311", "http://192.168.10.167:11311", "DesertStorm3", "WEP2", "yujin0610");
00146 //        MasterDescription  md = MasterDescription.createUnknown(mid);
00147 //        md.setMasterName("grieg");
00148 //        md.setMasterType("turtlebot");
00149 //        getIntent().putExtra(MasterDescription.UNIQUE_KEY, md);
00150 //        getIntent().putExtra(AppManager.PACKAGE + ".paired_app_name", "KKKK");
00151 //        getIntent().putExtra("PairedManagerActivity", "com.github.rosjava.android_remocons.robot_remocon.RobotRemocon");
00153 //        getIntent().putExtra("Parameters", "{pickup_point: pickup}");
00154 //        getIntent().putExtra("Remappings", "{ 'cmd_vel':'/robot_teleop/cmd_vel', 'image_color':'/robot_teleop/image_color/compressed_throttle' }");
00155 
00156 
00157         for (InteractionMode mode : InteractionMode.values()) {
00158             // The remocon specifies its type in the app name extra content string, useful information for the app
00159             masterAppName = getIntent().getStringExtra(Constants.ACTIVITY_SWITCHER_ID + "." + mode + "_app_name");
00160             if (masterAppName != null) {
00161                 appMode = mode;
00162                 break;
00163             }
00164         }
00165 
00166         if (masterAppName == null) {
00167             // App name extra content key not present on intent; no remocon started the app, so we are standalone app
00168             Log.e("RosApp", "We are running as standalone :(");
00169             masterAppName = defaultMasterAppName;
00170             appMode = InteractionMode.STANDALONE;
00171                 }
00172         else
00173         {
00174             // Managed app; take from the intent all the fancy stuff remocon put there for us
00175 
00176             // Extract parameters and remappings from a YAML-formatted strings; translate into hash maps
00177             // We create empty maps if the strings are missing to avoid continuous if ! null checks
00178             Yaml yaml = new Yaml();
00179 
00180             String paramsStr = getIntent().getStringExtra("Parameters");
00181             String remapsStr = getIntent().getStringExtra("Remappings");
00182 
00183             Log.d("RosApp", "Parameters: " + paramsStr);
00184             Log.d("RosApp", "Remappings: " + remapsStr);
00185 
00186             try {
00187                 if ((paramsStr != null) && (! paramsStr.isEmpty())) {
00188                     LinkedHashMap<String, Object> paramsList = (LinkedHashMap<String, Object>)yaml.load(paramsStr);
00189                     if (paramsList != null) {
00190                         params.putAll(paramsList);
00191                         Log.d("RosApp", "Parameters: " + paramsStr);
00192                     }
00193                 }
00194             } catch (ClassCastException e) {
00195                 Log.e("RosApp", "Cannot cast parameters yaml string to a hash map (" + paramsStr + ")");
00196                 throw new RosRuntimeException("Cannot cast parameters yaml string to a hash map (" + paramsStr + ")");
00197             }
00198 
00199             try {
00200                 if ((remapsStr != null) && (! remapsStr.isEmpty())) {
00201                     LinkedHashMap<String, String> remapsList = (LinkedHashMap<String, String>)yaml.load(remapsStr);
00202                     if (remapsList != null) {
00203                         remaps.putAll(remapsList);
00204                         Log.d("RosApp", "Remappings: " + remapsStr);
00205                     }
00206                 }
00207             } catch (ClassCastException e) {
00208                 Log.e("RosApp", "Cannot cast parameters yaml string to a hash map (" + remapsStr + ")");
00209                 throw new RosRuntimeException("Cannot cast parameters yaml string to a hash map (" + remapsStr + ")");
00210             }
00211 
00212             remoconActivity = getIntent().getStringExtra("RemoconActivity");
00213 
00214             // Master description is mandatory on managed apps, as it contains master URI
00215             if (getIntent().hasExtra(MasterDescription.UNIQUE_KEY)) {
00216                 // Keep a non-casted copy of the master description, so we don't lose the inheriting object
00217                 // when switching back to the remocon. Not fully sure why this works and not if casting
00218                 remoconExtraData = getIntent().getSerializableExtra(MasterDescription.UNIQUE_KEY);
00219 
00220                 try {
00221                     masterDescription =
00222                             (MasterDescription) getIntent().getSerializableExtra(MasterDescription.UNIQUE_KEY);
00223                 } catch (ClassCastException e) {
00224                     Log.e("RosApp", "Master description expected on intent on " + appMode + " mode");
00225                     throw new RosRuntimeException("Master description expected on intent on " + appMode + " mode");
00226                 }
00227             }
00228             else {
00229                 // TODO how should I handle these things? try to go back to remocon? Show a message?
00230                 Log.e("RosApp", "Master description missing on intent on " + appMode + " mode");
00231                 throw new RosRuntimeException("Master description missing on intent on " + appMode + " mode");
00232             }
00233         }
00234 
00235         if (dashboard == null) {
00236                         dashboard = new Dashboard(this);
00237                         dashboard.setView((LinearLayout) findViewById(dashboardResourceId),
00238                                         new LinearLayout.LayoutParams(
00239                                                         LinearLayout.LayoutParams.WRAP_CONTENT,
00240                                                         LinearLayout.LayoutParams.WRAP_CONTENT));
00241                 }
00242         }
00243 
00244         @Override
00245         protected void init(NodeMainExecutor nodeMainExecutor) {
00246                 this.nodeMainExecutor = nodeMainExecutor;
00247                 nodeConfiguration = NodeConfiguration.newPublic(InetAddressFactory
00248                 .newNonLoopback().getHostAddress(), getMasterUri());
00249 
00250         if (appMode == InteractionMode.STANDALONE) {
00251             dashboard.setRobotName(masterNameResolver.getMasterName());
00252         }
00253         else {
00254             masterNameResolver.setMaster(masterDescription);
00255             dashboard.setRobotName(masterDescription.getMasterName());  // TODO dashboard not working for concerted apps (Issue #32)
00256 
00257             if (appMode == InteractionMode.PAIRED) {
00258                 dashboard.setRobotName(masterDescription.getMasterType());
00259             }
00260         }
00261 
00262         // Run master namespace resolver
00263         nodeMainExecutor.execute(masterNameResolver, nodeConfiguration.setNodeName("masterNameResolver"));
00264         masterNameResolver.waitForResolver();
00265 
00266         nodeMainExecutor.execute(dashboard, nodeConfiguration.setNodeName("dashboard"));
00267     }
00268 
00269         protected NameResolver getMasterNameSpace() {
00270                 return masterNameResolver.getMasterNameSpace();
00271         }
00272 
00273         protected void onAppTerminate() {
00274                 RosAppActivity.this.runOnUiThread(new Runnable() {
00275                         @Override
00276                         public void run() {
00277                                 new AlertDialog.Builder(RosAppActivity.this)
00278                                                 .setTitle("App Termination")
00279                                                 .setMessage(
00280                                                                 "The application has terminated on the server, so the client is exiting.")
00281                                                 .setCancelable(false)
00282                                                 .setNeutralButton("Exit",
00283                                                                 new DialogInterface.OnClickListener() {
00284                                                                         public void onClick(DialogInterface dialog,
00285                                                                                         int which) {
00286                                         RosAppActivity.this.finish();
00287                                                                         }
00288                                                                 }).create().show();
00289                         }
00290                 });
00291         }
00292 
00293         @Override
00294         public void startMasterChooser() {
00295                 if (appMode == InteractionMode.STANDALONE) {
00296                         super.startMasterChooser();
00297                 } else {
00298                         try {
00299                 nodeMainExecutorService.setMasterUri(new URI(masterDescription.getMasterUri()));
00300                 new AsyncTask<Void, Void, Void>() {
00301                     @Override
00302                     protected Void doInBackground(Void... params) {
00303                         RosAppActivity.this.init(nodeMainExecutorService);
00304                         return null;
00305                     }
00306                 }.execute();
00307                         } catch (URISyntaxException e) {
00308                 // Remocon cannot be such a bastard to send as a wrong URI...
00309                                 throw new RosRuntimeException(e);
00310                         }
00311                 }
00312         }
00313 
00314         protected void releaseMasterNameResolver() {
00315                 nodeMainExecutor.shutdownNodeMain(masterNameResolver);
00316         }
00317 
00318         protected void releaseDashboardNode() {
00319                 nodeMainExecutor.shutdownNodeMain(dashboard);
00320         }
00321 
00332     private boolean managePairedRobotApplication() {
00333         return ((appMode == InteractionMode.STANDALONE) && (masterAppName != null));
00334     }
00335 
00336         @Override
00337         protected void onDestroy() {
00338                 super.onDestroy();
00339         }
00340 
00341         @Override
00342         public void onBackPressed() {
00343                 if (appMode != InteractionMode.STANDALONE) {  // i.e. it's a managed app
00344             Log.i("RosApp", "app terminating and returning control to the remocon.");
00345             // Restart the remocon, supply it with the necessary information and stop this activity
00346                         Intent intent = new Intent();
00347                         intent.putExtra(Constants.ACTIVITY_SWITCHER_ID + "." + appMode + "_app_name", "AppChooser");
00348             intent.putExtra(MasterDescription.UNIQUE_KEY, remoconExtraData);
00349             intent.setAction(remoconActivity);
00350                         intent.addCategory("android.intent.category.DEFAULT");
00351                         startActivity(intent);
00352                         finish();
00353                 } else {
00354             Log.i("RosApp", "backpress processing for RosAppActivity");
00355         }
00356                 super.onBackPressed();
00357         }
00358 }


android_remocons
Author(s): Daniel Stonier, Kazuto Murase
autogenerated on Sat Jun 8 2019 19:32:24