Remove in-app purchase
This commit is contained in:
parent
91e8e9c20c
commit
d4bb313429
|
@ -10,12 +10,6 @@ android {
|
||||||
targetSdkVersion 21
|
targetSdkVersion 21
|
||||||
versionCode 7
|
versionCode 7
|
||||||
versionName "1.1.0"
|
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')
|
File keystoreFile = file('keystore.properties')
|
||||||
|
|
|
@ -41,11 +41,6 @@ public class Settings {
|
||||||
|
|
||||||
// Tags to save the values
|
// Tags to save the values
|
||||||
private static final String CURRENT_HOST_ID = "CURRENT_HOST_ID";
|
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)
|
// Maximum pictures to show on cast list (-1 to show all)
|
||||||
public static final int DEFAULT_MAX_CAST_PICTURES = 12;
|
public static final int DEFAULT_MAX_CAST_PICTURES = 12;
|
||||||
|
@ -74,7 +69,6 @@ public class Settings {
|
||||||
|
|
||||||
// Other keys used in preferences.xml
|
// Other keys used in preferences.xml
|
||||||
public static final String KEY_PREF_ABOUT = "pref_about";
|
public static final String KEY_PREF_ABOUT = "pref_about";
|
||||||
public static final String KEY_PREF_COFFEE = "pref_coffee";
|
|
||||||
|
|
||||||
// Filter watched movies on movie list
|
// Filter watched movies on movie list
|
||||||
public static final String KEY_PREF_MOVIES_FILTER_HIDE_WATCHED = "movies_filter_hide_watched";
|
public static final String KEY_PREF_MOVIES_FILTER_HIDE_WATCHED = "movies_filter_hide_watched";
|
||||||
|
@ -118,16 +112,6 @@ public class Settings {
|
||||||
*/
|
*/
|
||||||
public int currentHostId;
|
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
|
* Protected singleton constructor. Loads all the preferences
|
||||||
* @param context App context
|
* @param context App context
|
||||||
|
@ -138,8 +122,6 @@ public class Settings {
|
||||||
SharedPreferences preferences = context.getSharedPreferences(SETTINGS_KEY, Context.MODE_PRIVATE);
|
SharedPreferences preferences = context.getSharedPreferences(SETTINGS_KEY, Context.MODE_PRIVATE);
|
||||||
|
|
||||||
currentHostId = preferences.getInt(CURRENT_HOST_ID, -1);
|
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();
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
|
||||||
editor.putInt(CURRENT_HOST_ID, currentHostId);
|
editor.putInt(CURRENT_HOST_ID, currentHostId);
|
||||||
editor.putBoolean(SHOW_THANKS_FOR_COFFEE_MESSAGE, showThanksForCofeeMessage);
|
|
||||||
editor.putBoolean(HAS_BOUGHT_COFFEE, hasBoughtCoffee);
|
|
||||||
editor.apply();
|
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);
|
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.BuildConfig;
|
||||||
import com.syncedsynapse.kore2.R;
|
import com.syncedsynapse.kore2.R;
|
||||||
import com.syncedsynapse.kore2.Settings;
|
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;
|
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 String COFFEE_SKU = "coffee";
|
||||||
public static final int COFFEE_RC = 1001;
|
public static final int COFFEE_RC = 1001;
|
||||||
|
|
||||||
// Billing helper
|
|
||||||
private IabHelper mBillingHelper;
|
|
||||||
|
|
||||||
private Settings mSettings;
|
private Settings mSettings;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,13 +53,7 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
|
|
||||||
mSettings = Settings.getInstance(getActivity());
|
mSettings = Settings.getInstance(getActivity());
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
setupPreferences();
|
||||||
mSettings.hasBoughtCoffee = true;
|
|
||||||
}
|
|
||||||
setupPreferences(mSettings.hasBoughtCoffee);
|
|
||||||
if (!BuildConfig.DEBUG) {
|
|
||||||
checkCoffeeUpgradeAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,7 +74,7 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
// Update summaries
|
// Update summaries
|
||||||
setupPreferences(mSettings.hasBoughtCoffee);
|
setupPreferences();
|
||||||
|
|
||||||
if (key.equals(Settings.KEY_PREF_THEME)) {
|
if (key.equals(Settings.KEY_PREF_THEME)) {
|
||||||
//String newTheme = sharedPreferences.getString(key, DEFAULT_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
|
* 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());
|
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
|
// Theme preferences
|
||||||
ListPreference themePref = (ListPreference)findPreference(Settings.KEY_PREF_THEME);
|
ListPreference themePref = (ListPreference)findPreference(Settings.KEY_PREF_THEME);
|
||||||
if (hasBoughtCoffee) {
|
themePref.setSummary(themePref.getEntry());
|
||||||
themePref.setEnabled(true);
|
|
||||||
themePref.setSummary(themePref.getEntry());
|
|
||||||
} else {
|
|
||||||
themePref.setEnabled(false);
|
|
||||||
themePref.setSummary(getActivity().getString(R.string.buy_coffee_to_unlock_themes));
|
|
||||||
}
|
|
||||||
|
|
||||||
// About preference
|
// About preference
|
||||||
String nameAndVersion = getActivity().getString(R.string.app_name);
|
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>
|
следвайте общността на Kore в <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<!-- String for coffee -->
|
<!--<!– String for coffee –>-->
|
||||||
<string name="buy_me_coffee">Почерпете ме с кафе</string>
|
<!--<string name="buy_me_coffee">Почерпете ме с кафе</string>-->
|
||||||
<string name="expresso_please">Еспресо, моля. Благодаря!</string>
|
<!--<string name="expresso_please">Еспресо, моля. Благодаря!</string>-->
|
||||||
<string name="thanks_for_coffe">Благодаря за кафето!</string>
|
<!--<string name="thanks_for_coffe">Благодаря за кафето!</string>-->
|
||||||
<string name="remove_coffee_message">Докоснете за скриване на настройката</string>
|
<!--<string name="remove_coffee_message">Докоснете за скриване на настройката</string>-->
|
||||||
<string name="buy_coffee_to_unlock_themes">За още теми, моля - купете ми кафе</string>
|
<!--<string name="buy_coffee_to_unlock_themes">За още теми, моля - купете ми кафе</string>-->
|
||||||
|
|
||||||
<string name="error_setting_up_billing">Грешка при свързването с услугата Google Play Billing:\n%s</string>
|
<!--<string name="error_setting_up_billing">Грешка при свързването с услугата Google Play Billing:\n%s</string>-->
|
||||||
<string name="error_querying_inventory">Липсва информация за покупката.</string>
|
<!--<string name="error_querying_inventory">Липсва информация за покупката.</string>-->
|
||||||
<string name="error_during_purchased">Възникна грешка по време на закупуването.</string>
|
<!--<string name="error_during_purchased">Възникна грешка по време на закупуването.</string>-->
|
||||||
<string name="purchase_thanks">Благодаря за подкрепата!</string>
|
<!--<string name="purchase_thanks">Благодаря за подкрепата!</string>-->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -283,16 +283,16 @@
|
||||||
folgen Sie Kores <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
|
folgen Sie Kores <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<!-- String for coffee -->
|
<!--<!– String for coffee –>-->
|
||||||
<string name="buy_me_coffee">Kaufen Sie mir einen Kaffee</string>
|
<!--<string name="buy_me_coffee">Kaufen Sie mir einen Kaffee</string>-->
|
||||||
<string name="expresso_please">Espresso, bitte. Danke!</string>
|
<!--<string name="expresso_please">Espresso, bitte. Danke!</string>-->
|
||||||
<string name="thanks_for_coffe">Danke für den Kaffee!</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="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="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_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_querying_inventory">Fehler bei Überprüfung.</string>-->
|
||||||
<string name="error_during_purchased">Ein Fehler trat beim Kauf auf.</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="purchase_thanks">Danke für ihre Unterstützung!</string>-->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -278,16 +278,16 @@
|
||||||
rejoignez la <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Communauté Google+</a></b> de Kore
|
rejoignez la <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Communauté Google+</a></b> de Kore
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<!-- String for coffee -->
|
<!--<!– String for coffee –>-->
|
||||||
<string name="buy_me_coffee">Achetez-moi un café</string>
|
<!--<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="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="thanks_for_coffe">Merci pour le café\u00A0!</string>-->
|
||||||
<string name="remove_coffee_message">Appuyez pour masquer ce paramètre</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="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_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_querying_inventory">Échec.</string>-->
|
||||||
<string name="error_during_purchased">Erreur lors de l\'achat.</string>
|
<!--<string name="error_during_purchased">Erreur lors de l\'achat.</string>-->
|
||||||
<string name="purchase_thanks">Merci pour votre soutien\u00A0!</string>
|
<!--<string name="purchase_thanks">Merci pour votre soutien\u00A0!</string>-->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -281,16 +281,16 @@
|
||||||
segui Kore <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
|
segui Kore <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<!-- String for coffee -->
|
<!--<!– String for coffee –>-->
|
||||||
<string name="buy_me_coffee">Offrimi un caffè</string>
|
<!--<string name="buy_me_coffee">Offrimi un caffè</string>-->
|
||||||
<string name="expresso_please">Un espresso, per favore. Grazie!</string>
|
<!--<string name="expresso_please">Un espresso, per favore. Grazie!</string>-->
|
||||||
<string name="thanks_for_coffe">Grazie per il caffè!</string>
|
<!--<string name="thanks_for_coffe">Grazie per il caffè!</string>-->
|
||||||
<string name="remove_coffee_message">Premi per nascondere questa impostazione</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="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_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_querying_inventory">Errore durante l\'interrogazione dell\'inventario.</string>-->
|
||||||
<string name="error_during_purchased">Si è un errore durante l\'acquisto.</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="purchase_thanks">Grazie per il tuo supporto!</string>-->
|
||||||
|
|
||||||
</resources>
|
</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
|
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>
|
||||||
<string name="buy_me_coffee">Compre-me um café</string>
|
|
||||||
<string name="expresso_please">Expresso, por favor. Obrigado!</string>
|
<!--<string name="buy_me_coffee">Compre-me um café</string>-->
|
||||||
<string name="thanks_for_coffe">Obrigado pelo café!</string>
|
<!--<string name="expresso_please">Expresso, por favor. Obrigado!</string>-->
|
||||||
<string name="remove_coffee_message">Toque para ocultar esta configuração</string>
|
<!--<string name="thanks_for_coffe">Obrigado pelo café!</string>-->
|
||||||
<string name="buy_coffee_to_unlock_themes">Por favor me page um café para desbloquear mais temas</string>
|
<!--<string name="remove_coffee_message">Toque para ocultar esta configuração</string>-->
|
||||||
<string name="error_setting_up_billing">Não consegui configurar o serviço de Pagamento Google: \n%s</string>
|
<!--<string name="buy_coffee_to_unlock_themes">Por favor me page um café para desbloquear mais temas</string>-->
|
||||||
<string name="error_querying_inventory">Erro ao consultar inventário.</string>
|
<!--<string name="error_setting_up_billing">Não consegui configurar o serviço de Pagamento Google: \n%s</string>-->
|
||||||
<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>
|
<!--<string name="error_querying_inventory">Erro ao consultar inventário.</string>-->
|
||||||
<string name="purchase_thanks">Obrigado pelo seu apoio!</string>
|
<!--<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>-->
|
||||||
|
<!--<string name="purchase_thanks">Obrigado pelo seu apoio!</string>-->
|
||||||
|
|
||||||
</resources>
|
</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>
|
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>
|
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="buy_me_coffee">Oferecer um café</string>-->
|
||||||
<string name="thanks_for_coffe">Obrigado pelo café!</string>
|
<!--<string name="expresso_please">Expresso, por favor. Obrigado!</string>-->
|
||||||
<string name="remove_coffee_message">Pressione para ocultar esta configuração</string>
|
<!--<string name="thanks_for_coffe">Obrigado pelo café!</string>-->
|
||||||
<string name="buy_coffee_to_unlock_themes">Por favor ofereça-me um café para desbloquear os temas</string>
|
<!--<string name="remove_coffee_message">Pressione para ocultar esta configuração</string>-->
|
||||||
<string name="error_setting_up_billing">Não consegui configurar o serviço de facturação Google: \n%s</string>
|
<!--<string name="buy_coffee_to_unlock_themes">Por favor ofereça-me um café para desbloquear os temas</string>-->
|
||||||
<string name="error_querying_inventory">Erro ao consultar inventário.</string>
|
<!--<string name="error_setting_up_billing">Não consegui configurar o serviço de facturação Google: \n%s</string>-->
|
||||||
<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>
|
<!--<string name="error_querying_inventory">Erro ao consultar inventário.</string>-->
|
||||||
<string name="purchase_thanks">Obrigado pelo apoio!</string>
|
<!--<string name="error_during_purchased">Ocorreu um erro durante a compra.</string>-->
|
||||||
|
<!--<string name="purchase_thanks">Obrigado pelo apoio!</string>-->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -285,16 +285,16 @@
|
||||||
follow Kore\'s <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
|
follow Kore\'s <b><a href="https://plus.google.com/u/0/communities/110340113064213296333">Google+ Community</a></b>
|
||||||
]]></string>
|
]]></string>
|
||||||
|
|
||||||
<!-- String for coffee -->
|
<!--<!– String for coffee –>-->
|
||||||
<string name="buy_me_coffee">Buy me a coffee</string>
|
<!--<string name="buy_me_coffee">Buy me a coffee</string>-->
|
||||||
<string name="expresso_please">Espresso, please. Thanks!</string>
|
<!--<string name="expresso_please">Espresso, please. Thanks!</string>-->
|
||||||
<string name="thanks_for_coffe">Thanks for the coffee!</string>
|
<!--<string name="thanks_for_coffe">Thanks for the coffee!</string>-->
|
||||||
<string name="remove_coffee_message">Tap to hide this setting</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="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_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_querying_inventory">Failed to query inventory.</string>-->
|
||||||
<string name="error_during_purchased">An error occurred during purchase.</string>
|
<!--<string name="error_during_purchased">An error occurred during purchase.</string>-->
|
||||||
<string name="purchase_thanks">Thanks for your support!</string>
|
<!--<string name="purchase_thanks">Thanks for your support!</string>-->
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -16,11 +16,6 @@
|
||||||
-->
|
-->
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<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
|
<ListPreference
|
||||||
android:key="pref_theme"
|
android:key="pref_theme"
|
||||||
android:title="@string/theme"
|
android:title="@string/theme"
|
||||||
|
|
Loading…
Reference in New Issue