Remove in-app purchase
This commit is contained in:
parent
91e8e9c20c
commit
d4bb313429
|
@ -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')
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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; }
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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(); }
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
<!--<!– 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>-->
|
||||
|
||||
<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>
|
||||
|
|
|
@ -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>
|
||||
<!--<!– 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>-->
|
||||
|
||||
<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>
|
||||
|
|
|
@ -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>
|
||||
<!--<!– 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>-->
|
||||
|
||||
<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>
|
||||
|
|
|
@ -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>
|
||||
<!--<!– 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>-->
|
||||
|
||||
<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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
<!--<!– 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>-->
|
||||
|
||||
<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>
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue