Showing posts with label android-wear. Show all posts
Showing posts with label android-wear. Show all posts

Saturday, February 4, 2017

Android HTTP Requests Working In Simulator But Not On Wear Device

Leave a Comment

I am making a simple Android Wear app to control my thermostats, and I'm sending POST requests with Volley to control them. Everything works great in the Android Wear simulator (the request works), but, while the app does load on my Moto 360, the volley request gets called but invariably times out.

Why could my volley request be failing on my watch but working on the simulator? Other apps' requests succeed on my watch (for example, the built-in weather app can load up weather data in about 3 seconds). And, the weirdest part: I had the app working (successfully making volley requests) on my watch, and, about a day after I installed it to my watch from Android Studio, it suddenly stopped loading data for no apparent reason.

What I've tried so far:

  • I have requested the Internet permission in my manifest.xml.
  • I have increased the timeout to 30 seconds (see my code below), which didn't change anything.
  • I have tried tethering my computer and the simulator to my phone's connection via Bluetooth (to replicate the Bluetooth connection my physical watch has to my phone), and the simulator made the request successfully still (albeit with a two-second delay), ruling out the possibility of Bluetooth being too slow.
  • I made sure the API level is low enough for my Marshmallow-running watch (my watch and the app are both API level 23).
  • I tried doing a quick test request to Google before the request to the company's servers with my thermostat data, and while the Google request returns the site's HTML code in the simulator, it times out on my watch (thirty seconds after the request is initiated).
  • I tried putting some dummy data into the recycler view data should be loaded into, and the dummy data indeed showed up, ruling out that the recycler view is broken.
  • I deleted the app from my watch and reinstalled it, and deleted the companion from my phone, reinstalled it, and deleted it again, all to no avail.
  • A lengthy chat with Google Support did not produce anything meaningful.

Here's my code (from my main view's adapter):

public void refreshThermostatsRecyclerView(RequestQueue queue) {     String url = "https://mobile.skyport.io:9090/login"; // login call to the thermostats server Skyport      Log.w("myApp", "Starting /login call to Skyport"); // this gets called on simulator and watch      // Request a string response from the provided URL.      StringRequest stringRequest = new StringRequest(Request.Method.POST, url,  Response.Listener<String>() {        @Override        public void onResponse(String response) {            // Display the response string.            Log.w("myApp", "Response is: " + response); // this gets called on the simulator but not the watch            try {                // there's some code to parse the data.            } catch (JSONException e) {                 Log.w("myApp", "catching an error parsing the json."); // never gets called.                 e.printStackTrace();             }             }       }, new Response.ErrorListener() {             @Override             public void onErrorResponse(VolleyError error) {                 Log.w("myApp", "Skyport request didn't work! " + error);  // this always gets called on the watch, with the error being a timeout error (com.Android.Volley.timeouterror) but never gets called in the simulator             }         }) {             @Override             public Map<String, String> getHeaders() throws AuthFailureError {                 Map<String, String> m = new HashMap<>();                 m.put("Referer", "app:/VenstarCloud.swf");                 // here I put some more headers                 return m;             }              @Override             protected Map<String, String> getParams() throws AuthFailureError {                 Map<String, String> m = new HashMap<>();                 m.put("version", "3.0.5");                 m.put("email", userEmail);                 m.put("password", userToken);                 return m;             }         };         // Add the request to the RequestQueue.         int socketTimeout1 = 30000; // times out 30 seconds after the request starts on the watch         RetryPolicy policy1 = new DefaultRetryPolicy(socketTimeout1, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);         stringRequest.setRetryPolicy(policy1);         queue.add(stringRequest);     } 

Which is called from the onCreate() method in my Main Activity with this code:

RequestQueue queue = Volley.newRequestQueue(this); refreshThermostatsRecyclerView(queue); 

If you'd like to view the logs created by running this in the simulator and on the watch, they're on Google Drive here.


Edit 1: A reboot of my watch fixes the issue temporarily and allows the watch to make HTTP Requests again, but it breaks again once the watch disconnects from Bluetooth, connects to WiFi, disconnects from WiFi, and reconnects to Bluetooth (so it breaks every time I go across my apartment without my phone and then return).

Edit 2: I switched the volley requests all over to HTTPURLConnection Requests in an Async thread, and the same issues occur as with volley.


tl;dr: My app's Volley requests are working in the simulator but not on my Android Wear watch anymore (though Play Store-downloaded apps' similar requests work), how can I get a volley request to work again on my app on the watch?

4 Answers

Answers 1

I am also using volley on an Android wear app I built and I am running it on a Moto 360, I have run into the same problem a couple o times. Try restarting the device. Go to Settings > Restart. It sounds silly but it has worked for me.

Answers 2

You could try an alternative to volley if you can rule out the connection as the problem:

compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:support-v4:23.1.0' compile 'com.android.support:design:23.1.0' compile 'com.google.code.gson:gson:2.2.4' compile 'com.google.api-client:google-api-client:1.20.0' 

The versions are important.

Then to your request:

Map<String, String> contentParams = new HashMap<>(); InputStream is = null; NetHttpTransport transport = null; HttpRequest request = null; HttpResponse resp = null; HttpHeaders headers = new HttpHeaders(); JSONObject json = null;      try {         transport = new NetHttpTransport();         HttpRequestFactory factory = transport.createRequestFactory();         request = factory.buildPostRequest(new GenericUrl(url), null);         contentParams = getContentParameters();         headers.putAll(getHeaderParameters());         request.setHeaders(headers);         request.getUrl().putAll(contentParams);         resp = request.execute();         is = resp.getContent();     } catch (Exception e) {         e.printStackTrace();     } finally {         try {             if (is != null) {                 string = getJSONFromInputStream(is);                 json = new JSONObject(string);             }          } catch (Exception e) {             e.printStackTrace();         }     }     transport.shutdown();  protected Map<String, String> getContentParameters() {      Map<String, String> m = new HashMap<>();      m.put("version", "3.0.5");      m.put("email", userEmail);      m.put("password", userToken);      return m; }  protected Map<String, String> getHeaderParameters() {      Map<String, String> m = new HashMap<>();      m.put("Referer", "app:/VenstarCloud.swf");      return m; }  protected String getJSONFromInputStream(InputStream is) {     if (is == null)         throw new NullPointerException();     //instantiates a reader with max size     BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8 * 1024);      StringBuilder sb = new StringBuilder();      try {         //reads the response line by line (and separates by a line-break)         String line;         while ((line = reader.readLine()) != null) {             sb.append(line + "\n");         }     } catch (IOException e) {         e.printStackTrace();     } finally {         try {             //closes the inputStream             is.close();         } catch (IOException e) {             e.printStackTrace();         }     }     return sb.toString(); } 

Then just execute your code from a thread/asynctask/have it delay your frontend slightly

Edit: Just in case there is a problem with appending a map:

for (Entry<String, String> entry : getHeaderParameters()) {     headers.put(entry.getKey(), entry.getValue()); }  for (Entry<String, String> entry : getContentParameters()) {     request.getUrl().put(entry.getKey(), entry.getValue()); } 

Also as another note, make sure to change the return type from void on both those methods to Map

Answers 3

Is this not just the case of when the watch is connected to the phone via bluetooth the internet will not work, as wifi is turned off. If the watch is using wifi to connect to the phone then it will work.

I'm working on wear 2.0 app and just turn blueooth off on my phone for my watch to get internet connection.

Answers 4

Perhaps, your thermostat server https://mobile.skyport.io:9090 has restrictions on API key.

For example, Google APIs are restricted by IP addresses that can call them. Maybe you added IP address of your computer on which simulator is running but forgot to add IP of your watch.

Kind regards, Bala

Read More

Monday, January 30, 2017

Can't install my app on wear

Leave a Comment

I tried to add a wear module to my existing app, tried a lot of solutions, but can't figure out why my app is not being installed on my watch.

What I tried :

First, Manual packaging with my app : https://developer.android.com/training/wearables/apps/packaging.html

But I quickly decided not to go through this.

Then I decided to go to gradle include, so to the build.gradle of app, I added the following to the end of dependencies :

debugWearApp project(path:':wear', configuration: 'flavor1Debug') releaseWearApp project(path:':wear', configuration: 'flavor1Release') 

To the build.gradle of wear, I added the following to the beginning of dependencies :

wearApp project(':wear') 

Then, in android{} section of wear build.gradle, just after buildToolsVersion, I added the following :

publishNonDefault true 

What I have seen :

  • No problem to install the wear app to the wear using bluetooth debug of the wear

Then, when I install a generate a release version of my app, I can see in raw, that it has been added a file android_wear_micro_apk.apk to res/raw which is my watch app. I also saw a file android_wear_micro_apk.xml in res/xml with, from what I guess between hexa codes, the description of wear app.

Then I compare signatures :

keytool -list -printcert -jarfile mobile_app.apk keytool -list -printcert -jarfile wear_app.apk 

Using the wear app generated in res/raw. They exactly have the same signature. Then I compared :

aapt dump badging mobile_app.apk aapt dump badging wear_app.apk 

They have exact same package names and version codes and names.

So, from that :

  • Apk of wear is correctly added
  • Apk of wear is working if installed on the wear using adb and bluetooth debug
  • Both apk have same version code, version name, and package name
  • Wear is not requiring any permission
  • Phone is requesting following permissions
    • android.permission.ACCESS_NETWORK_STATE
    • android.permission.INTERNET
    • com.android.vending.BILLING
    • com.google.android.c2dm.permission.RECEIVE
    • android.permission.VIBRATE
    • android.permission.WRITE_EXTERNAL_STORAGE
    • android.permission.BLUETOOTH
    • android.permission.BLUETOOTH_ADMIN
    • com.samsung.accessory.permission.ACCESSORY_FRAMEWORK
    • com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY
    • com.samsung.WATCH_APP_TYPE.Companion
    • com.samsung.wmanager.ENABLE_NOTIFICATION

I'm really wondering what I could have forgotten.

Thanks for any help

4 Answers

Answers 1

According to one of Google’s Android Developer Advocates, Android Wear 2.0 will require completely standalone watch and phone apps, and abandons the system used since the first version of Android Wear that automatically installs Android Wear apps based on the apps you have on your phone. He puts it plainly in reply to another developer in the Android Wear Developers Google+ community (emphasis ours):

A Wear 2.0 user must visit the Play Store on their watch to install apps. There is no auto-install like on Wear 1.X. Wear 2.0 apps get full network access and can be installed completely separately from the handheld app so the focus is much more on standalone Wear apps than the handheld centric 1.X Wear apps.

But what about apps built solely for your watch? Well, there's a whole store worth of apps that go beyond simple notifications and live on the watch itself. Rather oddly, these still have to be installed through your smartphone. For now, at least - the new Android Wear 2.0 update will include functionality for standalone apps.

Answers 2

You may want to check permissions declared in your app. As mentioned in Requesting Permissions on Android Wear regarding mismatching permission models between wearable and handset app,

If your handset app begins using the Android 6.0 (API level 23) model but your wearable app does not, the system downloads the Wear app, but does not install it. The first time the user launches the app, the system prompts them to grant all pending permissions. Once they do so, it installs the app. If your app, for example a watch face, does not have a launcher, the system displays a stream notification asking the user to grant the permissions the app needs.

The suggested solutions in these SO posts might also help:

Answers 3

Forum

On the watch:

  • Settings, Un-pair with phone. (Old release of AW may say Factory reset.)

  • Do not set up the watch yet.

On the phone:

  • In Android Wear use the Disconnect... option.

  • In Android Wear, use Settings, Device settings and touch the watch name, Touch FORGET WATCH.

  • Settings, Bluetooth. If you still see the watch, touch Forget... the watch so it no longer appears in the list of paired devices.

  • Settings, Device, Apps, select Google Play Services. Clear cache and Clear data. Uninstall updates.

  • Settings, Device, Apps, select Google App. Clear cache and Clear data. Also Uninstall updates.

  • Settings, Device, Apps, select Android Wear. Clear cache and Clear data. Also Uninstall updates.

  • Play Store, Apps, My apps, touch the Update all button.

Answers 4

You aren't required to add wearApp project(':wear') in your build.gradle of the wear module

In the case of different build variants just adjust accordingly, in your build.gradle of the mobile module:

debugWearApp project(path:':wear', configuration: 'flavor1Debug') releaseWearApp project(path:':wear', configuration: 'flavor1Release')

Also, further tips:

  • Check your permissions. The Smartphone part needs to have all the permissions the Wear component has.
  • use the same package id for both apps (wear and mobile)

Hope this solves it.

Read More

Saturday, August 13, 2016

Launch an Android Wear app with a “Start …” voice command

Leave a Comment

I try to launch my wear app by Start voice command. I followed the documentation Adding Voice Capabilities. But when I try to launch the app by saying (OK Google) "Start my app" I receive the Google Now web search results back for the topics related to the "Start my app" instead of launching the app itself.

<activity         android:label="my app"         android:name=".MainActivityWear"         android:theme="@android:style/Theme.DeviceDefault.Light">         <intent-filter>             <action android:name="android.intent.action.MAIN" />             <category android:name="android.intent.category.LAUNCHER" />         </intent-filter>     </activity  

My feeling is that the documentation is outdated and the Start command used to be in older versions of Android wear. (My version is 1.3.0 with OS 5.1.1.) I think so, because in my version of the wear to activate the Speak Now card I have to say "OK Google" or swipe the screen from left. The Settings/Launcher with the app list is 2 pages left from the Speak Now card, rather than at the bottom of the card as mentioned in this documentation: Wearable Applications Launch. Especially the part saying the following looks unfamiliar to my watch behaviour:

To manually launch the app, touch the watch face and scroll to the last action, which is “Start...”. Then select your app from the list of installed apps, in this case First_Wearable.

Is anyone able to launch apps in watch by START voice command? Am I doing something wrong?

0 Answers

Read More

Monday, June 13, 2016

How do I display notification pages with expandable custom layouts in Android Wear?

Leave a Comment

What's the proper way of having a tall custom activity layout in a notification page that starts off collapsed?

Details We have a top-level notification to which I am adding pages, each with its own custom activity (via WearableExtender#setDisplayIntent(...)). This works great!

We also have an Asset image that corresponds to each of these custom activities and we want to show that behind them such that we take advantage of the beautiful transitions provided by the wear system UI. To do this, we use WearableExtender#setBackground(toBitmap(asset)) and this, too, works great!

The problem: in order to see the background, we need the custom activity to not take up the entire screen. Therefore, we use a custom preset size smaller than setCustomSizePreset(WearableExtender.SIZE_FULL_SCREEN) but now, our custom Activity is scrolled off the bottom of the screen (it has a minimum height) and slightly cut off. The white "card" part can't be scrolled up to expose the rest of the card.

The question: is there a proper way to tell the system UI to make the card that contains the custom activity expandable like the other cards? In other words: is there a way to start the card collapsed to the very bottom such that it's only "peeking" from the bottom but allow the user to expand it to full screen?

0 Answers

Read More

Saturday, June 11, 2016

How to access Android Heart Rate Sensor RAW DATA?

Leave a Comment

The android sdk on heart rate sensor only returns the calculated bpm which I have no interest.

I need to access to android heart rate sensor RAW data, e.g. in terms of intensity of reflected value would be great. (because basically heart rate sensor uses led and measures the reflectance over time)

If possible, access the the raw image collected by the whatever image sensor would be greater. thanks.

1 Answers

Answers 1

You can use Google Fit's Sensor API to get the raw heartbeat data. See Google Fit Guide for details.

private void trackHeartRate() {     SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()             .setDataTypes(DataType.TYPE_HEART_RATE_BPM)             // Can specify whether data type is raw or derived.             .setDataSourceTypes(DataSource.TYPE_RAW)             .build())             .setResultCallback(new ResultCallback<DataSourcesResult>() {                 @Override                 public void onResult(DataSourcesResult dataSourcesResult) {                     Log.i(TAG, "Result: " + dataSourcesResult.getStatus().toString());                     for (DataSource dataSource : dataSourcesResult.getDataSources()) {                         Log.i(TAG, "Data source found: " + dataSource.toString());                         Log.i(TAG, "Data Source type: " + dataSource.getDataType().getName());                          if (dataSource.getDataType().equals(DataType.TYPE_HEART_RATE_BPM)                                 && mListener == null) {                             Log.i(TAG, "Data source for heart rate found!  Registering.");                             registerFitnessDataListener(dataSource,                                     DataType.TYPE_HEART_RATE_BPM);                         }                     }                 }             });     mListener = new OnDataPointListener() {         @Override         public void onDataPoint(DataPoint dataPoint) {             for (Field field : dataPoint.getDataType().getFields()) {                 Value val = dataPoint.getValue(field);                 Log.i(TAG, "Detected DataPoint field: " + field.getName());                 Log.i(TAG, "Detected DataPoint value: " + val);             }         }     }; }  private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {     Fitness.SensorsApi.add(             mClient,             new SensorRequest.Builder()                     .setDataSource(dataSource) // Optional but recommended for custom data sets.                     .setDataType(dataType) // Can't be omitted.                     .setSamplingRate(10, TimeUnit.SECONDS)                     .build(),             mListener)             .setResultCallback(new ResultCallback<Status>() {                 @Override                 public void onResult(Status status) {                     if (status.isSuccess()) {                         Log.i(TAG, "Listener registered!");                     } else {                         Log.i(TAG, "Listener not registered.");                     }                 }             }); } 

Hope this helps.

Read More

Thursday, April 28, 2016

Accelerometer sensor cause losses running into a service

Leave a Comment

I am developing an application for Android Wear. It consists listen coordinates from accelerometer sensor, and find a pattern.

To do this, when the user clicks a button, the service starts and begins to store coordinates in a List. Usually accelerometer sensor log 4 to 5 coordinates per second.

The problem is sometimes the onSensorChanged() method does not receive data for several seconds, causing losses of data and difficult to find a pattern.

Here is a gist of my service: https://gist.github.com/cpalosrejano/8f0e59e47124275136fc3d5d941faa07

Things I've tried:

  • I am using android:stopWithTask=false to prevent the service stops when the activity dies.
  • I have also used a WakeLock to prevent the device go to sleep while the services is recording coordinates.

What am I doing wrong? Is there another way to receive callbacks from accelerometer sensor without causing lose data?

Thanks for any help.

1 Answers

Answers 1

What you can do is to write the data to a file on the device and read it from the file.

// When sensor value has changed @Override public void onSensorChanged(SensorEvent event){     if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){      //Perform a background task to store the data     new SensorEventLoggerTask().execute(event);      // And continue to do whatever you want and grab the data     // The data will be saved in to a file on the device and read it from themre     } } 
Read More