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