diff --git a/.gitignore b/.gitignore
index a0cc93d..5b42e12 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
pebble-unpacked-apk/
playground-unpacked-apk/
-
+/output
+/playground-app
*.apk
diff --git a/README.md b/README.md
index c7d6366..3d6d278 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,22 @@ Currently I document the open issues here, if there'll be enough of them - we'll
### Protocol
- RTL canned responsed (currently they're LTR)
+## Pre-requirements
+ - [APKtool](https://ibotpeaches.github.io/Apktool/install/)
+ - [Python 2.7](https://www.python.org/downloads/)
+ - [Android SDK](https://developer.android.com/studio/index.html?hl=eo)
+ - I tested it on a Linux machine but you should be able to compile on a Mac too
+
+## How to build RTL apk
+ `$ git clone https://github.com/rosenpin/pebble-rtl`
+
+ `$ cd pebble-rtl`
+
+ `$ ./configure.sh`
+
+ Edit `playground-app/local.properties` and change `sdk.dir` to your SDK directory
+
+ `$ ./compile.sh`
## What to change?
@@ -28,10 +44,10 @@ After changing the code:
- run "build-pg-app.sh"
- run "unpack-pg-app.sh"
- run "unpack-pebble-app.sh"
-- run ./auto_patcher.py (defaults are good, but check out --help)
+- run python2.7 auto_patcher.py (defaults are good, but check out --help)
- run "generate-apk.sh"
- your home baked apk is at output-apks/bla.apk, not sorry for the name :)
### Other stuff
The patch is supposed to be automatic, but it was never tested :)
-In this version - the file to patch is: com/getpebble/android/b/c/e.smali
\ No newline at end of file
+In this version - the file to patch is: com/getpebble/android/b/c/e.smali
diff --git a/clean.sh b/clean.sh
new file mode 100755
index 0000000..91b1708
--- /dev/null
+++ b/clean.sh
@@ -0,0 +1,4 @@
+rm -rf playground-unpacked-apk
+rm -rf app-release-unsigned
+rm -rf pebble-unpacked-apk
+
diff --git a/compile.sh b/compile.sh
new file mode 100755
index 0000000..4a7805d
--- /dev/null
+++ b/compile.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+set -e
+
+./build-pg-app.sh
+# rm -rf playground-unpacked-apk
+./unpack-pg-app.sh
+# rm -rf pebble-unpacked-apk
+./unpack-pebble-app.sh
+python2.7 auto_patcher.py
+./generate-apk.sh
+
+FILENAME=`ls -t1 input-apks/ | head -n 1`
+cp output-apks/$FILENAME output-apks/patched_$FILENAME
+
+./clean.sh
diff --git a/configure.sh b/configure.sh
new file mode 100755
index 0000000..bc16f62
--- /dev/null
+++ b/configure.sh
@@ -0,0 +1 @@
+git clone https://github.com/rosenpin/playground-app
diff --git a/do_it_all.sh b/do_it_all.sh
deleted file mode 100755
index 5e84996..0000000
--- a/do_it_all.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# ./build-pg-app.sh
-# rm -rf playground-unpacked-apk
-# ./unpack-pg-app.sh
-rm -rf pebble-unpacked-apk
-./unpack-pebble-app.sh
-./auto_patcher.py
-./generate-apk.sh
-
-FILENAME=`ls -t1 input-apks/ | head -n 1`
-cp output-apks/bla.apk output-apks/patched_$FILENAME
\ No newline at end of file
diff --git a/generate-apk.sh b/generate-apk.sh
index ba2ec10..f83e1ac 100755
--- a/generate-apk.sh
+++ b/generate-apk.sh
@@ -2,6 +2,8 @@
set -e
-./apktool-files/apktool b -f -p ./apktool-files/framework -o output-apks/bla.apk pebble-unpacked-apk
+apktool b -f pebble-unpacked-apk -o output/$FILENAME
# where do I get jarsigner from?
-jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore keystore/keystore.jks -storepass 123456 output-apks/bla.apk bla
\ No newline at end of file
+FILENAME=`ls -t1 input-apks/ | head -n 1`
+jarsigner -verbose -keystore keystore/keystore.jks -storepass 123456 output/$FILENAME bla
+
diff --git a/playground-app/.gitignore b/playground-app/.gitignore
deleted file mode 100644
index afbdab3..0000000
--- a/playground-app/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.gradle
-/local.properties
-/.idea/workspace.xml
-/.idea/libraries
-.DS_Store
-/build
diff --git a/playground-app/.idea/.name b/playground-app/.idea/.name
deleted file mode 100644
index 333aa89..0000000
--- a/playground-app/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-playground-app
\ No newline at end of file
diff --git a/playground-app/.idea/compiler.xml b/playground-app/.idea/compiler.xml
deleted file mode 100644
index 9a8b7e5..0000000
--- a/playground-app/.idea/compiler.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/.idea/copyright/profiles_settings.xml b/playground-app/.idea/copyright/profiles_settings.xml
deleted file mode 100644
index e7bedf3..0000000
--- a/playground-app/.idea/copyright/profiles_settings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/playground-app/.idea/gradle.xml b/playground-app/.idea/gradle.xml
deleted file mode 100644
index 1bbc21d..0000000
--- a/playground-app/.idea/gradle.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/.idea/misc.xml b/playground-app/.idea/misc.xml
deleted file mode 100644
index 6a1e020..0000000
--- a/playground-app/.idea/misc.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/.idea/modules.xml b/playground-app/.idea/modules.xml
deleted file mode 100644
index cb73068..0000000
--- a/playground-app/.idea/modules.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/.idea/runConfigurations.xml b/playground-app/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/playground-app/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/.idea/vcs.xml b/playground-app/.idea/vcs.xml
deleted file mode 100644
index 6564d52..0000000
--- a/playground-app/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/app/.gitignore b/playground-app/app/.gitignore
deleted file mode 100644
index 796b96d..0000000
--- a/playground-app/app/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/playground-app/app/app.iml b/playground-app/app/app.iml
deleted file mode 100644
index 13b58a5..0000000
--- a/playground-app/app/app.iml
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- generateDebugSources
- generateDebugTestSources
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/app/build.gradle b/playground-app/app/build.gradle
deleted file mode 100644
index dd005b2..0000000
--- a/playground-app/app/build.gradle
+++ /dev/null
@@ -1,25 +0,0 @@
-apply plugin: 'com.android.application'
-
-android {
- compileSdkVersion 23
- buildToolsVersion "21.1.2"
-
- defaultConfig {
- applicationId "com.bla.testapp"
- minSdkVersion 21
- targetSdkVersion 23
- versionCode 1
- versionName "1.0"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
-}
-
-dependencies {
- compile fileTree(dir: 'libs', include: ['*.jar'])
- compile 'com.android.support:appcompat-v7:23.0.1'
-}
diff --git a/playground-app/app/manifest-merger-release-report.txt b/playground-app/app/manifest-merger-release-report.txt
deleted file mode 100644
index 03c5e21..0000000
--- a/playground-app/app/manifest-merger-release-report.txt
+++ /dev/null
@@ -1,53 +0,0 @@
--- Merging decision tree log ---
-manifest
-ADDED from AndroidManifest.xml:2:1
- xmlns:android
- ADDED from AndroidManifest.xml:2:11
- package
- ADDED from AndroidManifest.xml:3:5
- INJECTED from AndroidManifest.xml:0:0
- INJECTED from AndroidManifest.xml:0:0
- android:versionName
- INJECTED from AndroidManifest.xml:0:0
- INJECTED from AndroidManifest.xml:0:0
- android:versionCode
- INJECTED from AndroidManifest.xml:0:0
- INJECTED from AndroidManifest.xml:0:0
-application
-ADDED from AndroidManifest.xml:5:5
-MERGED from com.android.support:appcompat-v7:23.0.1:22:5
-MERGED from com.android.support:support-v4:23.0.1:22:5
- android:label
- ADDED from AndroidManifest.xml:8:9
- android:allowBackup
- ADDED from AndroidManifest.xml:6:9
- android:icon
- ADDED from AndroidManifest.xml:7:9
- android:theme
- ADDED from AndroidManifest.xml:9:9
-activity#com.bla.testapp.MainActivity
-ADDED from AndroidManifest.xml:10:9
- android:label
- ADDED from AndroidManifest.xml:12:13
- android:name
- ADDED from AndroidManifest.xml:11:13
-intent-filter#android.intent.action.MAIN+android.intent.category.LAUNCHER
-ADDED from AndroidManifest.xml:13:13
-action#android.intent.action.MAIN
-ADDED from AndroidManifest.xml:14:17
- android:name
- ADDED from AndroidManifest.xml:14:25
-category#android.intent.category.LAUNCHER
-ADDED from AndroidManifest.xml:16:17
- android:name
- ADDED from AndroidManifest.xml:16:27
-uses-sdk
-INJECTED from AndroidManifest.xml:0:0 reason: use-sdk injection requested
-MERGED from com.android.support:appcompat-v7:23.0.1:20:5
-MERGED from com.android.support:support-v4:23.0.1:20:5
- android:targetSdkVersion
- INJECTED from AndroidManifest.xml:0:0
- INJECTED from AndroidManifest.xml:0:0
- android:minSdkVersion
- INJECTED from AndroidManifest.xml:0:0
- INJECTED from AndroidManifest.xml:0:0
diff --git a/playground-app/app/proguard-rules.pro b/playground-app/app/proguard-rules.pro
deleted file mode 100644
index 2c98583..0000000
--- a/playground-app/app/proguard-rules.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-# Add project specific ProGuard rules here.
-# By default, the flags in this file are appended to flags specified
-# in /Users/flow3d/work/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 *;
-#}
diff --git a/playground-app/app/src/androidTest/java/com/bla/testapp/ApplicationTest.java b/playground-app/app/src/androidTest/java/com/bla/testapp/ApplicationTest.java
deleted file mode 100644
index 7bce1d2..0000000
--- a/playground-app/app/src/androidTest/java/com/bla/testapp/ApplicationTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.bla.testapp;
-
-import android.app.Application;
-import android.test.ApplicationTestCase;
-
-/**
- * Testing Fundamentals
- */
-public class ApplicationTest extends ApplicationTestCase {
- public ApplicationTest() {
- super(Application.class);
- }
-}
\ No newline at end of file
diff --git a/playground-app/app/src/main/AndroidManifest.xml b/playground-app/app/src/main/AndroidManifest.xml
deleted file mode 100644
index bd3dd7b..0000000
--- a/playground-app/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/playground-app/app/src/main/java/com/bla/patch/LoggingInputStream.java b/playground-app/app/src/main/java/com/bla/patch/LoggingInputStream.java
deleted file mode 100644
index 99ddc14..0000000
--- a/playground-app/app/src/main/java/com/bla/patch/LoggingInputStream.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.bla.patch;
-
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-public class LoggingInputStream extends InputStream {
- InputStream baseStream;
- final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
-
- public LoggingInputStream(InputStream baseStream) {
- this.baseStream = baseStream;
- }
-
- public static String bytesToHex(byte[] bytes) {
- return bytesToHex(bytes, 0, bytes.length);
- }
-
- public static String bytesToHex(byte[] bytes, int offset, int count) {
- char[] hexChars = new char[count * 2];
- for ( int j = 0; j < count; j++ ) {
- int v = bytes[offset + j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- @Override
- public int read() throws IOException {
- return this.baseStream.read();
- }
-
- @Override
- public int read(@NonNull byte[] buffer) throws IOException {
- int res = this.baseStream.read(buffer);
- String str = new String(buffer, StandardCharsets.UTF_8);
- Log.i("WOOHOO-input", str);
- Log.i("WOOHOO-input", bytesToHex(buffer));
- return res;
- }
-
- @Override
- public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
- int res = this.baseStream.read(buffer, byteOffset, byteCount);
- String str = new String(buffer, byteOffset, byteCount, StandardCharsets.UTF_8);
- Log.i("WOOHOO-input", str);
- Log.i("WOOHOO-input", bytesToHex(buffer, byteOffset, byteCount));
- return res;
- }
-}
diff --git a/playground-app/app/src/main/java/com/bla/patch/LoggingOutputStream.java b/playground-app/app/src/main/java/com/bla/patch/LoggingOutputStream.java
deleted file mode 100644
index 519cac3..0000000
--- a/playground-app/app/src/main/java/com/bla/patch/LoggingOutputStream.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.bla.patch;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-
-public class LoggingOutputStream extends OutputStream {
- OutputStream baseStream;
- final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
-
- public LoggingOutputStream(OutputStream baseStream) {
- this.baseStream = baseStream;
- }
-
- public static String bytesToHex(byte[] bytes) {
- return bytesToHex(bytes, 0, bytes.length);
- }
-
- public static String bytesToHex(byte[] bytes, int offset, int count) {
- char[] hexChars = new char[count * 2];
- for ( int j = 0; j < count; j++ ) {
- int v = bytes[offset + j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- @Override
- public void write(byte[] buffer) throws IOException {
- String str = new String(buffer, StandardCharsets.UTF_8);
- Log.i("WOOHOO-output", str);
- Log.i("WOOHOO-output", bytesToHex(buffer));
- baseStream.write(buffer);
- }
-
- @Override
- public void write(byte[] buffer, int offset, int count) throws IOException {
- String str = new String(buffer, offset, count, StandardCharsets.UTF_8);
- Log.i("WOOHOO-output", str);
- Log.i("WOOHOO-output", bytesToHex(buffer, offset, count));
- baseStream.write(buffer, offset, count);
- }
-
- @Override
- public void write(int i) throws IOException {
- Log.i("WOOHOO-output", Integer.toString(i));
- baseStream.write(i);
- }
-
-}
diff --git a/playground-app/app/src/main/java/com/bla/patch/ModifierOutputStream.java b/playground-app/app/src/main/java/com/bla/patch/ModifierOutputStream.java
deleted file mode 100644
index 04df5f9..0000000
--- a/playground-app/app/src/main/java/com/bla/patch/ModifierOutputStream.java
+++ /dev/null
@@ -1,286 +0,0 @@
-package com.bla.patch;
-
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.LinkedList;
-
-// What I know so far about message format (I'd build a struct, but java):
-// >H message Length
-// >H message type, notifications type: 0xB1DB
-// B - always 01
-// H some kind of verification code repeated by the other side
-// B - unknown. was 01 for whatsapp & inbox, was 04 for calendar events
-// B message id length (always 10, message ids are 0x10 bytes long)
-// 0x10B - message ID
-// >> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- @Override
- public void write(@NonNull byte[] buffer) throws IOException {
- String str = new String(buffer, StandardCharsets.UTF_8);
- byte[] resBuffer = this.modifyBuffer(buffer);
- baseStream.write(resBuffer);
- }
-
- private int readShort(byte[] buffer, int offset, boolean be) {
- if (be)
- return (((buffer[offset] & 0xff)) << 8) + (buffer[offset + 1] & 0xff);
- return (((buffer[offset + 1] & 0xff)) << 8) + (buffer[offset] & 0xff);
- }
-
- private void writeShort(byte[] buffer, int number, int offset, boolean be) {
- if (be) {
- buffer[offset] = (byte)((number >> 8) & 0xff);
- buffer[offset + 1] = (byte)((number) & 0xff);
- }
- else {
- buffer[offset] = (byte)((number) & 0xff);
- buffer[offset + 1] = (byte)((number >> 8) & 0xff);
- }
- }
-
- private byte[] modifyBuffer(byte[] buffer) {
- return modifyBuffer(buffer, 0, buffer.length);
- }
-
- private byte[] modifyBuffer(byte[] buffer, int offset, int count) {
- // this code is ugly, the proper way would have been to re-construct everything
- // I'm lazy, sorry future observers of this code
- try {
- if (buffer.length < offset + count) {
- throw new IllegalArgumentException("Buffer is too short to handle :(");
- }
- // TODO: handle fragmentation
- // TODO: learn to handle structs in java, this sucks
- int base = offset;
- int message_length = readShort(buffer, base, true); base += 2;
- if (buffer.length != message_length + 4) {
- throw new Exception(String.format("Wrong length encountered: %d != %d",
- message_length + 4, buffer.length));
- }
-
- if ((buffer[base] != (byte)0xB1) || (buffer[base + 1] != (byte)0xDB)) {
- // not a notifications afaik
- return buffer;
- }
- base += 2;
- // skip unknown byte + 2 identifier bytes + other unknown
- base += 4;
- if (buffer[base] != 0x10) {
- throw new Exception(String.format("Unknown message identifier length: %d", buffer[base]));
- }
-
- base += 0x11; // 1 length byte + signature
- // should I verify that the message id matches the inner copy?
-
- int message_length_inner1 = readShort(buffer, base, false); base += 2;
- if (message_length_inner1 != (buffer.length - base)) {
- throw new Exception(String.format("Wrong inner(1) length encountered: %d != %d",
- message_length_inner1, buffer.length - base));
- }
-
- // skip inner signature copy + 0x18 unknown bytes + show/hide byte + other unknown byte
- base += 0x10 + 0x18 + 1 + 1;
-
- int message_length_inner2 = readShort(buffer, base, false); base += 2;
- if (message_length_inner2 + 2 != (buffer.length - base)) {
- throw new Exception(String.format("Wrong inner(2) length encountered: %d != %d",
- message_length_inner2 + 2, buffer.length - base));
- }
-
- int dataFieldCount = buffer[base++];
- int menuItemCount = buffer[base++];
- int sectionBase = base;
- int numBytesAdded = 0;
- LinkedList dataFields = new LinkedList<>();
- for (int i = 0; i < dataFieldCount; i++) {
- byte dataType = buffer[base++];
- int sectionLength = readShort(buffer, base, false); base += 2;
- switch (dataType) {
- case 0x1:
- case 0x3:
- case 0xb:
- case 0x1a:
- case 0x19:
- byte[] reversedSection = reverseSectionContent(buffer, base, sectionLength);
- // UGLY
- byte[] sectionHeader = new byte[3];
- sectionHeader[0] = dataType;
- writeShort(sectionHeader, reversedSection.length, 1, false);
- dataFields.add(sectionHeader);
- dataFields.add(reversedSection);
- numBytesAdded += reversedSection.length - sectionLength;
- break;
- default:
- // certainly bad ones: 0x4, 0x1c, 0x1f
- dataFields.add(Arrays.copyOfRange(buffer, base - 3,
- base + sectionLength));
- }
- base += sectionLength;
- }
-
- int menuItemsBase = base;
- int copyPointer = offset;
- ByteBuffer resBuffer = ByteBuffer.allocate(count + numBytesAdded);
-
- // outer length
- resBuffer.order(ByteOrder.BIG_ENDIAN);
- resBuffer.putShort((short) (message_length + numBytesAdded));
- copyPointer += 2;
-
- resBuffer.order(ByteOrder.LITTLE_ENDIAN);
-
- int curLength = 2 + 4 + 0x11;
- resBuffer.put(buffer, copyPointer, curLength);
- copyPointer += curLength;
- resBuffer.putShort((short) (message_length_inner1 + numBytesAdded));
- copyPointer += 2;
- curLength = 0x10 + 0x18 + 1 + 1;
- resBuffer.put(buffer, copyPointer, curLength);
- copyPointer += curLength;
- resBuffer.putShort((short) (message_length_inner2 + numBytesAdded));
- copyPointer += 2;
- resBuffer.put(buffer, copyPointer, 2);
- copyPointer += 2;
-
- for (byte[] sectionBuffer : dataFields) {
- resBuffer.put(sectionBuffer);
- }
- Log.d(TAG, bytesToHex(resBuffer.array(), 0, resBuffer.position()));
- resBuffer.put(buffer, base, offset + count - base);
-
- return resBuffer.array();
- } catch (Exception ex) {
- // TODO: add better logging
- Log.e(TAG, ex.toString());
- ex.printStackTrace();
- // revert to normal operation on failure
- return Arrays.copyOfRange(buffer, offset, offset + count);
- }
- }
-
- private byte[] reverseSectionContent(byte[] buffer, int offset, int count) throws Exception {
- // a section can contain several strings separated by a null terminator.
- int curBase = offset;
- LinkedList resBufferList = new LinkedList<>();
- byte[] nullTerm = new byte[1];
- nullTerm[0] = 0;
- for (int i = offset; i < offset + count; i++) {
- if (buffer[i] == 0) {
- if (curBase < i) {
- resBufferList.add(reverseText(buffer, curBase, i));
- }
- resBufferList.add(nullTerm);
- // move curBase to be after the \0;
- curBase = i + 1;
- }
- }
-
- if (curBase < offset + count) {
- resBufferList.add(reverseText(buffer, curBase, offset + count));
- }
-
- int totalLength = 0;
- for (byte[] partialBuffer : resBufferList) {
- totalLength += partialBuffer.length;
- }
-
- ByteBuffer resBuffer = ByteBuffer.allocate(totalLength);
- for (byte[] partialBuffer : resBufferList) {
- resBuffer.put(partialBuffer);
- }
-
- return resBuffer.array();
- }
-
- private byte[] reverseText(byte[] buffer, int curBase, int endCursor) throws Exception {
- String input = new String(buffer, curBase, endCursor - curBase, Charset.forName("UTF-8"));
- String result = null;
- try {
- result = TextReverser.reversedText(input, LINE_LIMIT);
- } catch (Exception e) {
- // well... I don't know what happened... just return the original buffer
- return Arrays.copyOfRange(buffer, curBase, endCursor);
- }
-
- return result.getBytes(Charset.forName("UTF-8"));
- }
-
- @Override
- public void write(@NonNull byte[] buffer, int offset, int count) throws IOException {
- byte[] resBuffer = this.modifyBuffer(buffer, offset, count);
- baseStream.write(resBuffer);
- }
-
- @Override
- public void write(int i) throws IOException {
- baseStream.write(i);
- }
-}
diff --git a/playground-app/app/src/main/java/com/bla/patch/TextReverser.java b/playground-app/app/src/main/java/com/bla/patch/TextReverser.java
deleted file mode 100644
index e8f6354..0000000
--- a/playground-app/app/src/main/java/com/bla/patch/TextReverser.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package com.bla.patch;
-
-
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import java.awt.font.TextAttribute;
-import java.text.Bidi;
-
-public class TextReverser {
- private static final String TAG = "WOOHOO-converter";
-
- public static String reversedText(String input, int lineLimit) throws Exception {
- int start = 0;
- int flags = 0;
- Bidi bidi = new Bidi(input, flags);
- if (bidi.isLeftToRight()) {
-// Log.d(TAG, String.format("Not for me: %s", input));
- return input;
- }
- String res2 = "";
- while (input.length() > start) {
-// Log.d(TAG, String.format("Going to do: createLineBidi(%d, %d)", start, Math.min(lineLimit, input.length() - start)));
- Pair lineEndParams = findCurrentLineEnd(input, start, lineLimit);
- int curLineEnd = lineEndParams.first;
- int skipCount = lineEndParams.second;
-
- // just a safety
- if ((curLineEnd == start) && (skipCount == 0)) {
- throw new Exception("Seems like we're not making any progress here...");
- }
-
- Bidi lineBidi = bidi.createLineBidi(start, curLineEnd);
- String line_input = input.substring(start, start + lineBidi.getLength());
- if (start > 0) {
- res2 += "\n";
- }
- res2 += reverseLine(line_input);
- start += lineBidi.getLength();
-
- start += skipCount;
- }
-// tv.setText(res + "\n\n" + res2);
- return res2;
- }
-
- private static Pair findCurrentLineEnd(String input, int start, int lineLimit) {
- int lineEnd = Math.min(start + lineLimit, input.length());
- boolean skipChar = false;
- boolean skippedLineBreak = false;
- int skipCount = 0;
-
- // line breaks take priority (we always want to strip white spaces around them)
- for (int i = start; i < lineEnd; i++) {
- if (input.charAt(i) == '\n') {
- skipChar = true;
- skippedLineBreak = true;
- lineEnd = i;
- break;
- }
- }
-
- // if no line breaks and the result is the whole string, leave it as it is
- if (lineEnd == input.length()) {
- return new Pair<>(lineEnd, 0);
- }
-
- // no line break found, search for a white space
- // TODO: add support for non-whitespace separators (line '-', '+' etc...)
- boolean shouldStop = false;
- for (int currentChar = lineEnd - 1; (currentChar > start) && (!shouldStop); currentChar--) {
- switch (input.charAt(currentChar)) {
- // TODO: find a generic way to determine white spaces
- case ' ':
- case '\t':
- skipChar = true;
- lineEnd = currentChar;
- shouldStop = true;
- break;
- }
- }
- if (skipChar) {
- skipCount = 1;
- while (((lineEnd + skipCount) < input.length()) &&
- isAllowedWhitespace(input, lineEnd + skipCount, skippedLineBreak)) {
- if (input.charAt(lineEnd + skipCount) == '\n') {
- skippedLineBreak = true;
- }
- skipCount++;
- }
- while ((lineEnd > start) &&
- isAllowedWhitespace(input, lineEnd - 1, skippedLineBreak)) {
- if (input.charAt(lineEnd - 1) == '\n') {
- skippedLineBreak = true;
- }
- lineEnd--;
- skipCount++;
- }
- }
- return new Pair<>(lineEnd, skipCount);
- }
-
- private static boolean isAllowedWhitespace(String input, int position,
- boolean skippedLineBreak) {
- return ((input.charAt(position) == ' ') ||
- (input.charAt(position) == '\t') ||
- ((!skippedLineBreak) && (input.charAt(position) == '\n')));
- }
-
- @NonNull
- private static String reverseLine(String input) {
- String res2 = "";
- int flags = 0;
- Bidi bidi = new Bidi(input, flags);
-// String res = String.format("Base: %d", bidi.getBaseLevel());
- for (int i = 0; i < bidi.getRunCount(); i++) {
-// res += String.format("\n%d: %d, %d-%d", i, bidi.getRunLevel(i),
-// bidi.getRunStart(i), bidi.getRunLimit(i));
- String cur_segment = input.substring(bidi.getRunStart(i), bidi.getRunLimit(i));
- try {
- switch (bidi.getRunLevel(i)) {
- case 0:
- res2 += cur_segment;
- break;
- case 1:
- res2 += new StringBuilder(cur_segment).reverse().toString();
- break;
- case 2:
- if (bidi.getRunStart(i - 1) > 0) {
- res2 = res2.substring(0, bidi.getRunStart(i - 1) - 1) + cur_segment + res2.substring(bidi.getRunStart(i - 1), res2.length());
- } else {
- res2 = cur_segment + res2;
- }
- break;
- }
- } catch (Exception ex) {
- // TODO: add better logging
- Log.e(TAG, ex.toString());
- throw ex;
- }
- }
- return res2;
- }
-}
diff --git a/playground-app/app/src/main/java/com/bla/testapp/MainActivity.java b/playground-app/app/src/main/java/com/bla/testapp/MainActivity.java
deleted file mode 100644
index 5d868b4..0000000
--- a/playground-app/app/src/main/java/com/bla/testapp/MainActivity.java
+++ /dev/null
@@ -1,190 +0,0 @@
-package com.bla.testapp;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.bluetooth.BluetoothSocket;
-import android.content.Intent;
-import android.support.annotation.NonNull;
-import android.support.v4.text.TextDirectionHeuristicCompat;
-import android.support.v7.app.ActionBarActivity;
-import android.os.Bundle;
-import android.text.BidiFormatter;
-import android.text.Editable;
-import android.text.TextDirectionHeuristic;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.bla.patch.LoggingOutputStream;
-import com.bla.patch.ModifierOutputStream;
-import com.bla.patch.TextReverser;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.text.Bidi;
-
-
-public class MainActivity extends ActionBarActivity {
- BluetoothSocket socket;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- final EditText textMessage = (EditText)findViewById(R.id.editText);
- final SeekBar bar = (SeekBar)findViewById(R.id.lineLimiter);
- textMessage.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
-
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- updateReversedText(textMessage.getText().toString(), getProgressValue(bar));
- }
- });
- final TextView limiterDisplay = (TextView)findViewById(R.id.lineLimiterDisplay);
- limiterDisplay.setText(String.valueOf(getProgressValue(bar)));
- bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
- limiterDisplay.setText(String.valueOf(getProgressValue(bar)));
- updateReversedText(textMessage.getText().toString(), getProgressValue(bar));
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
-
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
-
- }
- });
- final Button button = (Button)findViewById(R.id.notificationButton);
- button.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- createNotification(textMessage.getText().toString());
- }
- });
- }
-
- private void createNotification(String text) {
- // prepare intent which is triggered if the
- // notification is selected
- Intent intent = new Intent(this, MainActivity.class);
- // use System.currentTimeMillis() to have a unique ID for the pending intent
- PendingIntent pIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), intent, 0);
-
- // build notification
- // the addAction re-use the same intent to keep the example short
- Notification n = new Notification.Builder(this)
- .setContentTitle("My test")
- .setContentText(text)
- .setSmallIcon(R.drawable.ic_launcher)
- .setContentIntent(pIntent)
- .setAutoCancel(true).build();
-// .addAction(R.drawable.ic_launcher, "Call", pIntent)
-// .addAction(R.drawable.ic_launcher, "More", pIntent)
-// .addAction(R.drawable.ic_launcher, "And more", pIntent).build();
-
- NotificationManager notificationManager =
- (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-
- notificationManager.notify(0, n);
- }
-
- @NonNull
- private int getProgressValue(SeekBar bar) {
- return bar.getProgress() + 1;
- }
-
- private void updateReversedText(String input, int lineLimit) {
- TextView tv = (TextView)findViewById(R.id.textView);
-// tv.setText(res + "\n\n" + res2);
- try {
- tv.setText(TextReverser.reversedText(input, lineLimit));
- } catch (Exception ex) {
- tv.setText(ex.toString());
- }
-// byte[] inputBla = hexStringToByteArray("0100B1DB01327D0410AF0550B78F074F50B63E1ED47BFCCA76E900AF0550B78F074F50B63E1ED47BFCCA76ED429C16F674422095DA454F303F15E2BA3F1956000001010004BB000405010A004F6D726920496C646973030700626C610A626C610404003D0000801C0100C70204010107004469736D6973730403020109005265706C7920616C6C084D00596573004E6F004F4B0048656865005468616E6B730049206167726565004E696365004F6E206D7920776179004F4B2C206C6574206D6520676574206261636B20746F20796F75003A29003A28030201010400446F6E65010201010D004F70656E206F6E2070686F6E65050201010A004D75746520496E626F78");
-// byte[] inputBla = hexStringToByteArray("0139B1DB0181810410B716D4D8997640998486CF47D93D2EE82201B716D4D8997640998486CF47D93D2EE8ED429C16F674422095DA454F303F15E2ED111856000001011004F40004040107005961656C203A29033B0035D7A2D7A2D7A2D7A2D7A2D7A2D7A2D7A2D7A2D7A2D7A2D7A2D7A2D7A2D798D79820D799D79ED799D7A0D79920D79FD799D795D795D795D795D79F040400050000801C0100C80204010107004469736D6973730303020110005265706C7920746F205961656C203A29085500F09F988300596573004E6F004F6E206D7920776179004F6B0053656520796F7520736F6F6E0048616861004C6F6C004E69636500536F7272792C2063616E27742074616C6B206E6F7700F09F989E005468616E6B73010201010D004F70656E206F6E2070686F6E65040201010D004D757465205768617473417070");
-// ByteArrayOutputStream ost = new ByteArrayOutputStream();
-// ModifierOutputStream most = new ModifierOutputStream(ost);
-// try {
-// most.write(inputBla);
-// Log.d("WOOHOO-temp", bytesToHex(ost.toByteArray()));
-// } catch (IOException e) {
-// tv.setText(e.toString());
-// }
- }
-
- private byte[] hexStringToByteArray(String s) {
- int len = s.length();
- byte[] data = new byte[len / 2];
- for (int i = 0; i < len; i += 2) {
- data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
- + Character.digit(s.charAt(i+1), 16));
- }
- return data;
- }
-
- final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
-
- public static String bytesToHex(byte[] bytes) {
- return bytesToHex(bytes, 0, bytes.length);
- }
-
- public static String bytesToHex(byte[] bytes, int offset, int count) {
- char[] hexChars = new char[count * 2];
- for ( int j = 0; j < count; j++ ) {
- int v = bytes[offset + j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.menu_main, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- // Handle action bar item clicks here. The action bar will
- // automatically handle clicks on the Home/Up button, so long
- // as you specify a parent activity in AndroidManifest.xml.
- int id = item.getItemId();
-
- //noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- public OutputStream getOutputStream() throws IOException {
- OutputStream bla = socket.getOutputStream();
- return new LoggingOutputStream(bla);
- }
-}
diff --git a/playground-app/app/src/main/java/com/getpebble/android/b/c/LoggingInputStream.java b/playground-app/app/src/main/java/com/getpebble/android/b/c/LoggingInputStream.java
deleted file mode 100644
index 0487ea2..0000000
--- a/playground-app/app/src/main/java/com/getpebble/android/b/c/LoggingInputStream.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.getpebble.android.b.c;
-
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-public class LoggingInputStream extends InputStream {
- InputStream baseStream;
- final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
-
- public LoggingInputStream(InputStream baseStream) {
- this.baseStream = baseStream;
- }
-
- public static String bytesToHex(byte[] bytes) {
- return bytesToHex(bytes, 0, bytes.length);
- }
-
- public static String bytesToHex(byte[] bytes, int offset, int count) {
- char[] hexChars = new char[count * 2];
- for ( int j = 0; j < count; j++ ) {
- int v = bytes[offset + j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- @Override
- public int read() throws IOException {
- return this.baseStream.read();
- }
-
- @Override
- public int read(@NonNull byte[] buffer) throws IOException {
- int res = this.baseStream.read(buffer);
- String str = new String(buffer, StandardCharsets.UTF_8);
- Log.i("WOOHOO-input", str);
- Log.i("WOOHOO-input", bytesToHex(buffer));
- return res;
- }
-
- @Override
- public int read(@NonNull byte[] buffer, int byteOffset, int byteCount) throws IOException {
- int res = this.baseStream.read(buffer, byteOffset, byteCount);
- String str = new String(buffer, byteOffset, byteCount, StandardCharsets.UTF_8);
- Log.i("WOOHOO-input", str);
- Log.i("WOOHOO-input", bytesToHex(buffer, byteOffset, byteCount));
- return res;
- }
-}
diff --git a/playground-app/app/src/main/java/com/getpebble/android/b/c/LoggingOutputStream.java b/playground-app/app/src/main/java/com/getpebble/android/b/c/LoggingOutputStream.java
deleted file mode 100644
index 2f437f7..0000000
--- a/playground-app/app/src/main/java/com/getpebble/android/b/c/LoggingOutputStream.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.getpebble.android.b.c;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-
-public class LoggingOutputStream extends OutputStream {
- OutputStream baseStream;
- final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
-
- public LoggingOutputStream(OutputStream baseStream) {
- this.baseStream = baseStream;
- }
-
- public static String bytesToHex(byte[] bytes) {
- return bytesToHex(bytes, 0, bytes.length);
- }
-
- public static String bytesToHex(byte[] bytes, int offset, int count) {
- char[] hexChars = new char[count * 2];
- for ( int j = 0; j < count; j++ ) {
- int v = bytes[offset + j] & 0xFF;
- hexChars[j * 2] = hexArray[v >>> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- @Override
- public void write(byte[] buffer) throws java.io.IOException {
- String str = new String(buffer, StandardCharsets.UTF_8);
- Log.i("WOOHOO-output", str);
- Log.i("WOOHOO-output", bytesToHex(buffer));
- baseStream.write(buffer);
- }
-
- @Override
- public void write(byte[] buffer, int offset, int count) throws java.io.IOException {
- String str = new String(buffer, offset, count, StandardCharsets.UTF_8);
- Log.i("WOOHOO-output", str);
- Log.i("WOOHOO-output", bytesToHex(buffer, offset, count));
- baseStream.write(buffer, offset, count);
- }
-
- @Override
- public void write(int i) throws java.io.IOException {
- Log.i("WOOHOO-output", Integer.toString(i));
- baseStream.write(i);
- }
-
-}
diff --git a/playground-app/app/src/main/java/com/getpebble/android/b/c/ModifierOutputStream.java b/playground-app/app/src/main/java/com/getpebble/android/b/c/ModifierOutputStream.java
deleted file mode 100644
index d1fa443..0000000
--- a/playground-app/app/src/main/java/com/getpebble/android/b/c/ModifierOutputStream.java
+++ /dev/null
@@ -1,286 +0,0 @@
-package com.getpebble.android.b.c;
-
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.LinkedList;
-
-// What I know so far about message format (I'd build a struct, but java):
-// >H message Length
-// >H message type, notifications type: 0xB1DB
-// B - always 01
-// H some kind of verification code repeated by the other side
-// B - unknown. was 01 for whatsapp & inbox, was 04 for calendar events
-// B message id length (always 10, message ids are 0x10 bytes long)
-// 0x10B - message ID
-// >> 4];
- hexChars[j * 2 + 1] = hexArray[v & 0x0F];
- }
- return new String(hexChars);
- }
-
- @Override
- public void write(@NonNull byte[] buffer) throws IOException {
- String str = new String(buffer, StandardCharsets.UTF_8);
- byte[] resBuffer = this.modifyBuffer(buffer);
- baseStream.write(resBuffer);
- }
-
- private int readShort(byte[] buffer, int offset, boolean be) {
- if (be)
- return (((buffer[offset] & 0xff)) << 8) + (buffer[offset + 1] & 0xff);
- return (((buffer[offset + 1] & 0xff)) << 8) + (buffer[offset] & 0xff);
- }
-
- private void writeShort(byte[] buffer, int number, int offset, boolean be) {
- if (be) {
- buffer[offset] = (byte)((number >> 8) & 0xff);
- buffer[offset + 1] = (byte)((number) & 0xff);
- }
- else {
- buffer[offset] = (byte)((number) & 0xff);
- buffer[offset + 1] = (byte)((number >> 8) & 0xff);
- }
- }
-
- private byte[] modifyBuffer(byte[] buffer) {
- return modifyBuffer(buffer, 0, buffer.length);
- }
-
- private byte[] modifyBuffer(byte[] buffer, int offset, int count) {
- // this code is ugly, the proper way would have been to re-construct everything
- // I'm lazy, sorry future observers of this code
- try {
- if (buffer.length < offset + count) {
- throw new IllegalArgumentException("Buffer is too short to handle :(");
- }
- // TODO: handle fragmentation
- // TODO: learn to handle structs in java, this sucks
- int base = offset;
- int message_length = readShort(buffer, base, true); base += 2;
- if (buffer.length != message_length + 4) {
- throw new Exception(String.format("Wrong length encountered: %d != %d",
- message_length + 4, buffer.length));
- }
-
- if ((buffer[base] != (byte)0xB1) || (buffer[base + 1] != (byte)0xDB)) {
- // not a notifications afaik
- return buffer;
- }
- base += 2;
- // skip unknown byte + 2 identifier bytes + other unknown
- base += 4;
- if (buffer[base] != 0x10) {
- throw new Exception(String.format("Unknown message identifier length: %d", buffer[base]));
- }
-
- base += 0x11; // 1 length byte + signature
- // should I verify that the message id matches the inner copy?
-
- int message_length_inner1 = readShort(buffer, base, false); base += 2;
- if (message_length_inner1 != (buffer.length - base)) {
- throw new Exception(String.format("Wrong inner(1) length encountered: %d != %d",
- message_length_inner1, buffer.length - base));
- }
-
- // skip inner signature copy + 0x18 unknown bytes + show/hide byte + other unknown byte
- base += 0x10 + 0x18 + 1 + 1;
-
- int message_length_inner2 = readShort(buffer, base, false); base += 2;
- if (message_length_inner2 + 2 != (buffer.length - base)) {
- throw new Exception(String.format("Wrong inner(2) length encountered: %d != %d",
- message_length_inner2 + 2, buffer.length - base));
- }
-
- int dataFieldCount = buffer[base++];
- int menuItemCount = buffer[base++];
- int sectionBase = base;
- int numBytesAdded = 0;
- LinkedList dataFields = new LinkedList<>();
- for (int i = 0; i < dataFieldCount; i++) {
- byte dataType = buffer[base++];
- int sectionLength = readShort(buffer, base, false); base += 2;
- switch (dataType) {
- case 0x1:
- case 0x3:
- case 0xb:
- case 0x1a:
- case 0x19:
- byte[] reversedSection = reverseSectionContent(buffer, base, sectionLength);
- // UGLY
- byte[] sectionHeader = new byte[3];
- sectionHeader[0] = dataType;
- writeShort(sectionHeader, reversedSection.length, 1, false);
- dataFields.add(sectionHeader);
- dataFields.add(reversedSection);
- numBytesAdded += reversedSection.length - sectionLength;
- break;
- default:
- // certainly bad ones: 0x4, 0x1c, 0x1f
- dataFields.add(Arrays.copyOfRange(buffer, base - 3,
- base + sectionLength));
- }
- base += sectionLength;
- }
-
- int menuItemsBase = base;
- int copyPointer = offset;
- ByteBuffer resBuffer = ByteBuffer.allocate(count + numBytesAdded);
-
- // outer length
- resBuffer.order(ByteOrder.BIG_ENDIAN);
- resBuffer.putShort((short) (message_length + numBytesAdded));
- copyPointer += 2;
-
- resBuffer.order(ByteOrder.LITTLE_ENDIAN);
-
- int curLength = 2 + 4 + 0x11;
- resBuffer.put(buffer, copyPointer, curLength);
- copyPointer += curLength;
- resBuffer.putShort((short) (message_length_inner1 + numBytesAdded));
- copyPointer += 2;
- curLength = 0x10 + 0x18 + 1 + 1;
- resBuffer.put(buffer, copyPointer, curLength);
- copyPointer += curLength;
- resBuffer.putShort((short) (message_length_inner2 + numBytesAdded));
- copyPointer += 2;
- resBuffer.put(buffer, copyPointer, 2);
- copyPointer += 2;
-
- for (byte[] sectionBuffer : dataFields) {
- resBuffer.put(sectionBuffer);
- }
- Log.d(TAG, bytesToHex(resBuffer.array(), 0, resBuffer.position()));
- resBuffer.put(buffer, base, offset + count - base);
-
- return resBuffer.array();
- } catch (Exception ex) {
- // TODO: add better logging
- Log.e(TAG, ex.toString());
- ex.printStackTrace();
- // revert to normal operation on failure
- return Arrays.copyOfRange(buffer, offset, offset + count);
- }
- }
-
- private byte[] reverseSectionContent(byte[] buffer, int offset, int count) throws Exception {
- // a section can contain several strings separated by a null terminator.
- int curBase = offset;
- LinkedList resBufferList = new LinkedList<>();
- byte[] nullTerm = new byte[1];
- nullTerm[0] = 0;
- for (int i = offset; i < offset + count; i++) {
- if (buffer[i] == 0) {
- if (curBase < i) {
- resBufferList.add(reverseText(buffer, curBase, i));
- }
- resBufferList.add(nullTerm);
- // move curBase to be after the \0;
- curBase = i + 1;
- }
- }
-
- if (curBase < offset + count) {
- resBufferList.add(reverseText(buffer, curBase, offset + count));
- }
-
- int totalLength = 0;
- for (byte[] partialBuffer : resBufferList) {
- totalLength += partialBuffer.length;
- }
-
- ByteBuffer resBuffer = ByteBuffer.allocate(totalLength);
- for (byte[] partialBuffer : resBufferList) {
- resBuffer.put(partialBuffer);
- }
-
- return resBuffer.array();
- }
-
- private byte[] reverseText(byte[] buffer, int curBase, int endCursor) throws Exception {
- String input = new String(buffer, curBase, endCursor - curBase, Charset.forName("UTF-8"));
- String result = null;
- try {
- result = TextReverser.reversedText(input, LINE_LIMIT);
- } catch (Exception e) {
- // well... I don't know what happened... just return the original buffer
- return Arrays.copyOfRange(buffer, curBase, endCursor);
- }
-
- return result.getBytes(Charset.forName("UTF-8"));
- }
-
- @Override
- public void write(@NonNull byte[] buffer, int offset, int count) throws IOException {
- byte[] resBuffer = this.modifyBuffer(buffer, offset, count);
- baseStream.write(resBuffer);
- }
-
- @Override
- public void write(int i) throws IOException {
- baseStream.write(i);
- }
-}
diff --git a/playground-app/app/src/main/java/com/getpebble/android/b/c/TextReverser.java b/playground-app/app/src/main/java/com/getpebble/android/b/c/TextReverser.java
deleted file mode 100644
index 2b28e10..0000000
--- a/playground-app/app/src/main/java/com/getpebble/android/b/c/TextReverser.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.getpebble.android.b.c;
-
-
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.widget.TextView;
-
-import com.bla.testapp.R;
-
-import java.text.Bidi;
-
-public class TextReverser {
- private static final String TAG = "WOOHOO-converter";
-
- public static String reversedText(String input, int lineLimit) throws Exception {
- int start = 0;
- int flags = 0;
- Bidi bidi = new Bidi(input, flags);
- if (bidi.isLeftToRight()) {
- Log.d(TAG, String.format("Not for me: %s", input));
- return input;
- }
- String res2 = "";
- while (input.length() > start) {
-// Log.d(TAG, String.format("Going to do: createLineBidi(%d, %d)", start, Math.min(lineLimit, input.length() - start)));
- Bidi lineBidi = bidi.createLineBidi(start, Math.min(start + lineLimit, input.length()));
- String line_input = input.substring(start, start + lineBidi.getLength());
- if (start > 0) {
- res2 += "\n";
- }
- res2 += reverseLine(line_input);
- start += lineBidi.getLength();
-
- // just a safety
- if (lineBidi.getLength() == 0) {
- throw new Exception("Seems like we're not making any progress here...");
- }
- }
-// tv.setText(res + "\n\n" + res2);
- return res2;
- }
-
- @NonNull
- private static String reverseLine(String input) {
- String res2 = "";
- int flags = 0;
- Bidi bidi = new Bidi(input, flags);
-// String res = String.format("Base: %d", bidi.getBaseLevel());
- for (int i = 0; i < bidi.getRunCount(); i++) {
-// res += String.format("\n%d: %d, %d-%d", i, bidi.getRunLevel(i),
-// bidi.getRunStart(i), bidi.getRunLimit(i));
- String cur_segment = input.substring(bidi.getRunStart(i), bidi.getRunLimit(i));
- try {
- switch (bidi.getRunLevel(i)) {
- case 0:
- res2 += cur_segment;
- break;
- case 1:
- res2 += new StringBuilder(cur_segment).reverse().toString();
- break;
- case 2:
- if (bidi.getRunStart(i - 1) > 0) {
- res2 = res2.substring(0, bidi.getRunStart(i - 1) - 1) + cur_segment + res2.substring(bidi.getRunStart(i - 1), res2.length());
- } else {
- res2 = cur_segment + res2;
- }
- break;
- }
- } catch (Exception ex) {
- // TODO: add better logging
- Log.e(TAG, ex.toString());
- throw ex;
- }
- }
- return res2;
- }
-}
diff --git a/playground-app/app/src/main/res/drawable-hdpi/ic_launcher.png b/playground-app/app/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e..0000000
Binary files a/playground-app/app/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/playground-app/app/src/main/res/drawable-mdpi/ic_launcher.png b/playground-app/app/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047d..0000000
Binary files a/playground-app/app/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/playground-app/app/src/main/res/drawable-xhdpi/ic_launcher.png b/playground-app/app/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d76..0000000
Binary files a/playground-app/app/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/playground-app/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/playground-app/app/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 4df1894..0000000
Binary files a/playground-app/app/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/playground-app/app/src/main/res/layout/activity_main.xml b/playground-app/app/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 0533cad..0000000
--- a/playground-app/app/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/playground-app/app/src/main/res/menu/menu_main.xml b/playground-app/app/src/main/res/menu/menu_main.xml
deleted file mode 100644
index b1cb908..0000000
--- a/playground-app/app/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
diff --git a/playground-app/app/src/main/res/values-w820dp/dimens.xml b/playground-app/app/src/main/res/values-w820dp/dimens.xml
deleted file mode 100644
index 63fc816..0000000
--- a/playground-app/app/src/main/res/values-w820dp/dimens.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- 64dp
-
diff --git a/playground-app/app/src/main/res/values/dimens.xml b/playground-app/app/src/main/res/values/dimens.xml
deleted file mode 100644
index 47c8224..0000000
--- a/playground-app/app/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 16dp
- 16dp
-
diff --git a/playground-app/app/src/main/res/values/strings.xml b/playground-app/app/src/main/res/values/strings.xml
deleted file mode 100644
index 4d47809..0000000
--- a/playground-app/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
- TestApp
- Hello world!
- Settings
-
-
diff --git a/playground-app/app/src/main/res/values/styles.xml b/playground-app/app/src/main/res/values/styles.xml
deleted file mode 100644
index 766ab99..0000000
--- a/playground-app/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
diff --git a/playground-app/build.gradle b/playground-app/build.gradle
deleted file mode 100644
index fc67c9c..0000000
--- a/playground-app/build.gradle
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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:1.0.0-rc2'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
- }
-}
-
-allprojects {
- repositories {
- jcenter()
- }
-}
diff --git a/playground-app/gradle.properties b/playground-app/gradle.properties
deleted file mode 100644
index 1d3591c..0000000
--- a/playground-app/gradle.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# 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.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
-# 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
\ No newline at end of file
diff --git a/playground-app/gradle/wrapper/gradle-wrapper.jar b/playground-app/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index 8c0fb64..0000000
Binary files a/playground-app/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/playground-app/gradle/wrapper/gradle-wrapper.properties b/playground-app/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 0c71e76..0000000
--- a/playground-app/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Wed Apr 10 15:27:10 PDT 2013
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/playground-app/gradlew b/playground-app/gradlew
deleted file mode 100755
index 91a7e26..0000000
--- a/playground-app/gradlew
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/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
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# 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\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
-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"`
-
- # 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 "$@"
diff --git a/playground-app/gradlew.bat b/playground-app/gradlew.bat
deleted file mode 100644
index aec9973..0000000
--- a/playground-app/gradlew.bat
+++ /dev/null
@@ -1,90 +0,0 @@
-@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
diff --git a/playground-app/playground-app.iml b/playground-app/playground-app.iml
deleted file mode 100644
index e346187..0000000
--- a/playground-app/playground-app.iml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/playground-app/settings.gradle b/playground-app/settings.gradle
deleted file mode 100644
index e7b4def..0000000
--- a/playground-app/settings.gradle
+++ /dev/null
@@ -1 +0,0 @@
-include ':app'
diff --git a/playground-app/test_app.iml b/playground-app/test_app.iml
deleted file mode 100644
index c6e99a8..0000000
--- a/playground-app/test_app.iml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/unpack-pebble-app.sh b/unpack-pebble-app.sh
index ccdefbc..1f86e99 100755
--- a/unpack-pebble-app.sh
+++ b/unpack-pebble-app.sh
@@ -4,4 +4,4 @@ FILENAME=`ls -t1 input-apks/ | head -n 1`
echo "Unpacking (the most recent file I found): $FILENAME"
-./apktool-files/apktool d -v -p apktool-files/framework -o pebble-unpacked-apk input-apks/$FILENAME
+apktool d -v -f -o pebble-unpacked-apk input-apks/$FILENAME
diff --git a/unpack-pg-app.sh b/unpack-pg-app.sh
index e1a5652..6962b0c 100755
--- a/unpack-pg-app.sh
+++ b/unpack-pg-app.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-apktool d -v -p framework -o playground-unpacked-apk playground-app/app/build/outputs/apk/app-release-unsigned.apk
\ No newline at end of file
+apktool d -v -f -o playground-unpacked-apk playground-app/app/build/outputs/apk/app-release-unsigned.apk