Remove in-app purchase

This commit is contained in:
Synced Synapse 2015-03-08 22:34:14 +00:00
parent 91e8e9c20c
commit d4bb313429
21 changed files with 76 additions and 2323 deletions

View File

@ -10,12 +10,6 @@ android {
targetSdkVersion 21
versionCode 7
versionName "1.1.0"
if(rootProject.hasProperty("IAP_KEY")) {
buildConfigField("String", "IAP_KEY", "\"${rootProject.property("IAP_KEY")}\"")
} else {
buildConfigField("String", "IAP_KEY", "\"\"")
}
}
File keystoreFile = file('keystore.properties')

View File

@ -41,11 +41,6 @@ public class Settings {
// Tags to save the values
private static final String CURRENT_HOST_ID = "CURRENT_HOST_ID";
private static final String TVSHOWS_FILTER_HIDE_WATCHED = "TVSHOWS_FILTER_HIDE_WATCHED";
private static final String TVSHOW_EPISODES_FILTER_HIDE_WATCHED = "TVSHOW_EPISODES_FILTER_HIDE_WATCHED";
private static final String SHOW_THANKS_FOR_COFFEE_MESSAGE = "SHOW_THANKS_FOR_COFFEE_MESSAGE";
private static final String HAS_BOUGHT_COFFEE = "HAS_BOUGHT_COFFEE";
// Maximum pictures to show on cast list (-1 to show all)
public static final int DEFAULT_MAX_CAST_PICTURES = 12;
@ -74,7 +69,6 @@ public class Settings {
// Other keys used in preferences.xml
public static final String KEY_PREF_ABOUT = "pref_about";
public static final String KEY_PREF_COFFEE = "pref_coffee";
// Filter watched movies on movie list
public static final String KEY_PREF_MOVIES_FILTER_HIDE_WATCHED = "movies_filter_hide_watched";
@ -118,16 +112,6 @@ public class Settings {
*/
public int currentHostId;
/**
* Show the thanks for coffee message
*/
public boolean showThanksForCofeeMessage;
/**
* Local variable to save the last state of the coffe purchase
*/
public boolean hasBoughtCoffee;
/**
* Protected singleton constructor. Loads all the preferences
* @param context App context
@ -138,8 +122,6 @@ public class Settings {
SharedPreferences preferences = context.getSharedPreferences(SETTINGS_KEY, Context.MODE_PRIVATE);
currentHostId = preferences.getInt(CURRENT_HOST_ID, -1);
showThanksForCofeeMessage = preferences.getBoolean(SHOW_THANKS_FOR_COFFEE_MESSAGE, true);
hasBoughtCoffee = preferences.getBoolean(HAS_BOUGHT_COFFEE, false);
}
/**
@ -161,8 +143,6 @@ public class Settings {
SharedPreferences.Editor editor = preferences.edit();
editor.putInt(CURRENT_HOST_ID, currentHostId);
editor.putBoolean(SHOW_THANKS_FOR_COFFEE_MESSAGE, showThanksForCofeeMessage);
editor.putBoolean(HAS_BOUGHT_COFFEE, hasBoughtCoffee);
editor.apply();
}
}

View File

@ -1,570 +0,0 @@
// Portions copyright 2002, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.syncedsynapse.kore2.billing;
// This code was converted from code at http://iharder.sourceforge.net/base64/
// Lots of extraneous features were removed.
/* The original code said:
* <p>
* I am placing this code in the Public Domain. Do with it as you will.
* This software comes with no guarantees or warranties but with
* plenty of well-wishing instead!
* Please visit
* <a href="http://iharder.net/xmlizable">http://iharder.net/xmlizable</a>
* periodically to check for updates or to contribute improvements.
* </p>
*
* @author Robert Harder
* @author rharder@usa.net
* @version 1.3
*/
/**
* Base64 converter class. This code is not a complete MIME encoder;
* it simply converts binary data to base64 data and back.
*
* <p>Note {@link CharBase64} is a GWT-compatible implementation of this
* class.
*/
public class Base64 {
/** Specify encoding (value is {@code true}). */
public final static boolean ENCODE = true;
/** Specify decoding (value is {@code false}). */
public final static boolean DECODE = false;
/** The equals sign (=) as a byte. */
private final static byte EQUALS_SIGN = (byte) '=';
/** The new line character (\n) as a byte. */
private final static byte NEW_LINE = (byte) '\n';
/**
* The 64 valid Base64 values.
*/
private final static byte[] ALPHABET =
{(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
(byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
(byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
(byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
(byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
(byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
(byte) '9', (byte) '+', (byte) '/'};
/**
* The 64 valid web safe Base64 values.
*/
private final static byte[] WEBSAFE_ALPHABET =
{(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
(byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
(byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
(byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
(byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
(byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
(byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
(byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
(byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
(byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
(byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
(byte) '9', (byte) '-', (byte) '_'};
/**
* Translates a Base64 value to either its 6-bit reconstruction value
* or a negative number indicating some other meaning.
**/
private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
62, // Plus sign at decimal 43
-9, -9, -9, // Decimal 44 - 46
63, // Slash at decimal 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, -9, -9, // Decimal 91 - 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
/* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
/** The web safe decodabet */
private final static byte[] WEBSAFE_DECODABET =
{-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
-5, -5, // Whitespace: Tab and Linefeed
-9, -9, // Decimal 11 - 12
-5, // Whitespace: Carriage Return
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
-9, -9, -9, -9, -9, // Decimal 27 - 31
-5, // Whitespace: Space
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
62, // Dash '-' sign at decimal 45
-9, -9, // Decimal 46-47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
-9, -9, -9, // Decimal 58 - 60
-1, // Equals sign at decimal 61
-9, -9, -9, // Decimal 62 - 64
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
-9, -9, -9, -9, // Decimal 91-94
63, // Underscore '_' at decimal 95
-9, // Decimal 96
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
-9, -9, -9, -9, -9 // Decimal 123 - 127
/* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
};
// Indicates white space in encoding
private final static byte WHITE_SPACE_ENC = -5;
// Indicates equals sign in encoding
private final static byte EQUALS_SIGN_ENC = -1;
/** Defeats instantiation. */
private Base64() {
}
/* ******** E N C O D I N G M E T H O D S ******** */
/**
* Encodes up to three bytes of the array <var>source</var>
* and writes the resulting four Base64 bytes to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accommodate <var>srcOffset</var> + 3 for
* the <var>source</var> array or <var>destOffset</var> + 4 for
* the <var>destination</var> array.
* The actual number of significant bytes in your array is
* given by <var>numSigBytes</var>.
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param numSigBytes the number of significant bytes in your array
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param alphabet is the encoding alphabet
* @return the <var>destination</var> array
* @since 1.3
*/
private static byte[] encode3to4(byte[] source, int srcOffset,
int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
// 1 2 3
// 01234567890123456789012345678901 Bit position
// --------000000001111111122222222 Array position from threeBytes
// --------| || || || | Six bit groups to index alphabet
// >>18 >>12 >> 6 >> 0 Right shift necessary
// 0x3f 0x3f 0x3f Additional AND
// Create buffer with zero-padding if there are only one or two
// significant bytes passed in the array.
// We have to shift left 24 in order to flush out the 1's that appear
// when Java treats a value as negative that is cast from a byte to an int.
int inBuff =
(numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
| (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
| (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch (numSigBytes) {
case 3:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
return destination;
case 2:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
case 1:
destination[destOffset] = alphabet[(inBuff >>> 18)];
destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = EQUALS_SIGN;
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
default:
return destination;
} // end switch
} // end encode3to4
/**
* Encodes a byte array into Base64 notation.
* Equivalent to calling
* {@code encodeBytes(source, 0, source.length)}
*
* @param source The data to convert
* @since 1.4
*/
public static String encode(byte[] source) {
return encode(source, 0, source.length, ALPHABET, true);
}
/**
* Encodes a byte array into web safe Base64 notation.
*
* @param source The data to convert
* @param doPadding is {@code true} to pad result with '=' chars
* if it does not fall on 3 byte boundaries
*/
public static String encodeWebSafe(byte[] source, boolean doPadding) {
return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source the data to convert
* @param off offset in array where conversion should begin
* @param len length of data to convert
* @param alphabet the encoding alphabet
* @param doPadding is {@code true} to pad result with '=' chars
* if it does not fall on 3 byte boundaries
* @since 1.4
*/
public static String encode(byte[] source, int off, int len, byte[] alphabet,
boolean doPadding) {
byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
int outLen = outBuff.length;
// If doPadding is false, set length to truncate '='
// padding characters
while (doPadding == false && outLen > 0) {
if (outBuff[outLen - 1] != '=') {
break;
}
outLen -= 1;
}
return new String(outBuff, 0, outLen);
}
/**
* Encodes a byte array into Base64 notation.
*
* @param source the data to convert
* @param off offset in array where conversion should begin
* @param len length of data to convert
* @param alphabet is the encoding alphabet
* @param maxLineLength maximum length of one line.
* @return the BASE64-encoded byte array
*/
public static byte[] encode(byte[] source, int off, int len, byte[] alphabet,
int maxLineLength) {
int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
int len43 = lenDiv3 * 4;
byte[] outBuff = new byte[len43 // Main 4:3
+ (len43 / maxLineLength)]; // New lines
int d = 0;
int e = 0;
int len2 = len - 2;
int lineLength = 0;
for (; d < len2; d += 3, e += 4) {
// The following block of code is the same as
// encode3to4( source, d + off, 3, outBuff, e, alphabet );
// but inlined for faster encoding (~20% improvement)
int inBuff =
((source[d + off] << 24) >>> 8)
| ((source[d + 1 + off] << 24) >>> 16)
| ((source[d + 2 + off] << 24) >>> 24);
outBuff[e] = alphabet[(inBuff >>> 18)];
outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
lineLength += 4;
if (lineLength == maxLineLength) {
outBuff[e + 4] = NEW_LINE;
e++;
lineLength = 0;
} // end if: end of line
} // end for: each piece of array
if (d < len) {
encode3to4(source, d + off, len - d, outBuff, e, alphabet);
lineLength += 4;
if (lineLength == maxLineLength) {
// Add a last newline
outBuff[e + 4] = NEW_LINE;
e++;
}
e += 4;
}
assert (e == outBuff.length);
return outBuff;
}
/* ******** D E C O D I N G M E T H O D S ******** */
/**
* Decodes four bytes from array <var>source</var>
* and writes the resulting bytes (up to three of them)
* to <var>destination</var>.
* The source and destination arrays can be manipulated
* anywhere along their length by specifying
* <var>srcOffset</var> and <var>destOffset</var>.
* This method does not check to make sure your arrays
* are large enough to accommodate <var>srcOffset</var> + 4 for
* the <var>source</var> array or <var>destOffset</var> + 3 for
* the <var>destination</var> array.
* This method returns the actual number of bytes that
* were converted from the Base64 encoding.
*
*
* @param source the array to convert
* @param srcOffset the index where conversion begins
* @param destination the array to hold the conversion
* @param destOffset the index where output will be put
* @param decodabet the decodabet for decoding Base64 content
* @return the number of decoded bytes converted
* @since 1.3
*/
private static int decode4to3(byte[] source, int srcOffset,
byte[] destination, int destOffset, byte[] decodabet) {
// Example: Dk==
if (source[srcOffset + 2] == EQUALS_SIGN) {
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
destination[destOffset] = (byte) (outBuff >>> 16);
return 1;
} else if (source[srcOffset + 3] == EQUALS_SIGN) {
// Example: DkL=
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
| ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
destination[destOffset] = (byte) (outBuff >>> 16);
destination[destOffset + 1] = (byte) (outBuff >>> 8);
return 2;
} else {
// Example: DkLE
int outBuff =
((decodabet[source[srcOffset]] << 24) >>> 6)
| ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
| ((decodabet[source[srcOffset + 2]] << 24) >>> 18)
| ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
destination[destOffset] = (byte) (outBuff >> 16);
destination[destOffset + 1] = (byte) (outBuff >> 8);
destination[destOffset + 2] = (byte) (outBuff);
return 3;
}
} // end decodeToBytes
/**
* Decodes data from Base64 notation.
*
* @param s the string to decode (decoded in default encoding)
* @return the decoded data
* @since 1.4
*/
public static byte[] decode(String s) throws Base64DecoderException {
byte[] bytes = s.getBytes();
return decode(bytes, 0, bytes.length);
}
/**
* Decodes data from web safe Base64 notation.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param s the string to decode (decoded in default encoding)
* @return the decoded data
*/
public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
byte[] bytes = s.getBytes();
return decodeWebSafe(bytes, 0, bytes.length);
}
/**
* Decodes Base64 content in byte array format and returns
* the decoded byte array.
*
* @param source The Base64 encoded data
* @return decoded data
* @since 1.3
* @throws Base64DecoderException
*/
public static byte[] decode(byte[] source) throws Base64DecoderException {
return decode(source, 0, source.length);
}
/**
* Decodes web safe Base64 content in byte array format and returns
* the decoded data.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param source the string to decode (decoded in default encoding)
* @return the decoded data
*/
public static byte[] decodeWebSafe(byte[] source)
throws Base64DecoderException {
return decodeWebSafe(source, 0, source.length);
}
/**
* Decodes Base64 content in byte array format and returns
* the decoded byte array.
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @return decoded data
* @since 1.3
* @throws Base64DecoderException
*/
public static byte[] decode(byte[] source, int off, int len)
throws Base64DecoderException {
return decode(source, off, len, DECODABET);
}
/**
* Decodes web safe Base64 content in byte array format and returns
* the decoded byte array.
* Web safe encoding uses '-' instead of '+', '_' instead of '/'
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @return decoded data
*/
public static byte[] decodeWebSafe(byte[] source, int off, int len)
throws Base64DecoderException {
return decode(source, off, len, WEBSAFE_DECODABET);
}
/**
* Decodes Base64 content using the supplied decodabet and returns
* the decoded byte array.
*
* @param source the Base64 encoded data
* @param off the offset of where to begin decoding
* @param len the length of characters to decode
* @param decodabet the decodabet for decoding Base64 content
* @return decoded data
*/
public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
throws Base64DecoderException {
int len34 = len * 3 / 4;
byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
int outBuffPosn = 0;
byte[] b4 = new byte[4];
int b4Posn = 0;
int i = 0;
byte sbiCrop = 0;
byte sbiDecode = 0;
for (i = 0; i < len; i++) {
sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
sbiDecode = decodabet[sbiCrop];
if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
if (sbiDecode >= EQUALS_SIGN_ENC) {
// An equals sign (for padding) must not occur at position 0 or 1
// and must be the last byte[s] in the encoded value
if (sbiCrop == EQUALS_SIGN) {
int bytesLeft = len - i;
byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
if (b4Posn == 0 || b4Posn == 1) {
throw new Base64DecoderException(
"invalid padding byte '=' at byte offset " + i);
} else if ((b4Posn == 3 && bytesLeft > 2)
|| (b4Posn == 4 && bytesLeft > 1)) {
throw new Base64DecoderException(
"padding byte '=' falsely signals end of encoded value "
+ "at offset " + i);
} else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
throw new Base64DecoderException(
"encoded value has invalid trailing byte");
}
break;
}
b4[b4Posn++] = sbiCrop;
if (b4Posn == 4) {
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
b4Posn = 0;
}
}
} else {
throw new Base64DecoderException("Bad Base64 input character at " + i
+ ": " + source[i + off] + "(decimal)");
}
}
// Because web safe encoding allows non padding base64 encodes, we
// need to pad the rest of the b4 buffer with equal signs when
// b4Posn != 0. There can be at most 2 equal signs at the end of
// four characters, so the b4 buffer must have two or three
// characters. This also catches the case where the input is
// padded with EQUALS_SIGN
if (b4Posn != 0) {
if (b4Posn == 1) {
throw new Base64DecoderException("single trailing character at offset "
+ (len - 1));
}
b4[b4Posn++] = EQUALS_SIGN;
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
}
byte[] out = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out;
}
}

View File

@ -1,32 +0,0 @@
// Copyright 2002, Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.syncedsynapse.kore2.billing;
/**
* Exception thrown when encountering an invalid Base64 input character.
*
* @author nelson
*/
public class Base64DecoderException extends Exception {
public Base64DecoderException() {
super();
}
public Base64DecoderException(String s) {
super(s);
}
private static final long serialVersionUID = 1L;
}

View File

@ -1,43 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncedsynapse.kore2.billing;
/**
* Exception thrown when something went wrong with in-app billing.
* An IabException has an associated IabResult (an error).
* To get the IAB result that caused this exception to be thrown,
* call {@link #getResult()}.
*/
public class IabException extends Exception {
IabResult mResult;
public IabException(IabResult r) {
this(r, null);
}
public IabException(int response, String message) {
this(new IabResult(response, message));
}
public IabException(IabResult r, Exception cause) {
super(r.getMessage(), cause);
mResult = r;
}
public IabException(int response, String message, Exception cause) {
this(new IabResult(response, message), cause);
}
/** Returns the IAB result (error) that this exception signals. */
public IabResult getResult() { return mResult; }
}

View File

@ -1,45 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncedsynapse.kore2.billing;
/**
* Represents the result of an in-app billing operation.
* A result is composed of a response code (an integer) and possibly a
* message (String). You can get those by calling
* {@link #getResponse} and {@link #getMessage()}, respectively. You
* can also inquire whether a result is a success or a failure by
* calling {@link #isSuccess()} and {@link #isFailure()}.
*/
public class IabResult {
int mResponse;
String mMessage;
public IabResult(int response, String message) {
mResponse = response;
if (message == null || message.trim().length() == 0) {
mMessage = IabHelper.getResponseDesc(response);
}
else {
mMessage = message + " (response: " + IabHelper.getResponseDesc(response) + ")";
}
}
public int getResponse() { return mResponse; }
public String getMessage() { return mMessage; }
public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; }
public boolean isFailure() { return !isSuccess(); }
public String toString() { return "IabResult: " + getMessage(); }
}

View File

@ -1,91 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncedsynapse.kore2.billing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a block of information about in-app items.
* An Inventory is returned by such methods as {@link IabHelper#queryInventory}.
*/
public class Inventory {
Map<String,SkuDetails> mSkuMap = new HashMap<String,SkuDetails>();
Map<String,Purchase> mPurchaseMap = new HashMap<String,Purchase>();
Inventory() { }
/** Returns the listing details for an in-app product. */
public SkuDetails getSkuDetails(String sku) {
return mSkuMap.get(sku);
}
/** Returns purchase information for a given product, or null if there is no purchase. */
public Purchase getPurchase(String sku) {
return mPurchaseMap.get(sku);
}
/** Returns whether or not there exists a purchase of the given product. */
public boolean hasPurchase(String sku) {
return mPurchaseMap.containsKey(sku);
}
/** Return whether or not details about the given product are available. */
public boolean hasDetails(String sku) {
return mSkuMap.containsKey(sku);
}
/**
* Erase a purchase (locally) from the inventory, given its product ID. This just
* modifies the Inventory object locally and has no effect on the server! This is
* useful when you have an existing Inventory object which you know to be up to date,
* and you have just consumed an item successfully, which means that erasing its
* purchase data from the Inventory you already have is quicker than querying for
* a new Inventory.
*/
public void erasePurchase(String sku) {
if (mPurchaseMap.containsKey(sku)) mPurchaseMap.remove(sku);
}
/** Returns a list of all owned product IDs. */
List<String> getAllOwnedSkus() {
return new ArrayList<String>(mPurchaseMap.keySet());
}
/** Returns a list of all owned product IDs of a given type */
List<String> getAllOwnedSkus(String itemType) {
List<String> result = new ArrayList<String>();
for (Purchase p : mPurchaseMap.values()) {
if (p.getItemType().equals(itemType)) result.add(p.getSku());
}
return result;
}
/** Returns a list of all purchases. */
List<Purchase> getAllPurchases() {
return new ArrayList<Purchase>(mPurchaseMap.values());
}
void addSkuDetails(SkuDetails d) {
mSkuMap.put(d.getSku(), d);
}
void addPurchase(Purchase p) {
mPurchaseMap.put(p.getSku(), p);
}
}

View File

@ -1,63 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncedsynapse.kore2.billing;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents an in-app billing purchase.
*/
public class Purchase {
String mItemType; // ITEM_TYPE_INAPP or ITEM_TYPE_SUBS
String mOrderId;
String mPackageName;
String mSku;
long mPurchaseTime;
int mPurchaseState;
String mDeveloperPayload;
String mToken;
String mOriginalJson;
String mSignature;
public Purchase(String itemType, String jsonPurchaseInfo, String signature) throws JSONException {
mItemType = itemType;
mOriginalJson = jsonPurchaseInfo;
JSONObject o = new JSONObject(mOriginalJson);
mOrderId = o.optString("orderId");
mPackageName = o.optString("packageName");
mSku = o.optString("productId");
mPurchaseTime = o.optLong("purchaseTime");
mPurchaseState = o.optInt("purchaseState");
mDeveloperPayload = o.optString("developerPayload");
mToken = o.optString("token", o.optString("purchaseToken"));
mSignature = signature;
}
public String getItemType() { return mItemType; }
public String getOrderId() { return mOrderId; }
public String getPackageName() { return mPackageName; }
public String getSku() { return mSku; }
public long getPurchaseTime() { return mPurchaseTime; }
public int getPurchaseState() { return mPurchaseState; }
public String getDeveloperPayload() { return mDeveloperPayload; }
public String getToken() { return mToken; }
public String getOriginalJson() { return mOriginalJson; }
public String getSignature() { return mSignature; }
@Override
public String toString() { return "PurchaseInfo(type:" + mItemType + "):" + mOriginalJson; }
}

View File

@ -1,121 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncedsynapse.kore2.billing;
import android.text.TextUtils;
import android.util.Log;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
/**
* Security-related methods. For a secure implementation, all of this code
* should be implemented on a server that communicates with the
* application on the device. For the sake of simplicity and clarity of this
* example, this code is included here and is executed on the device. If you
* must verify the purchases on the phone, you should obfuscate this code to
* make it harder for an attacker to replace the code with stubs that treat all
* purchases as verified.
*/
public class Security {
private static final String TAG = "IABUtil/Security";
private static final String KEY_FACTORY_ALGORITHM = "RSA";
private static final String SIGNATURE_ALGORITHM = "SHA1withRSA";
/**
* Verifies that the data was signed with the given signature, and returns
* the verified purchase. The data is in JSON format and signed
* with a private key. The data also contains the {@link PurchaseState}
* and product ID of the purchase.
* @param base64PublicKey the base64-encoded public key to use for verifying.
* @param signedData the signed JSON string (signed, not encrypted)
* @param signature the signature for the data, signed with the private key
*/
public static boolean verifyPurchase(String base64PublicKey, String signedData, String signature) {
if (TextUtils.isEmpty(signedData) || TextUtils.isEmpty(base64PublicKey) ||
TextUtils.isEmpty(signature)) {
Log.e(TAG, "Purchase verification failed: missing data.");
return false;
}
PublicKey key = Security.generatePublicKey(base64PublicKey);
return Security.verify(key, signedData, signature);
// TODO: Uncomment this
//return true;
}
/**
* Generates a PublicKey instance from a string containing the
* Base64-encoded public key.
*
* @param encodedPublicKey Base64-encoded public key
* @throws IllegalArgumentException if encodedPublicKey is invalid
*/
public static PublicKey generatePublicKey(String encodedPublicKey) {
try {
byte[] decodedKey = Base64.decode(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
Log.e(TAG, "Invalid key specification.");
throw new IllegalArgumentException(e);
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
throw new IllegalArgumentException(e);
}
}
/**
* Verifies that the signature from the server matches the computed
* signature on the data. Returns true if the data is correctly signed.
*
* @param publicKey public key associated with the developer account
* @param signedData signed data from server
* @param signature server signature
* @return true if the data and signature match
*/
public static boolean verify(PublicKey publicKey, String signedData, String signature) {
Signature sig;
try {
sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(signedData.getBytes());
if (!sig.verify(Base64.decode(signature))) {
Log.e(TAG, "Signature verification failed.");
return false;
}
return true;
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "NoSuchAlgorithmException.");
} catch (InvalidKeyException e) {
Log.e(TAG, "Invalid key specification.");
} catch (SignatureException e) {
Log.e(TAG, "Signature exception.");
} catch (Base64DecoderException e) {
Log.e(TAG, "Base64 decoding failed.");
}
return false;
}
}

View File

@ -1,58 +0,0 @@
/* Copyright (c) 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.syncedsynapse.kore2.billing;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents an in-app product's listing details.
*/
public class SkuDetails {
String mItemType;
String mSku;
String mType;
String mPrice;
String mTitle;
String mDescription;
String mJson;
public SkuDetails(String jsonSkuDetails) throws JSONException {
this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails);
}
public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
mItemType = itemType;
mJson = jsonSkuDetails;
JSONObject o = new JSONObject(mJson);
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mTitle = o.optString("title");
mDescription = o.optString("description");
}
public String getSku() { return mSku; }
public String getType() { return mType; }
public String getPrice() { return mPrice; }
public String getTitle() { return mTitle; }
public String getDescription() { return mDescription; }
@Override
public String toString() {
return "SkuDetails:" + mJson;
}
}

View File

@ -70,23 +70,4 @@ public class SettingsActivity extends ActionBarActivity{
}
return super.onOptionsItemSelected(item);
}
/**
* This is kind of an Hack...
* The settings fragment launches the purchase workflow, which calls
* startIntentSenderForResult on this activity, which, when finished calls
* this onActivityResult.
* Wee need to pass this to the fragment, so it can update itself
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Pass on the activity result to the fragment
if (!settingsFragment.onPurchaseWorkflowFinish(requestCode, resultCode, data)) {
// not handled, so handle it ourselves
super.onActivityResult(requestCode, resultCode, data);
}
else {
LogUtils.LOGD(TAG, "onActivityResult handled by IABUtil.");
}
}
}

View File

@ -29,10 +29,6 @@ import android.widget.Toast;
import com.syncedsynapse.kore2.BuildConfig;
import com.syncedsynapse.kore2.R;
import com.syncedsynapse.kore2.Settings;
import com.syncedsynapse.kore2.billing.IabHelper;
import com.syncedsynapse.kore2.billing.IabResult;
import com.syncedsynapse.kore2.billing.Inventory;
import com.syncedsynapse.kore2.billing.Purchase;
import com.syncedsynapse.kore2.utils.LogUtils;
/**
@ -46,9 +42,6 @@ public class SettingsFragment extends PreferenceFragment
public static final String COFFEE_SKU = "coffee";
public static final int COFFEE_RC = 1001;
// Billing helper
private IabHelper mBillingHelper;
private Settings mSettings;
@Override
@ -60,13 +53,7 @@ public class SettingsFragment extends PreferenceFragment
mSettings = Settings.getInstance(getActivity());
if (BuildConfig.DEBUG) {
mSettings.hasBoughtCoffee = true;
}
setupPreferences(mSettings.hasBoughtCoffee);
if (!BuildConfig.DEBUG) {
checkCoffeeUpgradeAsync();
}
setupPreferences();
}
@Override
@ -87,7 +74,7 @@ public class SettingsFragment extends PreferenceFragment
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// Update summaries
setupPreferences(mSettings.hasBoughtCoffee);
setupPreferences();
if (key.equals(Settings.KEY_PREF_THEME)) {
//String newTheme = sharedPreferences.getString(key, DEFAULT_PREF_THEME);
@ -102,54 +89,13 @@ public class SettingsFragment extends PreferenceFragment
/**
* Sets up the preferences state and summaries
* @param hasBoughtCoffee Whether the user has bought me a coffee
*/
private void setupPreferences(boolean hasBoughtCoffee) {
private void setupPreferences() {
final Settings settings = Settings.getInstance(getActivity());
LogUtils.LOGD(TAG, "Setting up preferences. Has bought coffee? " + hasBoughtCoffee);
// Coffee upgrade
final Preference coffeePref = findPreference(Settings.KEY_PREF_COFFEE);
if (coffeePref != null) {
if (hasBoughtCoffee) {
if (settings.showThanksForCofeeMessage) {
coffeePref.setTitle(getResources().getString(R.string.thanks_for_coffe));
coffeePref.setSummary(getResources().getString(R.string.remove_coffee_message));
coffeePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
settings.showThanksForCofeeMessage = false;
settings.save();
getPreferenceScreen().removePreference(coffeePref);
return true;
}
});
} else {
getPreferenceScreen().removePreference(coffeePref);
}
} else {
coffeePref.setTitle(getResources().getString(R.string.buy_me_coffee));
coffeePref.setSummary(getResources().getString(R.string.expresso_please));
coffeePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
launchCoffeePurchase();
return true;
}
});
}
}
// Theme preferences
ListPreference themePref = (ListPreference)findPreference(Settings.KEY_PREF_THEME);
if (hasBoughtCoffee) {
themePref.setEnabled(true);
themePref.setSummary(themePref.getEntry());
} else {
themePref.setEnabled(false);
themePref.setSummary(getActivity().getString(R.string.buy_coffee_to_unlock_themes));
}
themePref.setSummary(themePref.getEntry());
// About preference
String nameAndVersion = getActivity().getString(R.string.app_name);
@ -170,111 +116,4 @@ public class SettingsFragment extends PreferenceFragment
});
}
/**
* Check if the user has bought coffee and locks/unlocks the ui
*/
private void checkCoffeeUpgradeAsync() {
mBillingHelper = new IabHelper(this.getActivity(), BuildConfig.IAP_KEY);
mBillingHelper.enableDebugLogging(BuildConfig.DEBUG);
final Context context = this.getActivity();
mBillingHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
if (!result.isSuccess()) {
// Oh noes, there was a problem.
Toast.makeText(context,
getResources().getString(R.string.error_setting_up_billing, result.getMessage()),
Toast.LENGTH_SHORT).show();
// Lock UI
mSettings.hasBoughtCoffee = false;
setupPreferences(mSettings.hasBoughtCoffee);
mSettings.save();
// Lock upgrade preference
Preference coffeePreference = findPreference(Settings.KEY_PREF_COFFEE);
coffeePreference.setEnabled(false);
coffeePreference.setSummary(getResources().getString(R.string.error_setting_up_billing, result.getMessage()));
LogUtils.LOGD(TAG, "Problem setting up In-app Billing: " + result);
mBillingHelper.dispose();
mBillingHelper = null;
return;
}
// Have we been disposed of in the meantime? If so, quit.
if (mBillingHelper == null) return;
// IAB is fully set up. Query purchased items
mBillingHelper.queryInventoryAsync(new IabHelper.QueryInventoryFinishedListener() {
@Override
public void onQueryInventoryFinished(IabResult result, Inventory inv) {
// Have we been disposed of in the meantime? If so, quit.
if (mBillingHelper == null) return;
if (result.isFailure()) {
LogUtils.LOGD(TAG, "Failed to query inventory. Result: " + result);
return;
}
Purchase upgradePurchase = inv.getPurchase(COFFEE_SKU);
if (upgradePurchase != null)
LogUtils.LOGD(TAG, "Purchase " + upgradePurchase.toString());
//boolean hasUpgrade = (upgradePurchase != null && verifyDeveloperPayload(upgradePurchase));
boolean hasUpgrade = inv.hasPurchase(COFFEE_SKU);
LogUtils.LOGD(TAG, "Has purchase " + String.valueOf(hasUpgrade));
// update UI accordingly
mSettings.hasBoughtCoffee = hasUpgrade;
if (isAdded()) setupPreferences(mSettings.hasBoughtCoffee);
mSettings.save();
}
});
}
});
}
public void launchCoffeePurchase() {
if (mBillingHelper == null) return;
final Context context = this.getActivity();
mBillingHelper.launchPurchaseFlow(this.getActivity(),
COFFEE_SKU, COFFEE_RC,
new IabHelper.OnIabPurchaseFinishedListener() {
@Override
public void onIabPurchaseFinished(IabResult result, Purchase info) {
// if we were disposed of in the meantime, quit.
if (mBillingHelper == null) return;
LogUtils.LOGD(TAG, "Purchase result: " + result + ". Info: " + info);
if (result.isFailure()) {
String msg = getResources().getString(R.string.error_during_purchased);
msg = msg + "\n" + result.getMessage();
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
LogUtils.LOGD(TAG, "Error in purchase" + result);
return;
}
if (info.getSku().equals(COFFEE_SKU)) {
Toast.makeText(context, R.string.purchase_thanks, Toast.LENGTH_SHORT).show();
mSettings.hasBoughtCoffee = true;
setupPreferences(mSettings.hasBoughtCoffee);
mSettings.save();
}
}
});
}
/**
* This method gets called when the purchase workflow is finished by the enclosing activity
*/
public boolean onPurchaseWorkflowFinish(int requestCode, int resultCode, Intent data) {
LogUtils.LOGD(TAG, "onPurchaseWorkflowFinish(" + requestCode + "," + resultCode + "," + data);
return (mBillingHelper != null) &&
mBillingHelper.handleActivityResult(requestCode, resultCode, data);
}
}

View File

@ -277,16 +277,16 @@
следвайте общността на Kore в <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
]]></string>
<!-- String for coffee -->
<string name="buy_me_coffee">Почерпете ме с кафе</string>
<string name="expresso_please">Еспресо, моля. Благодаря!</string>
<string name="thanks_for_coffe">Благодаря за кафето!</string>
<string name="remove_coffee_message">Докоснете за скриване на настройката</string>
<string name="buy_coffee_to_unlock_themes">За още теми, моля - купете ми кафе</string>
<!--&lt;!&ndash; String for coffee &ndash;&gt;-->
<!--<string name="buy_me_coffee">Почерпете ме с кафе</string>-->
<!--<string name="expresso_please">Еспресо, моля. Благодаря!</string>-->
<!--<string name="thanks_for_coffe">Благодаря за кафето!</string>-->
<!--<string name="remove_coffee_message">Докоснете за скриване на настройката</string>-->
<!--<string name="buy_coffee_to_unlock_themes">За още теми, моля - купете ми кафе</string>-->
<string name="error_setting_up_billing">Грешка при свързването с услугата Google Play Billing:\n%s</string>
<string name="error_querying_inventory">Липсва информация за покупката.</string>
<string name="error_during_purchased">Възникна грешка по време на закупуването.</string>
<string name="purchase_thanks">Благодаря за подкрепата!</string>
<!--<string name="error_setting_up_billing">Грешка при свързването с услугата Google Play Billing:\n%s</string>-->
<!--<string name="error_querying_inventory">Липсва информация за покупката.</string>-->
<!--<string name="error_during_purchased">Възникна грешка по време на закупуването.</string>-->
<!--<string name="purchase_thanks">Благодаря за подкрепата!</string>-->
</resources>

View File

@ -283,16 +283,16 @@
folgen Sie Kores <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
]]></string>
<!-- String for coffee -->
<string name="buy_me_coffee">Kaufen Sie mir einen Kaffee</string>
<string name="expresso_please">Espresso, bitte. Danke!</string>
<string name="thanks_for_coffe">Danke für den Kaffee!</string>
<string name="remove_coffee_message">Hier tippen um diesen Eintrag zu verbergen</string>
<string name="buy_coffee_to_unlock_themes">Bitte kaufen Sie mir einen Kaffee um mehr Themen freizuschalten</string>
<!--&lt;!&ndash; String for coffee &ndash;&gt;-->
<!--<string name="buy_me_coffee">Kaufen Sie mir einen Kaffee</string>-->
<!--<string name="expresso_please">Espresso, bitte. Danke!</string>-->
<!--<string name="thanks_for_coffe">Danke für den Kaffee!</string>-->
<!--<string name="remove_coffee_message">Hier tippen um diesen Eintrag zu verbergen</string>-->
<!--<string name="buy_coffee_to_unlock_themes">Bitte kaufen Sie mir einen Kaffee um mehr Themen freizuschalten</string>-->
<string name="error_setting_up_billing">Google Play Bezahlservice konnte nicht konfiguriert werden:\n%s</string>
<string name="error_querying_inventory">Fehler bei Überprüfung.</string>
<string name="error_during_purchased">Ein Fehler trat beim Kauf auf.</string>
<string name="purchase_thanks">Danke für ihre Unterstützung!</string>
<!--<string name="error_setting_up_billing">Google Play Bezahlservice konnte nicht konfiguriert werden:\n%s</string>-->
<!--<string name="error_querying_inventory">Fehler bei Überprüfung.</string>-->
<!--<string name="error_during_purchased">Ein Fehler trat beim Kauf auf.</string>-->
<!--<string name="purchase_thanks">Danke für ihre Unterstützung!</string>-->
</resources>

View File

@ -278,16 +278,16 @@
rejoignez la <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Communauté Google+</a></b> de Kore
]]></string>
<!-- String for coffee -->
<string name="buy_me_coffee">Achetez-moi un café</string>
<string name="expresso_please">Un expresso, s\'il vous plaît. Merci\u00A0!</string>
<string name="thanks_for_coffe">Merci pour le café\u00A0!</string>
<string name="remove_coffee_message">Appuyez pour masquer ce paramètre</string>
<string name="buy_coffee_to_unlock_themes">S\'il vous plaît achetez-moi un café pour débloquer plus de thèmes</string>
<!--&lt;!&ndash; String for coffee &ndash;&gt;-->
<!--<string name="buy_me_coffee">Achetez-moi un café</string>-->
<!--<string name="expresso_please">Un expresso, s\'il vous plaît. Merci\u00A0!</string>-->
<!--<string name="thanks_for_coffe">Merci pour le café\u00A0!</string>-->
<!--<string name="remove_coffee_message">Appuyez pour masquer ce paramètre</string>-->
<!--<string name="buy_coffee_to_unlock_themes">S\'il vous plaît achetez-moi un café pour débloquer plus de thèmes</string>-->
<string name="error_setting_up_billing">Impossible de configurer le service de facturation du Google Play\u00A0:\n%s</string>
<string name="error_querying_inventory">Échec.</string>
<string name="error_during_purchased">Erreur lors de l\'achat.</string>
<string name="purchase_thanks">Merci pour votre soutien\u00A0!</string>
<!--<string name="error_setting_up_billing">Impossible de configurer le service de facturation du Google Play\u00A0:\n%s</string>-->
<!--<string name="error_querying_inventory">Échec.</string>-->
<!--<string name="error_during_purchased">Erreur lors de l\'achat.</string>-->
<!--<string name="purchase_thanks">Merci pour votre soutien\u00A0!</string>-->
</resources>

View File

@ -281,16 +281,16 @@
segui Kore <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
]]></string>
<!-- String for coffee -->
<string name="buy_me_coffee">Offrimi un caffè</string>
<string name="expresso_please">Un espresso, per favore. Grazie!</string>
<string name="thanks_for_coffe">Grazie per il caffè!</string>
<string name="remove_coffee_message">Premi per nascondere questa impostazione</string>
<string name="buy_coffee_to_unlock_themes">Per favore offrimi un caffè per sbloccare più temi</string>
<!--&lt;!&ndash; String for coffee &ndash;&gt;-->
<!--<string name="buy_me_coffee">Offrimi un caffè</string>-->
<!--<string name="expresso_please">Un espresso, per favore. Grazie!</string>-->
<!--<string name="thanks_for_coffe">Grazie per il caffè!</string>-->
<!--<string name="remove_coffee_message">Premi per nascondere questa impostazione</string>-->
<!--<string name="buy_coffee_to_unlock_themes">Per favore offrimi un caffè per sbloccare più temi</string>-->
<string name="error_setting_up_billing">Non risco ad accedere al Google Play Billing Service:\n%s</string>
<string name="error_querying_inventory">Errore durante l\'interrogazione dell\'inventario.</string>
<string name="error_during_purchased">Si è un errore durante l\'acquisto.</string>
<string name="purchase_thanks">Grazie per il tuo supporto!</string>
<!--<string name="error_setting_up_billing">Non risco ad accedere al Google Play Billing Service:\n%s</string>-->
<!--<string name="error_querying_inventory">Errore durante l\'interrogazione dell\'inventario.</string>-->
<!--<string name="error_during_purchased">Si è un errore durante l\'acquisto.</string>-->
<!--<string name="purchase_thanks">Grazie per il tuo supporto!</string>-->
</resources>

View File

@ -219,13 +219,15 @@ Por favor classifique-nos na <b><a href="market://details?id=com.syncedsynapse.k
Se necessitar de ajuda, visite a <b><a href="http://syncedsynapse.com/kore/kore-faq/">FAQ</a></b> ou adira à <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Comunidade Google+</a></b> do Kore
]]></string>
<string name="buy_me_coffee">Compre-me um café</string>
<string name="expresso_please">Expresso, por favor. Obrigado!</string>
<string name="thanks_for_coffe">Obrigado pelo café!</string>
<string name="remove_coffee_message">Toque para ocultar esta configuração</string>
<string name="buy_coffee_to_unlock_themes">Por favor me page um café para desbloquear mais temas</string>
<string name="error_setting_up_billing">Não consegui configurar o serviço de Pagamento Google: \n%s</string>
<string name="error_querying_inventory">Erro ao consultar inventário.</string>
<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>
<string name="purchase_thanks">Obrigado pelo seu apoio!</string>
<!--<string name="buy_me_coffee">Compre-me um café</string>-->
<!--<string name="expresso_please">Expresso, por favor. Obrigado!</string>-->
<!--<string name="thanks_for_coffe">Obrigado pelo café!</string>-->
<!--<string name="remove_coffee_message">Toque para ocultar esta configuração</string>-->
<!--<string name="buy_coffee_to_unlock_themes">Por favor me page um café para desbloquear mais temas</string>-->
<!--<string name="error_setting_up_billing">Não consegui configurar o serviço de Pagamento Google: \n%s</string>-->
<!--<string name="error_querying_inventory">Erro ao consultar inventário.</string>-->
<!--<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>-->
<!--<string name="purchase_thanks">Obrigado pelo seu apoio!</string>-->
</resources>

View File

@ -212,13 +212,15 @@ Pressione <b><i>Concluir</i></b> para usar o controlo remoto.]]></string>
Por favor classifique-nos na <b><a href="market://details?id=com.syncedsynapse.kore2">Googgle Play</a></b><br><br>
Se necessitar de ajuda, visite a <b><a href="http://syncedsynapse.com/kore/kore-faq/">FAQ</a></b> ou adira à <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Comunidade Google+</a></b> do Kore.]]></string>
<string name="buy_me_coffee">Oferecer um café</string>
<string name="expresso_please">Expresso, por favor. Obrigado!</string>
<string name="thanks_for_coffe">Obrigado pelo café!</string>
<string name="remove_coffee_message">Pressione para ocultar esta configuração</string>
<string name="buy_coffee_to_unlock_themes">Por favor ofereça-me um café para desbloquear os temas</string>
<string name="error_setting_up_billing">Não consegui configurar o serviço de facturação Google: \n%s</string>
<string name="error_querying_inventory">Erro ao consultar inventário.</string>
<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>
<string name="purchase_thanks">Obrigado pelo apoio!</string>
<!--<string name="buy_me_coffee">Oferecer um café</string>-->
<!--<string name="expresso_please">Expresso, por favor. Obrigado!</string>-->
<!--<string name="thanks_for_coffe">Obrigado pelo café!</string>-->
<!--<string name="remove_coffee_message">Pressione para ocultar esta configuração</string>-->
<!--<string name="buy_coffee_to_unlock_themes">Por favor ofereça-me um café para desbloquear os temas</string>-->
<!--<string name="error_setting_up_billing">Não consegui configurar o serviço de facturação Google: \n%s</string>-->
<!--<string name="error_querying_inventory">Erro ao consultar inventário.</string>-->
<!--<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>-->
<!--<string name="purchase_thanks">Obrigado pelo apoio!</string>-->
</resources>

View File

@ -285,16 +285,16 @@
follow Kore\'s <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
]]></string>
<!-- String for coffee -->
<string name="buy_me_coffee">Buy me a coffee</string>
<string name="expresso_please">Espresso, please. Thanks!</string>
<string name="thanks_for_coffe">Thanks for the coffee!</string>
<string name="remove_coffee_message">Tap to hide this setting</string>
<string name="buy_coffee_to_unlock_themes">Please buy me a coffee to unlock more themes</string>
<!--&lt;!&ndash; String for coffee &ndash;&gt;-->
<!--<string name="buy_me_coffee">Buy me a coffee</string>-->
<!--<string name="expresso_please">Espresso, please. Thanks!</string>-->
<!--<string name="thanks_for_coffe">Thanks for the coffee!</string>-->
<!--<string name="remove_coffee_message">Tap to hide this setting</string>-->
<!--<string name="buy_coffee_to_unlock_themes">Please buy me a coffee to unlock more themes</string>-->
<string name="error_setting_up_billing">Couldn\'t setup Google Play Billing Service:\n%s</string>
<string name="error_querying_inventory">Failed to query inventory.</string>
<string name="error_during_purchased">An error occurred during purchase.</string>
<string name="purchase_thanks">Thanks for your support!</string>
<!--<string name="error_setting_up_billing">Couldn\'t setup Google Play Billing Service:\n%s</string>-->
<!--<string name="error_querying_inventory">Failed to query inventory.</string>-->
<!--<string name="error_during_purchased">An error occurred during purchase.</string>-->
<!--<string name="purchase_thanks">Thanks for your support!</string>-->
</resources>

View File

@ -16,11 +16,6 @@
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:key="pref_coffee"
android:title="@string/buy_me_coffee"
android:summary="@string/expresso_please"/>
<ListPreference
android:key="pref_theme"
android:title="@string/theme"