Help with Apple WeatherKit - Wear OS Q&A, Help & Troubleshooting

I hope someone can help me.
I'm having trouble getting an Apple WeatherKit REST request working. I'm trying to follow all of the requirements listed on https://developer.apple.com/documen...equest_authentication_for_weatherkit_rest_api, but I still get a 403 response code -- and I can't locate any Java samples to follow. I've been sweating this for weeks and haven't gotten anywhere.
Here's what I have:
URL url = new URL ("https://weatherkit.apple.com/api/v1/weather/en/40.75/-73.00?dataSets=currentWeather&timezone=Europe/London");
PrivateKey pkey = getPrivateKey ("AuthKey_CXXXXXXXXX.p8");
String jwts = getJwtsString (pkey);
HttpURLConnection conn = getConnection (url, jwts);
conn.connect();
int responsecode = conn.getResponseCode();
Just in case I've got something wrong, here are the three functions I'm calling. First, the one that creates the PrivateKey from the p8 file (I'm pretty sure this one is OK):
private PrivateKey getPrivateKey (String filename) {
PrivateKey privateKey = null;
try {
AssetManager assetManager = this.getAssets();
InputStream stream = assetManager.open(filename);
byte[] bytes = new byte[stream.available()];
int bytesRead = stream.read(bytes);
byte[] pkcs8EncodedKey = new byte[0];
pkcs8EncodedKey = Base64.getDecoder().decode(bytes);
KeyFactory factory = KeyFactory.getInstance("EC");
privateKey = factory.generatePrivate(new PKCS8EncodedKeySpec(pkcs8EncodedKey));
} catch (Exception e) { }
return privateKey;
}
Next, creating the JWTS string:
private String getJwtsString(PrivateKey privateKey) {
long t1 = System.currentTimeMillis();
return Jwts.builder()
.setHeaderParam ("alg", "ES256")
.setHeaderParam ("kid", "CXXXXXXXXX")
.setHeaderParam ("id", "QXXXXXXXXX.com.mydomain.myproject")
.claim ("iss", "Q5DC438ZG4")
.claim ("iat", t1)
.claim ("exp", t1 + 60000)
.claim ("sub", "com.mydomain.myproject") // obviously, replaced with my project info
.signWith (privateKey, SignatureAlgorithm.ES256)
.compact ();
}
And finally, creating the connection:
private HttpURLConnection getConnection(URL url, String jws) {
HttpURLConnection conn = (HttpURLConnection) Objects.requireNonNull(url).openConnection();
conn.setRequestMethod ("GET")
conn.setRequestProperty ("Authorization", "Bearer "+ jws);
conn.setRequestProperty ("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty ("Content-Language", "en-US");
conn.setUseCaches (false);
conn.setDoInput (true);
conn.setDoOutput (true);
conn.setConnectTimeout (30000); //set timeout to 30 seconds
return conn;
}
Obviously, this is the first time I've tried creating an authenticated http call, so I wouldn't be at all surprised if this is wrong.
Can anybody supply some sample code that I could follow?
Thanks in advance...

Related

Newbie url text download problem

Hi guys,
I am totally new at this. I mean completely raw .
I successfully put together a searchlight project by following a tutorial on youtube.
Now I want to do some work on a project that will fetch information from my employers website into the app. This information will be inform of text and images.
Here's the code I'm using in my attempt to read the remote text:
Code:
try {
URL url = new URL( "fullpath-url-to-file-dot-php" );
URLConnection conn = url.openConnection();
// Get the response
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "";
while ((line = rd.readLine()) != null) {
Message lmsg;
lmsg = new Message();
lmsg.obj = line;
lmsg.what = 0;
Context context = getApplicationContext();
CharSequence text = "Hello toast! "+lmsg;
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);toast.show();
}
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
Context context = getApplicationContext();
CharSequence text = "error!";
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, text, duration);toast.show();
}
I admit I barely know what I am doing. My attempt to download a simple line of text from a website url keeps failing.
Anyway, Can you guys give me an idea what I am doing wrong and what I should be doing instead?
Thanks
Update
I found out what the nature of the exception is.
It's a
Code:
java.net.socketexception: permission denied
exception.
Can anyone please shed some light on this for me ?
Did you add the internet permission to your manifest file?
Should be something like
<uses-permission>android.permission.INTERNET</uses-permission>

AsyncTask and TCP Comms

Hey guys,
I have spent at least 24 hours trying to figure this out by myself and still havn't been able to...so I broke down and came here.
This code works...it does not (9/10s sure here...) run asyn and receives the same number of bytes as the picture should have
Code:
class ClientThread implements Runnable {
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public void run() {
while(true){
try {
int bytesRead;
int totalBytes = 0;
byte[] pic = new byte[1024];
byte[] inputPic_bytes = new byte[5000000];
DataInputStream dis;
DataOutputStream dos;
try {
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
dos.writeBytes("snapshot" + '\n');
int pic_size = Integer.parseInt(dis.readLine());
Log.e("TCP", "Pic size: " + pic_size);
while (totalBytes < pic_size){
bytesRead = dis.read(pic);
System.arraycopy(pic, 0, inputPic_bytes, totalBytes, bytesRead);
totalBytes += bytesRead;
pic[0] = (byte) (pic[0]&0xFF);
Log.e("TCP", "Read bytes: " + bytesRead + " Total bytes: " + totalBytes + " Bytes: " + pic[0] + " " + pic[1] + " " + pic[2]);
}
this code runs async and receives ~5k more bytes than the picture size which does not even seem possible to me (read_mode = 2 gets the pic)
Code:
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
//create a new socket instance
SocketAddress sockaddr = new InetSocketAddress("192.168.0.5", 50007);
nsocket = new Socket();
nsocket.connect(sockaddr, 5000);//connect and set a 10 second connection timeout
if (nsocket.isConnected()) {//when connected
nis = new DataInputStream(nsocket.getInputStream());
nos = nsocket.getOutputStream();//and output stream from the socket
Log.e("Buffer Size", "Size: " + nsocket.getReceiveBufferSize());
networktask.SendDataToNetwork("sensors");
while(true){//while connected
if(read_mode == 0){
String getPicLength = nis.readLine();//read the lines coming from the socket
byte[] array_getPicLength = getPicLength.getBytes();
publishProgress(array_getPicLength);//update the publishProgress
Log.e("While_Count_0", "Count");
read_mode = 0;
}
else if(read_mode == 1){
String getPicLength = nis.readLine();//read the lines coming from the socket
byte[] array_getPicLength = getPicLength.getBytes();
publishProgress(array_getPicLength);//update the publishProgress
Log.e("While_Count_1", "Count");
read_mode = 2;
}
else if(read_mode == 2){
nis.read(pic);
publishProgress(pic);//update the publishProgress
}
}
}
Not sure if I have included enough info here...but this is seriously troubling me as I used to have it working.
What is so different about async that it is causing to receive so many extra bytes of "picture" data?
Thanks all...this is really bothering me.

More secure encryption class using salt

Continuing with the theme from my last thread where I posted a simple class for encrypting strings using the SHA-512 hashing algorithm, here is an improved version that generates a random 20 byte salt to add in with the string to be hashed. This is then hashed providing greater security.
Due to the random generation of the salt each time a string is hashed, this makes it pretty much impossible to get the same hash for a string, therefore once the salt has been generated the first time round it is stored in sharedPreferences for future uses so that you can use it for checking matches etc
Method of converting the bytes to hex string adapted from maybeWeCouldStealAVan's method @ stackoverflow.
Code:
public class Crypto {
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
protected static String SHA512(String string, Context context) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-512");
String salt = getSalt(context);
md.update(salt.getBytes());
byte[] bytes = md.digest(string.getBytes());
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private static String getSalt(Context context) throws NoSuchAlgorithmException {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String salt = preferences.getString("salt", null);
if (salt == null) {
byte[] saltBytes = new byte[20];
SecureRandom.getInstance("SHA1PRNG").nextBytes(saltBytes);
salt = new String(saltBytes);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("salt", salt).commit();
}
return salt;
}
}
Usage:
Code:
String example = "example";
try {
example = Crypto.SHA512(example, context);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
Thanks for sharing, it's quite usefull ! I will include it to my project
Jonny said:
Continuing with the theme from my last thread where I posted a simple class for encrypting strings using the SHA-512 hashing algorithm, here is an improved version that generates a random 20 byte salt to add in with the string to be hashed. This is then hashed providing greater security.
Due to the random generation of the salt each time a string is hashed, this makes it pretty much impossible to get the same hash for a string, therefore once the salt has been generated the first time round it is stored in sharedPreferences for future uses so that you can use it for checking matches etc
Method of converting the bytes to hex string adapted from maybeWeCouldStealAVan's method @ stackoverflow.
Code:
public class Crypto {
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
protected static String SHA512(String string, Context context) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-512");
String salt = getSalt(context);
md.update(salt.getBytes());
byte[] bytes = md.digest(string.getBytes());
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private static String getSalt(Context context) throws NoSuchAlgorithmException {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String salt = preferences.getString("salt", null);
if (salt == null) {
byte[] saltBytes = new byte[20];
SecureRandom.getInstance("SHA1PRNG").nextBytes(saltBytes);
salt = new String(saltBytes);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("salt", salt).commit();
}
return salt;
}
}
Usage:
Code:
String example = "example";
try {
example = Crypto.SHA512(example, context);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
Click to expand...
Click to collapse
Thanks
Gesendet von meinem LG-D855 mit Tapatalk

Java is too complex for begginer lovers

I love Android. I want to learn to develop apps. I keep reading tutorials. I got dissapointed and read about HTML frameworks (phonegap, etc). I came back to Android Native Java. I want to learn from the roots. However, some things discourages me....
All this part of the code is just for making a request to the Openweather API and get the json data (plus a little debugging stuff); which in Python or similar languages you only have to care about
- importing the library that handles http requests
- make the request in one function and save it into a json object
Code:
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String forecastJsonStr = null;
String format = "json";
String units = "metric";
int numDays = 7;
try {
final String FORECAST_BASE_URL =
"<the-domain>/data/2.5/forecast/daily?";
final String QUERY_PARAM = "q";
final String FORMAT_PARAM = "mode";
final String UNITS_PARAM = "units";
final String DAYS_PARAM = "cnt";
Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon()
.appendQueryParameter(QUERY_PARAM, params[0])
.appendQueryParameter(FORMAT_PARAM, format)
.appendQueryParameter(UNITS_PARAM, units)
.appendQueryParameter(DAYS_PARAM, Integer.toString(numDays))
.build();
URL url = new URL(builtUri.toString());
Log.v(LOG_TAG, "Built URI " + builtUri.toString());
// Create the request to OpenWeatherMap, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
forecastJsonStr = buffer.toString();
Log.v(LOG_TAG, "Forecast string: " + forecastJsonStr);
} catch (IOException e) {
Log.e(LOG_TAG, "Error ", e);
// If the code didn't successfully get the weather data, there's no point in attemping
// to parse it.
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
This is the complete Class:
Code:
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();
/* The date/time conversion code is going to be moved outside the asynctask later,
* so for convenience we're breaking it out into its own method now.
*/
private String getReadableDateString(long time){
// Because the API returns a unix timestamp (measured in seconds),
// it must be converted to milliseconds in order to be converted to valid date.
Date date = new Date(time * 1000);
SimpleDateFormat format = new SimpleDateFormat("E, MMM d");
return format.format(date).toString();
}
/**
* Prepare the weather high/lows for presentation.
*/
private String formatHighLows(double high, double low) {
// For presentation, assume the user doesn't care about tenths of a degree.
long roundedHigh = Math.round(high);
long roundedLow = Math.round(low);
String highLowStr = roundedHigh + "/" + roundedLow;
return highLowStr;
}
/**
* Take the String representing the complete forecast in JSON Format and
* pull out the data we need to construct the Strings needed for the wireframes.
*
* Fortunately parsing is easy: constructor takes the JSON string and converts it
* into an Object hierarchy for us.
*/
private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays)
throws JSONException {
// These are the names of the JSON objects that need to be extracted.
final String OWM_LIST = "list";
final String OWM_WEATHER = "weather";
final String OWM_TEMPERATURE = "temp";
final String OWM_MAX = "max";
final String OWM_MIN = "min";
final String OWM_DATETIME = "dt";
final String OWM_DESCRIPTION = "main";
JSONObject forecastJson = new JSONObject(forecastJsonStr);
JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);
String[] resultStrs = new String[numDays];
for(int i = 0; i < weatherArray.length(); i++) {
// For now, using the format "Day, description, hi/low"
String day;
String description;
String highAndLow;
// Get the JSON object representing the day
JSONObject dayForecast = weatherArray.getJSONObject(i);
// The date/time is returned as a long. We need to convert that
// into something human-readable, since most people won't read "1400356800" as
// "this saturday".
long dateTime = dayForecast.getLong(OWM_DATETIME);
day = getReadableDateString(dateTime);
// description is in a child array called "weather", which is 1 element long.
JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0);
description = weatherObject.getString(OWM_DESCRIPTION);
// Temperatures are in a child object called "temp". Try not to name variables
// "temp" when working with temperature. It confuses everybody.
JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE);
double high = temperatureObject.getDouble(OWM_MAX);
double low = temperatureObject.getDouble(OWM_MIN);
highAndLow = formatHighLows(high, low);
resultStrs[i] = day + " - " + description + " - " + highAndLow;
}
for (String s : resultStrs) {
Log.v(LOG_TAG, "Forecast entry: " + s);
}
return resultStrs;
}
@Override
protected String[] doInBackground(String... params) {
// If there's no zip code, there's nothing to look up. Verify size of params.
if (params.length == 0) {
return null;
}
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String forecastJsonStr = null;
String format = "json";
String units = "metric";
int numDays = 7;
try {
// Construct the URL for the OpenWeatherMap query
// Possible parameters are avaiable at OWM's forecast API page
final String FORECAST_BASE_URL =
"<the-domain>/data/2.5/forecast/daily?";
final String QUERY_PARAM = "q";
final String FORMAT_PARAM = "mode";
final String UNITS_PARAM = "units";
final String DAYS_PARAM = "cnt";
Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon()
.appendQueryParameter(QUERY_PARAM, params[0])
.appendQueryParameter(FORMAT_PARAM, format)
.appendQueryParameter(UNITS_PARAM, units)
.appendQueryParameter(DAYS_PARAM, Integer.toString(numDays))
.build();
URL url = new URL(builtUri.toString());
Log.v(LOG_TAG, "Built URI " + builtUri.toString());
// Create the request to OpenWeatherMap, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
forecastJsonStr = buffer.toString();
Log.v(LOG_TAG, "Forecast string: " + forecastJsonStr);
} catch (IOException e) {
Log.e(LOG_TAG, "Error ", e);
// If the code didn't successfully get the weather data, there's no point in attemping
// to parse it.
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
try {
return getWeatherDataFromJson(forecastJsonStr, numDays);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
e.printStackTrace();
}
// This will only happen if there was an error getting or parsing the forecast.
return null;
}
}
I mean, I know this code can be reduced, but I'm angry about the way it works. Everything needs to be passed to another object! And even rembember all those castings! Castings everywhere!
- The builded URI to the URL.
- The URL to the HttpConection variable.
- Once you connect, save that into the InputStream.
- Make a StringBuffer because we are going to send line by line everything.
- Then create the reader = new BufferedReader(new InputStreamReader(inputStream)).
- Append the lines to the buffer and return if it's ok.
- Else catch all the errors and be sure to close all the connections.
Damn Java !
Forgive me. You'll hate me.
Java is readable, that's the truth... but don't tell me that it is easy for a normal person.
Am I the only one?
If you are a beginner and will straight move to these classes. You will obviously find Java difficult. But Java is very easy if you move step by step from start
Sent from my XT1033 using XDA Premium 4 mobile app
---------- Post added at 04:18 PM ---------- Previous post was at 04:16 PM ----------
And that library also does the same thing inside. Only difference is, your work is already done by author of the library.
Sent from my XT1033 using XDA Premium 4 mobile app
Java is definitely a very verbose language but it's also widely used and so you will find many libraries that do tasks like grab JSON data from a service that have already been implemented for you
manwoman said:
Damn Java !
Forgive me. You'll hate me.
Java is readable, that's the truth... but don't tell me that it is easy for a normal person.
Am I the only one?
Click to expand...
Click to collapse
I don't think you're the only one. It's easy to get scared away by the many too verbose examples available, the key is to look at what you're trying to achieve and then break it up into those parts.
Your code listing is (I think) an attempt to show all steps to get the forecast data, but if that would have been broken up into smaller steps I don't think you'd look at it as quite as bad.
You would then have methods like
Code:
URL getForecastUrl(String parameter);
Code:
BufferedReader getUrl(URL url) { }
Code:
String readAll(BufferedReader reader) {}
Each of which would have had something like 6-7 lines of simple, cohesive code.
I understand your point, but in this particular scenario I think you're the victim of a poorly structured code sample rather than a too verbose language.
If you think the default implementation is too complicated, here are also many java libraries which will make your life easier.

Starting chronometer with if condition comparing strings

I have an android application that is receiving a string from an arduino via Bluetooth, names the string "data" and displays it by setting a TextView to the string "data". I want a chronometer to start when the incoming string matches a predefined string.
For example:
Code:
if data.equals(startChrono)){
chronometerLeft.setBase(SystemClock.elapsedRealtime());
chronometerLeft.start();
I actually have the arduino sending a "g" and am setting my string goL to be "g" but cannot get the chronometer to start when the g is received. My TextView shows the g. Code is below. I've tried several things and at a loss. Using same code for chronometer.start() with onClickListener with a button works great. I just need it to start the chronometer when i receive a specific string from the arduino.
Code:
beginListenForData();
// text.setText("Bluetooth Opened");
}
void beginListenForData() {
final Handler handler = new Handler();
final byte delimiter = 10; // This is the ASCII code for a newline
// character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted() && !stopWorker) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0,
encodedBytes, 0,
encodedBytes.length);
final String data = new String(
encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable() {
public void run() {
text.setText(data);
String goL = "g";
String goR = "f";
chronometerLeft = (Chronometer)findViewById(R.id.chronometerLeft);
chronometerRight = (Chronometer)findViewById(R.id.chronometerRight);
if(data.equals(goL)){
chronometerLeft.setBase(SystemClock.elapsedRealtime());
chronometerLeft.start();
if(data.equals(goR))
chronometerRight.setBase(SystemClock.elapsedRealtime());
chronometerRight.start();
}
}
});
} else {
readBuffer[readBufferPosition++] = b;
}
}
}
} catch (IOException ex) {
stopWorker = true;
}
}
}
});
workerThread.start();
}
Sorry to bother, but in your while loop condition, what does the '!' before Thread do?

Categories

Resources