initial commit

This commit is contained in:
Idoideas 2018-11-12 02:55:24 +02:00
commit add996cbaf
102 changed files with 4906 additions and 0 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild

View file

@ -0,0 +1,29 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>

18
.idea/gradle.xml Normal file
View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

33
.idea/misc.xml Normal file
View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="7">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
<item index="6" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="6">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
<item index="4" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
</project>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

48
README.md Normal file
View file

@ -0,0 +1,48 @@
# StickerMaker for WhatsApp
[![Get it in the Play Store](https://i.imgur.com/GcvRPKp.png)](https://play.google.com/store/apps/details?id=com.idoideas.stickermaker)
<img src="https://i.imgur.com/cEvAjAN.png">
You probably heard, or even using right now, the worldwide social messaging app. And altough it is owned by the same company, I'm not talking about Facebook or FB Messenger. I'm talking about the popular mobile app, WhatsApp.
Many people around the world are using this app on a daily basis to send simple, short messages to their friends, colleagues and family. And as of such, the WhatsApp developer team has to make the experience of using their app the best possible, and the most fun for sure.
We all know there are alternatives with richer messeges. Telegram, Messenger, Kik and stuff. However, I don't think no one can beat the user base WhatsApp has, and for that, we are tied to their services.
Recently, they were graceful enough to finally bring us Stickers support for the app, something many apps had before. It was a surprise to be sure, but a welcomed one. However, when I tested this feature, I realized the only way to make my own sticker pack, I need to develop a whole new Android app and have people install it from the store (or through APK, but ordinary people won't do that). So I realized a solution to that problem must be made, so any person could create and share any kind of sticker pack they want, easily and without any hassle, directly from their mobile device.
Introducing ***StickerMaker for WhatsApp*** app by Idoideas, a simple way to enrich your stickers experience on WhatsApp, without the need of any programming knowledge.
## So what can you do in this app?
* Create and organize your very own sticker packs by pack name and creator.
* Share your newly created packs to your friends as easily as clicking a button.
* Load your friends' original packs to your WhatsApp client by sharing the files recieved to the app.
* Modify packs that were created using the app to your liking!
## Notice!
***Sticker packs created using the app and loaded using the app require the app to stay installed on the device and loaded on it if you want to use them. If you happen to remove the app from your device, the sticker packs will be removed from your WhatsApp client and your packs will be lost if weren't shared.***
## Download
You can [download the latest APK right here](), or [download the app directly from the Play Store](https://play.google.com/store/apps/details?id=com.idoideas.stickermaker).
**Supports all Android devices, all the way back to KitKat 4.4!**
## Screenshots
Tested on OnePlus 3T, 2018.
<img src="https://i.imgur.com/lLsucbx.jpg" width="270" height="480">
<img src="https://i.imgur.com/2kDps4R.jpg" width="270" height="480">
<img src="https://i.imgur.com/QtzVPCr.jpg" width="270" height="480">
<img src="https://i.imgur.com/XSJY3y8.png" width="219" height="181">
## Contact
* Twitter: [@idoideas](http://twitter.com/idoideas)
* Reddit: [/u/Idoideas](https://www.reddit.com/user/idoideas)
* Email: Idoideasmail@gmail.com

1
app/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

63
app/build.gradle Normal file
View file

@ -0,0 +1,63 @@
apply plugin: 'com.android.application'
android {
aaptOptions {
noCompress "webp"
}
compileSdkVersion 28
buildToolsVersion "28.0.3"
defaultConfig {
applicationId "com.idoideas.stickermaker"
minSdkVersion 19
targetSdkVersion 28
versionCode 3
versionName "1.0.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
def contentProviderAuthority = applicationId + ".WhatsAppLicensedCode.StickerContentProvider"
// Creates a placeholder property to use in the manifest.
manifestPlaceholders =
[contentProviderAuthority: contentProviderAuthority]
// Adds a new field for the authority to the BuildConfig class.
buildConfigField("String",
"CONTENT_PROVIDER_AUTHORITY",
"\"${contentProviderAuthority}\"")
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dataBinding {
enabled = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'android.arch.navigation:navigation-fragment:1.0.0-alpha07'
implementation 'android.arch.navigation:navigation-ui:1.0.0-alpha07'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.facebook.fresco:fresco:1.10.0'
implementation 'com.facebook.fresco:webpsupport:1.10.0'
implementation 'com.facebook.fresco:animated-webp:1.10.0'
implementation 'com.facebook.fresco:webpsupport:1.10.0'
implementation 'com.google.code.gson:gson:2.8.2'
implementation 'com.google.android.gms:play-services-ads:17.1.0'
implementation 'com.heinrichreimersoftware:material-intro:1.6'
implementation 'com.android.billingclient:billing:1.0'
implementation 'com.github.iammert:MaterialIntroView:1.6.0'
}

21
app/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

BIN
app/release/app-release.apk Normal file

Binary file not shown.

1
app/release/output.json Normal file
View file

@ -0,0 +1 @@
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":3,"versionName":"1.0.2","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]

View file

@ -0,0 +1,26 @@
package com.idoideas.stickermaker;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.idoideas.stickermaker", appContext.getPackageName());
}
}

View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.idoideas.stickermaker">
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<application
android:allowBackup="true"
android:icon="@drawable/stickermakerlogo"
android:label="@string/app_name"
android:roundIcon="@drawable/stickermakerlogo"
android:supportsRtl="false"
android:theme="@style/AppTheme"
tools:node="replace">
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="@string/admob_ad_id"/>
<activity
android:name="com.android.billingclient.api.ProxyBillingActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />
<activity
android:name=".WhatsAppBasedCode.StickerPackListActivity"
android:label="@string/title_activity_sticker_packs_list"
android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".WhatsAppBasedCode.StickerPackDetailsActivity"
android:parentActivityName=".WhatsAppBasedCode.StickerPackListActivity"
tools:ignore="UnusedAttribute">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.idoideas.stickermaker.WhatsAppBasedCode.StickerPackListActivity" />
</activity>
<activity
android:name=".WhatsAppBasedCode.StickerPackInfoActivity"
android:label="@string/title_activity_sticker_pack_info"
android:parentActivityName=".WhatsAppBasedCode.StickerPackDetailsActivity"
tools:ignore="UnusedAttribute">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.idoideas.stickermaker.WhatsAppBasedCode.StickerPackDetailsActivity" />
</activity>
<activity android:name=".NewUserIntroActivity"
android:theme="@style/Theme.Intro"/>
<provider
android:name=".WhatsAppBasedCode.StickerContentProvider"
android:authorities="${contentProviderAuthority}"
android:enabled="true"
android:exported="true"
android:readPermission="com.whatsapp.sticker.READ">
</provider>
</application>
</manifest>

View file

@ -0,0 +1,370 @@
package com.idoideas.stickermaker;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.reflect.TypeToken;
import com.idoideas.stickermaker.WhatsAppBasedCode.Sticker;
import com.idoideas.stickermaker.WhatsAppBasedCode.StickerPack;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class DataArchiver {
private static int BUFFER = 8192;
public static boolean writeStickerBookJSON(List<StickerPack> sb, Context context)
{
try {
SharedPreferences mSettings = context.getSharedPreferences("StickerMaker", Context.MODE_PRIVATE);
String writeValue = new GsonBuilder()
.registerTypeAdapter(Uri.class, new UriSerializer())
.create()
.toJson(
sb,
new TypeToken<ArrayList<StickerPack>>() {}.getType());
SharedPreferences.Editor mEditor = mSettings.edit();
mEditor.putString("stickerbook", writeValue);
mEditor.apply();
return true;
}
catch(Exception e)
{
return false;
}
}
public static ArrayList<StickerPack> readStickerPackJSON(Context context)
{
SharedPreferences mSettings = context.getSharedPreferences("StickerMaker", Context.MODE_PRIVATE);
String loadValue = mSettings.getString("stickerbook", "");
Type listType = new TypeToken<ArrayList<StickerPack>>(){}.getType();
return new GsonBuilder()
.registerTypeAdapter(Uri.class, new UriDeserializer())
.create()
.fromJson(loadValue, listType);
}
public static String createZipFileFromStickerPack(StickerPack sp, Context context){
String id = sp.getIdentifier();
String path = context.getFilesDir()+"/"+id;
createZip cz = new createZip(context, sp);
cz.execute();
return path + "/" + sp.getIdentifier() + ".zip";
}
public static void importZipFileToStickerPack(Uri uri, Context context){
String zipPath = "";
try {
zipPath = FilesUtils.inputStreamToSavedFile(context.getContentResolver().openInputStream(uri), context, UUID.randomUUID().toString()+".zip");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
parseZip pz = new parseZip(context, zipPath);
pz.execute();
}
public static void zip(ArrayList<String> _files, String zipFileName) {
try {
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(zipFileName);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
byte data[] = new byte[BUFFER];
for (int i = 0; i < _files.size(); i++) {
Log.v("Compress", "Adding: " + _files.get(i));
FileInputStream fi = new FileInputStream(_files.get(i));
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(_files.get(i).substring(_files.get(i).lastIndexOf("/") + 1));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void unzip(String _zipFile, String _targetLocation) {
//create target location folder if not exist
dirChecker(_targetLocation);
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.w("DECOMPRESSING FILE", ze.getName());
//create dir if required while unzipping
if (ze.isDirectory()) {
dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_targetLocation + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
Log.w("ENDED DECOMPRESSING", "DONEEEEEE");
} catch (Exception e) {
System.out.println(e);
}
}
private static void dirChecker(String dir) {
File f = new File(dir);
if (!f.isDirectory()) {
f.mkdirs();
}
}
private static void stickerPackToJSONFile(StickerPack sp, String path, Context context) {
try {
String writeValue = new GsonBuilder()
.registerTypeAdapter(Uri.class, new UriSerializer())
.create()
.toJson(
sp,
StickerPack.class);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(new File(path, sp.getIdentifier()+".json")));
outputStreamWriter.write(writeValue);
outputStreamWriter.close();
}
catch (IOException e) {
Log.e("Exception", "File write failed: " + e.toString());
}
}
private static StickerPack JSONFileToStickerPack(String id, String path, Context context) {
String ret = "";
try {
InputStream inputStream = new FileInputStream(new File(path, id+".json"));
if ( inputStream != null ) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString = "";
StringBuilder stringBuilder = new StringBuilder();
while ( (receiveString = bufferedReader.readLine()) != null ) {
stringBuilder.append(receiveString);
}
inputStream.close();
ret = stringBuilder.toString();
}
}
catch (FileNotFoundException e) {
Log.e("login activity", "File not found: " + e.toString());
} catch (IOException e) {
Log.e("login activity", "Can not read file: " + e.toString());
}
return new GsonBuilder()
.registerTypeAdapter(Uri.class, new UriDeserializer())
.create()
.fromJson(ret, StickerPack.class);
}
public static class UriSerializer implements JsonSerializer<Uri> {
public JsonElement serialize(Uri src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.toString());
}
}
public static class UriDeserializer implements JsonDeserializer<Uri> {
@Override
public Uri deserialize(final JsonElement src, final Type srcType,
final JsonDeserializationContext context) throws JsonParseException {
return Uri.parse(src.toString().replace("\"", ""));
}
}
private static class createZip extends AsyncTask<Void, Void, Void> {
private ProgressDialog dialog;
private StickerPack sp;
public createZip(Context context, StickerPack sp) {
dialog = new ProgressDialog(context);
this.sp = sp;
}
@Override
protected void onPreExecute() {
dialog.setMessage("Creating Sticker Pack Zip, Please wait...");
dialog.show();
}
@Override
protected void onPostExecute(Void d) {
if (dialog.isShowing()) {
dialog.dismiss();
}
String id = sp.getIdentifier();
String path = dialog.getContext().getFilesDir()+"/"+id;
ExternalPacksManager.sendStickerPackZipThroughWhatsApp(dialog.getContext(), id);
}
@Override
protected Void doInBackground(Void... params) {
String id = sp.getIdentifier();
ArrayList<String> namesOfFiles = new ArrayList<>();
String path = dialog.getContext().getFilesDir()+"/"+id;
stickerPackToJSONFile(sp, path+"/", dialog.getContext());
for(Sticker s : sp.getStickers()){
namesOfFiles.add(path+"/"+sp.getIdentifier()+"-"+s.getImageFileName()+".webp");
}
namesOfFiles.add(path+"/"+sp.getIdentifier()+"-trayImage.webp");
namesOfFiles.add(path+"/"+sp.getIdentifier()+".json");
zip(namesOfFiles, path + "/" + sp.getIdentifier() + ".zip");
return null;
}
}
private static class parseZip extends AsyncTask<Void, Void, Void> {
private ProgressDialog dialog;
private String zipPath;
private boolean isAlreadyLoaded = false;
private boolean isFailed = false;
public parseZip(Context context, String zipPath) {
dialog = new ProgressDialog(context);
dialog.setCancelable(false);
this.zipPath = zipPath;
}
@Override
protected void onPreExecute() {
dialog.setMessage("Parsing Sticker Pack Zip, Please Wait...");
dialog.setCancelable(false);
dialog.show();
}
@Override
protected void onPostExecute(Void d) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (isAlreadyLoaded){
AlertDialog.Builder builder = new AlertDialog.Builder(dialog.getContext());
builder.setMessage("Sticker Pack is already loaded.")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
alert.show();
}
if (isFailed){
AlertDialog.Builder builder = new AlertDialog.Builder(dialog.getContext());
builder.setMessage("The zip file that was imported is not a proper sticker pack file.")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
alert.show();
}
}
@Override
protected Void doInBackground(Void... params) {
String packId = zipPath.split("/")[zipPath.split("/").length-1].replace(".zip", "");
unzip(zipPath, dialog.getContext().getFilesDir()+"/"+packId+"/");
String path = dialog.getContext().getFilesDir()+"/"+packId;
String originalID = FilesUtils.getActualIDOfPack(path+"/");
File oldFolder = new File(dialog.getContext().getFilesDir(),packId);
if(StickerBook.getStickerPackById(originalID)==null){
File newFolder = null;
try{
newFolder = new File(dialog.getContext().getFilesDir(),originalID);
oldFolder.renameTo(newFolder);
StickerPack sp = JSONFileToStickerPack(originalID, newFolder.getAbsolutePath(), dialog.getContext());
StickerBook.addStickerPackExisting(sp);
} catch (Exception e){
isFailed = true;
if(newFolder!=null){
newFolder.delete();
} else {
oldFolder.delete();
}
}
} else {
oldFolder.delete();
isAlreadyLoaded = true;
}
return null;
}
}
}

View file

@ -0,0 +1,51 @@
package com.idoideas.stickermaker;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.os.StrictMode;
import android.provider.MediaStore;
import com.facebook.common.file.FileUtils;
import java.io.File;
import java.util.ArrayList;
public class ExternalPacksManager {
public static void sendStickerPackZipThroughWhatsApp(Context context, String id){
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
FilesUtils.handleCopyFilesToSdCard(context, id);
Uri uri = Uri.fromFile(new File(
Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/" + context.getString(R.string.app_name)
+"/"+id+".zip")
);
Uri noticeImage = Uri.parse(MediaStore.Images.Media.insertImage(context.getContentResolver(),
BitmapFactory.decodeResource(context.getResources(), R.drawable.stickerpacknotice), null, null));
Intent share = new Intent();
share.setAction(Intent.ACTION_SEND_MULTIPLE);
share.setType("*/*");
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
ArrayList<Uri> files = new ArrayList<Uri>();
files.add(noticeImage);
files.add(uri);
//share.putExtra(Intent.EXTRA_STREAM, uri);
share.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files);
share.setPackage("com.whatsapp");
context.startActivity(share);
}
}

View file

@ -0,0 +1,146 @@
package com.idoideas.stickermaker;
import android.content.Context;
import android.net.Uri;
import android.os.Environment;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
public class FilesUtils {
public static void handleCopyFilesToSdCard(final Context context, String id) {
File targetFolder = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + context.getString(R.string.app_name)
);
deleteAllFiles(targetFolder);
if (!targetFolder.exists()) {
targetFolder.mkdirs();
}
try {
exportFile(new File(context.getFilesDir()+"/"+id+"/"+id+".zip"),targetFolder);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void deleteAllFiles(File targetFolder) {
File[] filesList = targetFolder.listFiles();
if (filesList == null) {
return;
}
for (File file : filesList) {
if (file.isDirectory()) {
deleteAllFiles(file);
file.delete();
} else {
file.delete();
}
}
}
private static File exportFile(File src, File dst) throws IOException {
//if folder does not exist
if (!dst.exists()) {
if (!dst.mkdir()) {
return null;
}
}
File expFile = new File(dst.getPath() + File.separator + src.getPath().split("/")[src.getPath().split("/").length-1]);
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
inChannel = new FileInputStream(src).getChannel();
outChannel = new FileOutputStream(expFile).getChannel();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
inChannel.transferTo(0, inChannel.size(), outChannel);
} finally {
if (inChannel != null)
inChannel.close();
if (outChannel != null)
outChannel.close();
}
return expFile;
}
public static String inputStreamToSavedFile(InputStream input, Context context, String filename){
File targetFolder = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + context.getString(R.string.app_name)
);
deleteAllFiles(targetFolder);
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + context.getString(R.string.app_name)
, filename);
try {
file.createNewFile();
OutputStream output = new FileOutputStream(file);
try {
byte[] buffer = new byte[4 * 1024]; // or other buffer size
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
output.flush();
return file.getAbsolutePath();
} finally {
output.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
return file.getAbsolutePath();
}
}
public static String getActualIDOfPack(String path){
File directory = new File(path);
File[] files = directory.listFiles();
for (int i = 0; i < files.length; i++)
{
if(files[i].getName().contains(".json")){
return files[i].getName().replace(".json", "");
}
}
return null;
}
public static int getUriSize(Uri uri, Context context) throws IOException {
try (final InputStream inputStream = context.getContentResolver().openInputStream(uri);
final ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
if (inputStream == null) {
throw new IOException("cannot get URI SIZE");
}
int read;
byte[] data = new byte[16384];
while ((read = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, read);
}
return buffer.toByteArray().length;
}
}
}

View file

@ -0,0 +1,117 @@
package com.idoideas.stickermaker;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class ImageManipulation {
public static Uri convertImageToWebP(Uri uri, String StickerBookId, String StickerId, Context context){
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(),uri);
dirChecker(context.getFilesDir()+"/"+StickerBookId);
String path = context.getFilesDir()+"/"+StickerBookId+"/"+StickerBookId+"-"+StickerId+".webp";
Log.w("Conversion Data: ", "path: " + path);
/*FileOutputStream out = new FileOutputStream(path);
bitmap = Bitmap.createScaledBitmap(bitmap, 512, 512, true);
Log.w("IMAGE SIZE before comperssion", ""+FilesUtils.getUriSize(Uri.fromFile(new File(path)), context));
bitmap.compress(Bitmap.CompressFormat.WEBP, 100, out); //100-best quality
Log.w("IMAGE SIZE first", ""+FilesUtils.getUriSize(Uri.fromFile(new File(path)), context));
*/
makeSmallestBitmapCompatible(path, bitmap);
return Uri.fromFile(new File(path));
} catch (Exception e) {
e.printStackTrace();
}
return uri;
}
public static Uri convertIconTrayToWebP(Uri uri, String StickerBookId, String StickerId, Context context){
try {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(),uri);
dirChecker(context.getFilesDir()+"/"+StickerBookId);
String path = context.getFilesDir()+"/"+StickerBookId+"/"+StickerBookId+"-"+StickerId+".webp";
Log.w("Conversion Data: ", "path: " + path);
FileOutputStream out = new FileOutputStream(path);
bitmap = Bitmap.createScaledBitmap(bitmap, 256, 256, true);
bitmap.compress(Bitmap.CompressFormat.WEBP, 100, out); //100-best quality
out.close();
return Uri.fromFile(new File(path));
} catch (Exception e) {
e.printStackTrace();
}
return uri;
}
private static void dirChecker(String dir) {
File f = new File(dir);
if (!f.isDirectory()) {
f.mkdirs();
}
}
private static byte[] getByteArray(Bitmap bitmap, int quality) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.WEBP,
quality,
bos);
return bos.toByteArray();
}
private static void makeSmallestBitmapCompatible(String path, Bitmap bitmap){
int quality = 100;
FileOutputStream outs = null;
try {
outs = new FileOutputStream(path);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
bitmap = Bitmap.createScaledBitmap(bitmap, 512, 512, true);
int byteArrayLength = 100000;
ByteArrayOutputStream bos = null;
while((byteArrayLength/1000)>=100){
bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.WEBP,
quality,
bos);
byteArrayLength = bos.toByteArray().length;
quality-=10;
Log.w("IMAGE SIZE IS NOW", byteArrayLength+"");
}
try {
outs.write(bos.toByteArray());
outs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,194 @@
package com.idoideas.stickermaker;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Toast;
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
import com.heinrichreimersoftware.materialintro.app.NavigationPolicy;
import com.heinrichreimersoftware.materialintro.app.OnNavigationBlockedListener;
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
import com.idoideas.stickermaker.WhatsAppBasedCode.StickerPackListActivity;
public class NewUserIntroActivity extends IntroActivity {
@Override protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setButtonBackVisible(false);
addSlide(new SimpleSlide.Builder()
.title("Welcome to StickerMaker for WhatsApp!")
.description("The perfect solution for making and sharing your own WhatsApp sticker packs!")
.image(R.drawable.stickermakerlogo)
.background(R.color.colorAccent)
.scrollable(false)
.build());
if(!checkIfBatteryOptimizationIgnored() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
addSlide(new SimpleSlide.Builder()
.title("We've recognized our app is optimized by Android's Doze system.")
.description("In order for the app to work correctly, please disable the optimization for \"StickerMaker\" after clicking the button.")
.background(R.color.colorAccent)
.scrollable(false)
.buttonCtaLabel("Let's Go")
.buttonCtaClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
startActivityForResult(intent, 4113);
}
})
.build());
}
int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
addSlide(new SimpleSlide.Builder()
.title("We need to save and fetch your stickers!")
.description("In order for the app to work correctly, please allow the write and read storage permissions.")
.background(R.color.colorAccent)
.scrollable(false)
.buttonCtaLabel("Grant Permissions")
.buttonCtaClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
verifyStoragePermissions(NewUserIntroActivity.this);
}
})
.build());
}
addSlide(new SimpleSlide.Builder()
.title("Everything is set!")
.description("You can start creating your sticker packs!\n\n" +
"Please notice! Sticker packs created or loaded with the app require the app being installed on device.\n\n" +
"Uninstalling the app will cause of removing the sticker packs from your WhatsApp client " +
"and will be lost if they weren't shared.")
.background(R.color.colorAccent)
.scrollable(false)
.build());
addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
if(i>0 && i!=getSlides().size()-1){
setNavigation(false, true);
} else {
setNavigation(true, false);
}
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 4113){
if(checkIfBatteryOptimizationIgnored()){
setNavigation(true, false);
nextSlide();
}
}
}
private boolean checkIfBatteryOptimizationIgnored(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isIgnoringBatteryOptimizations(packageName);
} else {
return true;
}
}
private void setNavigation(boolean forward, boolean backward){
setNavigationPolicy(new NavigationPolicy() {
@Override
public boolean canGoForward(int i) {
return forward;
}
@Override
public boolean canGoBackward(int i) {
return backward;
}
});
}
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
},
1
);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setPositiveButton("Let's Go", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
verifyStoragePermissions(NewUserIntroActivity.this);
}
})
.create();
alertDialog.setTitle("Notice!");
alertDialog.setMessage("Allowing storage permissions is crucial for the app to work. Please grant the permissions.");
alertDialog.show();
} else {
setNavigation(true, false);
nextSlide();
}
break;
}
}
}

View file

@ -0,0 +1,100 @@
package com.idoideas.stickermaker;
import android.content.Context;
import android.util.Log;
import com.idoideas.stickermaker.WhatsAppBasedCode.StickerPack;
import java.io.File;
import java.util.ArrayList;
public class StickerBook {
static Context myContext;
public static ArrayList<StickerPack> allStickerPacks = checkIfPacksAreNull();
public static void init(Context context){
myContext = context;
ArrayList<StickerPack> lsp = DataArchiver.readStickerPackJSON(context);
if(lsp!=null && lsp.size()!=0){
allStickerPacks = lsp;
}
}
private static ArrayList<StickerPack> checkIfPacksAreNull(){
if(allStickerPacks==null){
Log.w("IS PACKS NULL?", "YES");
return new ArrayList<>();
}
Log.w("IS PACKS NULL?", "NO");
return allStickerPacks;
}
/*public static String addNewStickerPack(String name, String publisher, String trayImage){
String newId = UUID.randomUUID().toString();
StickerPack sp = new StickerPack(newId,
name,
publisher,
trayImage,
"",
"",
"",
"");
allStickerPacks.add(sp);
return newId;
}*/
public static void addStickerPackExisting(StickerPack sp){
allStickerPacks.add(sp);
}
public static ArrayList<StickerPack> getAllStickerPacks(){
return allStickerPacks;
}
public static StickerPack getStickerPackByName(String stickerPackName){
for (StickerPack sp : allStickerPacks){
if(sp.getName().equals(stickerPackName)){
return sp;
}
}
return null;
}
public static StickerPack getStickerPackById(String stickerPackId){
if(allStickerPacks.isEmpty()){
init(myContext);
}
Log.w("THIS IS THE ALL STICKER", allStickerPacks.toString());
for (StickerPack sp : allStickerPacks){
if(sp.getIdentifier().equals(stickerPackId)){
return sp;
}
}
return null;
}
public static StickerPack getStickerPackByIdWithContext(String stickerPackId, Context context){
if(allStickerPacks.isEmpty()){
init(context);
}
Log.w("THIS IS THE ALL STICKER", allStickerPacks.toString());
for (StickerPack sp : allStickerPacks){
if(sp.getIdentifier().equals(stickerPackId)){
return sp;
}
}
return null;
}
public static void deleteStickerPackById(String stickerPackId){
StickerPack myStickerPack = getStickerPackById(stickerPackId);
new File(myStickerPack.getTrayImageUri().getPath()).getParentFile().delete();
allStickerPacks.remove(myStickerPack);
}
public static StickerPack getStickerPackByIndex(int index){
return allStickerPacks.get(index);
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
public abstract class BaseActivity extends AppCompatActivity {
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
public static final class MessageDialogFragment extends DialogFragment {
private static final String ARG_TITLE_ID = "title_id";
private static final String ARG_MESSAGE = "message";
public static DialogFragment newInstance(@StringRes int titleId, String message) {
DialogFragment fragment = new MessageDialogFragment();
Bundle arguments = new Bundle();
arguments.putInt(ARG_TITLE_ID, titleId);
arguments.putString(ARG_MESSAGE, message);
fragment.setArguments(arguments);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
@StringRes final int title = getArguments().getInt(ARG_TITLE_ID);
String message = getArguments().getString(ARG_MESSAGE);
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity())
.setMessage(message)
.setCancelable(true)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
MessageDialogFragment.this.dismiss();
}
});
if (title != 0) {
dialogBuilder.setTitle(title);
}
return dialogBuilder.create();
}
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
public class BottomFadingRecyclerView extends RecyclerView {
public BottomFadingRecyclerView(Context context) {
super(context);
}
public BottomFadingRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BottomFadingRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected float getTopFadingEdgeStrength() {
return 0.0f;
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.List;
public class Sticker implements Parcelable {
String imageFileName;
List<String> emojis;
Uri uri;
long size;
public Sticker(String imageFileName, List<String> emojis) {
this.imageFileName = imageFileName;
this.emojis = emojis;
}
public Sticker(String imageFileName, Uri uri, List<String> emojis) {
this.imageFileName = imageFileName;
this.emojis = emojis;
this.uri = uri;
}
public Sticker(Parcel in) {
imageFileName = in.readString();
emojis = in.createStringArrayList();
size = in.readLong();
}
public static final Creator<Sticker> CREATOR = new Creator<Sticker>() {
@Override
public Sticker createFromParcel(Parcel in) {
return new Sticker(in);
}
@Override
public Sticker[] newArray(int size) {
return new Sticker[size];
}
};
public void setSize(long size) {
this.size = size;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(imageFileName);
dest.writeStringList(emojis);
dest.writeLong(size);
}
public Uri getUri() {
return uri;
}
public String getImageFileName() {
return imageFileName;
}
}

View file

@ -0,0 +1,318 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.idoideas.stickermaker.BuildConfig;
import com.idoideas.stickermaker.StickerBook;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public class StickerContentProvider extends ContentProvider {
/**
* Do not change the strings listed below, as these are used by WhatsApp. And changing these will break the interface between sticker app and WhatsApp.
*/
public static final String STICKER_PACK_IDENTIFIER_IN_QUERY = "sticker_pack_identifier";
public static final String STICKER_PACK_NAME_IN_QUERY = "sticker_pack_name";
public static final String STICKER_PACK_PUBLISHER_IN_QUERY = "sticker_pack_publisher";
public static final String STICKER_PACK_ICON_IN_QUERY = "sticker_pack_icon";
public static final String ANDROID_APP_DOWNLOAD_LINK_IN_QUERY = "android_play_store_link";
public static final String IOS_APP_DOWNLOAD_LINK_IN_QUERY = "ios_app_download_link";
public static final String PUBLISHER_EMAIL = "sticker_pack_publisher_email";
public static final String PUBLISHER_WEBSITE = "sticker_pack_publisher_website";
public static final String PRIVACY_POLICY_WEBSITE = "sticker_pack_privacy_policy_website";
public static final String LICENSE_AGREENMENT_WEBSITE = "sticker_pack_license_agreement_website";
public static final String STICKER_FILE_NAME_IN_QUERY = "sticker_file_name";
public static final String STICKER_FILE_EMOJI_IN_QUERY = "sticker_emoji";
public static final String CONTENT_SCHEME = "content";
public static Uri AUTHORITY_URI = new Uri.Builder().scheme(StickerContentProvider.CONTENT_SCHEME).authority(BuildConfig.CONTENT_PROVIDER_AUTHORITY).appendPath(StickerContentProvider.METADATA).build();
/**
* Do not change the values in the UriMatcher because otherwise, WhatsApp will not be able to fetch the stickers from the ContentProvider.
*/
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
static final String METADATA = "metadata";
private static final int METADATA_CODE = 1;
private static final int METADATA_CODE_FOR_SINGLE_PACK = 2;
static final String STICKERS = "stickers";
private static final int STICKERS_CODE = 3;
static final String STICKERS_ASSET = "stickers_asset";
private static final int STICKERS_ASSET_CODE = 4;
private static final int STICKER_PACK_TRAY_ICON_CODE = 5;
private List<StickerPack> stickerPackList;
@Override
public boolean onCreate() {
final String authority = BuildConfig.CONTENT_PROVIDER_AUTHORITY;
if (!authority.startsWith(Objects.requireNonNull(getContext()).getPackageName())) {
throw new IllegalStateException("your authority (" + authority + ") for the content provider should start with your package name: " + getContext().getPackageName());
}
//the call to get the metadata for the sticker packs.
MATCHER.addURI(authority, METADATA, METADATA_CODE);
//the call to get the metadata for single sticker pack. * represent the identifier
MATCHER.addURI(authority, METADATA + "/*", METADATA_CODE_FOR_SINGLE_PACK);
//gets the list of stickers for a sticker pack, * respresent the identifier.
MATCHER.addURI(authority, STICKERS + "/*", STICKERS_CODE);
for (StickerPack stickerPack : getStickerPackList()) {
MATCHER.addURI(authority, STICKERS_ASSET + "/" + stickerPack.identifier + "/" + stickerPack.trayImageFile, STICKER_PACK_TRAY_ICON_CODE);
for (Sticker sticker : stickerPack.getStickers()) {
MATCHER.addURI(authority, STICKERS_ASSET + "/" + stickerPack.identifier + "/" + sticker.imageFileName, STICKERS_ASSET_CODE);
}
}
return true;
}
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
final int code = MATCHER.match(uri);
Log.w("BASIC Query URI", String.valueOf(uri));
if(StickerBook.getAllStickerPacks().isEmpty()){
StickerBook.init(getContext());
}
if (code == METADATA_CODE) {
return getPackForAllStickerPacks(uri);
} else if (code == METADATA_CODE_FOR_SINGLE_PACK) {
return getCursorForSingleStickerPack(uri);
} else if (code == STICKERS_CODE) {
return getStickersForAStickerPack(uri);
} else {
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
@Nullable
@Override
public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode) {
final int matchCode = MATCHER.match(uri);
Log.w("ASSETFILE QUERY URI", String.valueOf(uri) + "");
Log.w("ASSETFILE QUERY PATH", uri.getPath() + "");
final List<String> pathSegments = uri.getPathSegments();
StickerPack csp = StickerBook.getStickerPackByIdWithContext(pathSegments.get(pathSegments.size() - 2), getContext());
if(csp!=null){
String filename = pathSegments.get(pathSegments.size() - 1);
ParcelFileDescriptor pfd = null;
try {
if(filename.equals("trayimage") && csp.getTrayImageUri()!=null){
pfd = Objects.requireNonNull(getContext()).getContentResolver().openFileDescriptor(
csp.getTrayImageUri(), "r");
Log.w("ASSETFILE ACTUAL URI", String.valueOf(csp.getTrayImageUri()) + "");
} else {
try {
pfd = Objects.requireNonNull(getContext()).getContentResolver().openFileDescriptor(
csp.getStickerById(Integer.valueOf(filename)).getUri(), "r");
Log.w("ASSETFILE ACTUAL URI", String.valueOf(csp.getStickerById(Integer.valueOf(filename)).getUri()) + "");
} catch (NullPointerException e){
Log.e("StickerMaker", "WhatsApp tried to access a non existent sticker, id: " + filename);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
}
return getImageAsset(uri);
/*if (matchCode == STICKERS_ASSET_CODE || matchCode == STICKER_PACK_TRAY_ICON_CODE) {
return getImageAsset(uri);
}
return null;*/
}
@Nullable
@Override
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException {
Log.w("TESTING THIS", "IS IT OPENING FILE REGULARLY?");
return super.openFile(uri, mode);
}
@Override
public String getType(@NonNull Uri uri) {
final int matchCode = MATCHER.match(uri);
switch (matchCode) {
case METADATA_CODE:
return "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA;
case METADATA_CODE_FOR_SINGLE_PACK:
return "vnd.android.cursor.item/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + METADATA;
case STICKERS_CODE:
return "vnd.android.cursor.dir/vnd." + BuildConfig.CONTENT_PROVIDER_AUTHORITY + "." + STICKERS;
case STICKERS_ASSET_CODE:
return "image/webp";
case STICKER_PACK_TRAY_ICON_CODE:
return "image/png";
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
public List<StickerPack> getStickerPackList() {
/*if (stickerPackList == null) {
readContentFile();
}*/
stickerPackList = StickerBook.getAllStickerPacks();
return stickerPackList;
}
private Cursor getPackForAllStickerPacks(@NonNull Uri uri) {
return getStickerPackInfo(uri, getStickerPackList());
}
private Cursor getCursorForSingleStickerPack(@NonNull Uri uri) {
final String identifier = uri.getLastPathSegment();
for (StickerPack stickerPack : getStickerPackList()) {
if (identifier.equals(stickerPack.identifier)) {
return getStickerPackInfo(uri, Collections.singletonList(stickerPack));
}
}
return getStickerPackInfo(uri, new ArrayList<StickerPack>());
}
@NonNull
private Cursor getStickerPackInfo(@NonNull Uri uri, @NonNull List<StickerPack> stickerPackList) {
MatrixCursor cursor = new MatrixCursor(
new String[]{
STICKER_PACK_IDENTIFIER_IN_QUERY,
STICKER_PACK_NAME_IN_QUERY,
STICKER_PACK_PUBLISHER_IN_QUERY,
STICKER_PACK_ICON_IN_QUERY,
ANDROID_APP_DOWNLOAD_LINK_IN_QUERY,
IOS_APP_DOWNLOAD_LINK_IN_QUERY,
PUBLISHER_EMAIL,
PUBLISHER_WEBSITE,
PRIVACY_POLICY_WEBSITE,
LICENSE_AGREENMENT_WEBSITE
});
for (StickerPack stickerPack : stickerPackList) {
MatrixCursor.RowBuilder builder = cursor.newRow();
builder.add(stickerPack.identifier);
builder.add(stickerPack.name);
builder.add(stickerPack.publisher);
builder.add(stickerPack.trayImageFile);
builder.add(stickerPack.androidPlayStoreLink);
builder.add(stickerPack.iosAppStoreLink);
builder.add(stickerPack.publisherEmail);
builder.add(stickerPack.publisherWebsite);
builder.add(stickerPack.privacyPolicyWebsite);
builder.add(stickerPack.licenseAgreementWebsite);
}
cursor.setNotificationUri(Objects.requireNonNull(getContext()).getContentResolver(), uri);
return cursor;
}
@NonNull
private Cursor getStickersForAStickerPack(@NonNull Uri uri) {
final String identifier = uri.getLastPathSegment();
MatrixCursor cursor = new MatrixCursor(new String[]{STICKER_FILE_NAME_IN_QUERY, STICKER_FILE_EMOJI_IN_QUERY});
for (StickerPack stickerPack : getStickerPackList()) {
if (identifier.equals(stickerPack.identifier)) {
for (Sticker sticker : stickerPack.getStickers()) {
cursor.addRow(new Object[]{sticker.imageFileName, TextUtils.join(",", sticker.emojis)});
}
}
}
cursor.setNotificationUri(Objects.requireNonNull(getContext()).getContentResolver(), uri);
return cursor;
}
private AssetFileDescriptor getImageAsset(Uri uri) throws IllegalArgumentException {
Log.w("IMAGE ASSET", "ASKING FOR ASSET?");
AssetManager am = Objects.requireNonNull(getContext()).getAssets();
final List<String> pathSegments = uri.getPathSegments();
if (pathSegments.size() != 3) {
throw new IllegalArgumentException("path segments should be 3, uri is: " + uri);
}
String fileName = pathSegments.get(pathSegments.size() - 1);
final String identifier = pathSegments.get(pathSegments.size() - 2);
if (TextUtils.isEmpty(identifier)) {
throw new IllegalArgumentException("identifier is empty, uri: " + uri);
}
if (TextUtils.isEmpty(fileName)) {
throw new IllegalArgumentException("file name is empty, uri: " + uri);
}
//making sure the file that is trying to be fetched is in the list of stickers.
for (StickerPack stickerPack : getStickerPackList()) {
if (identifier.equals(stickerPack.identifier)) {
if (fileName.equals(stickerPack.trayImageFile)) {
return fetchFile(uri, am, fileName, identifier);
} else {
for (Sticker sticker : stickerPack.getStickers()) {
if (fileName.equals(sticker.imageFileName)) {
return fetchFile(uri, am, fileName, identifier);
}
}
}
}
}
return null;
}
private AssetFileDescriptor fetchFile(@NonNull Uri uri, @NonNull AssetManager am, @NonNull String fileName, @NonNull String identifier) {
try {
return am.openFd(identifier + "/" + fileName);
} catch (IOException e) {
Log.e(Objects.requireNonNull(getContext()).getPackageName(), "IOException when getting asset file, uri:" + uri, e);
return null;
}
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Not supported");
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
throw new UnsupportedOperationException("Not supported");
}
@Override
public int update(@NonNull Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException("Not supported");
}
}

View file

@ -0,0 +1,186 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import com.idoideas.stickermaker.ImageManipulation;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class StickerPack implements Parcelable {
Uri trayImageUri;
String identifier;
String name;
String publisher;
String trayImageFile;
final String publisherEmail;
final String publisherWebsite;
final String privacyPolicyWebsite;
final String licenseAgreementWebsite;
String iosAppStoreLink;
private List<Sticker> stickers;
private long totalSize;
String androidPlayStoreLink;
private boolean isWhitelisted;
private int stickersAddedIndex = 0;
/*public StickerPack(String identifier, String name, String publisher, String trayImageFile, String publisherEmail, String publisherWebsite, String privacyPolicyWebsite, String licenseAgreementWebsite) {
this.identifier = identifier;
this.name = name;
this.publisher = publisher;
this.trayImageFile = trayImageFile;
this.trayImageUri = Uri.parse("");
this.publisherEmail = publisherEmail;
this.publisherWebsite = publisherWebsite;
this.privacyPolicyWebsite = privacyPolicyWebsite;
this.licenseAgreementWebsite = licenseAgreementWebsite;
this.stickers = new ArrayList<>();
}*/
public StickerPack(String identifier, String name, String publisher, Uri trayImageUri, String publisherEmail, String publisherWebsite, String privacyPolicyWebsite, String licenseAgreementWebsite, Context context) {
this.identifier = identifier;
this.name = name;
this.publisher = publisher;
this.trayImageFile = "trayimage";
this.trayImageUri = ImageManipulation.convertIconTrayToWebP(trayImageUri, this.identifier, "trayImage", context);
this.publisherEmail = publisherEmail;
this.publisherWebsite = publisherWebsite;
this.privacyPolicyWebsite = privacyPolicyWebsite;
this.licenseAgreementWebsite = licenseAgreementWebsite;
this.stickers = new ArrayList<>();
}
void setIsWhitelisted(boolean isWhitelisted) {
this.isWhitelisted = isWhitelisted;
}
boolean getIsWhitelisted() {
return isWhitelisted;
}
protected StickerPack(Parcel in) {
identifier = in.readString();
name = in.readString();
publisher = in.readString();
trayImageFile = in.readString();
publisherEmail = in.readString();
publisherWebsite = in.readString();
privacyPolicyWebsite = in.readString();
licenseAgreementWebsite = in.readString();
iosAppStoreLink = in.readString();
stickers = in.createTypedArrayList(Sticker.CREATOR);
totalSize = in.readLong();
androidPlayStoreLink = in.readString();
isWhitelisted = in.readByte() != 0;
}
public static final Creator<StickerPack> CREATOR = new Creator<StickerPack>() {
@Override
public StickerPack createFromParcel(Parcel in) {
return new StickerPack(in);
}
@Override
public StickerPack[] newArray(int size) {
return new StickerPack[size];
}
};
public void addSticker(Uri uri, Context context){
String index = String.valueOf(stickersAddedIndex);
this.stickers.add(new Sticker(
index,
ImageManipulation.convertImageToWebP(uri, this.identifier, index, context),
new ArrayList<String>()));
stickersAddedIndex++;
}
public void deleteSticker(Sticker sticker){
new File(sticker.getUri().getPath()).delete();
this.stickers.remove(sticker);
}
public Sticker getSticker(int index){
return this.stickers.get(index);
}
public Sticker getStickerById(int index){
for(Sticker s : this.stickers){
if(s.getImageFileName().equals(String.valueOf(index))){
return s;
}
}
return null;
}
public void setAndroidPlayStoreLink(String androidPlayStoreLink) {
this.androidPlayStoreLink = androidPlayStoreLink;
}
public void setIosAppStoreLink(String iosAppStoreLink) {
this.iosAppStoreLink = iosAppStoreLink;
}
public List<Sticker> getStickers() {
return stickers;
}
public long getTotalSize() {
return totalSize;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(identifier);
dest.writeString(name);
dest.writeString(publisher);
dest.writeString(trayImageFile);
dest.writeString(publisherEmail);
dest.writeString(publisherWebsite);
dest.writeString(privacyPolicyWebsite);
dest.writeString(licenseAgreementWebsite);
dest.writeString(iosAppStoreLink);
dest.writeTypedList(stickers);
dest.writeLong(totalSize);
dest.writeString(androidPlayStoreLink);
dest.writeByte((byte) (isWhitelisted ? 1 : 0));
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getIdentifier() {
return this.identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public Uri getTrayImageUri() {
return trayImageUri;
}
}

View file

@ -0,0 +1,371 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;
import com.google.android.gms.ads.MobileAds;
import com.idoideas.stickermaker.BuildConfig;
import com.idoideas.stickermaker.DataArchiver;
import com.idoideas.stickermaker.R;
import com.idoideas.stickermaker.StickerBook;
import java.lang.ref.WeakReference;
import java.util.Objects;
public class StickerPackDetailsActivity extends BaseActivity {
/**
* Do not change below values of below 3 lines as this is also used by WhatsApp
*/
public static final String EXTRA_STICKER_PACK_ID = "sticker_pack_id";
public static final String EXTRA_STICKER_PACK_AUTHORITY = "sticker_pack_authority";
public static final String EXTRA_STICKER_PACK_NAME = "sticker_pack_name";
public static final int ADD_PACK = 200;
public static final String EXTRA_STICKER_PACK_WEBSITE = "sticker_pack_website";
public static final String EXTRA_STICKER_PACK_EMAIL = "sticker_pack_email";
public static final String EXTRA_STICKER_PACK_PRIVACY_POLICY = "sticker_pack_privacy_policy";
public static final String EXTRA_STICKER_PACK_TRAY_ICON = "sticker_pack_tray_icon";
public static final String EXTRA_SHOW_UP_BUTTON = "show_up_button";
public static final String EXTRA_STICKER_PACK_DATA = "sticker_pack";
private static final String TAG = "StickerPackDetails";
private RecyclerView recyclerView;
private GridLayoutManager layoutManager;
private StickerPreviewAdapter stickerPreviewAdapter;
private int numColumns;
private View addButton;
private View alreadyAddedText;
private StickerPack stickerPack;
private View divider;
private WhiteListCheckAsyncTask whiteListCheckAsyncTask;
private FrameLayout shareButton;
private FrameLayout deleteButton;
private AdView mAdView;
private InterstitialAd mInterstitialAd;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sticker_pack_details);
MobileAds.initialize(this, getString(R.string.admob_ad_id));
mAdView = findViewById(R.id.adView);
AdRequest adRequest = new AdRequest
.Builder()
.addTestDevice(getString(R.string.test_device))
.build();
mAdView.loadAd(adRequest);
mInterstitialAd = new InterstitialAd(this);
mInterstitialAd.setAdUnitId(getString(R.string.admob_fullscreen_adding_pack_unit_id));
mInterstitialAd.loadAd(new AdRequest.Builder().addTestDevice(getString(R.string.test_device)).build());
boolean showUpButton = getIntent().getBooleanExtra(EXTRA_SHOW_UP_BUTTON, false);
stickerPack = StickerBook.getStickerPackById(getIntent().getStringExtra(EXTRA_STICKER_PACK_DATA));
TextView packNameTextView = findViewById(R.id.pack_name);
TextView packPublisherTextView = findViewById(R.id.author);
ImageView packTrayIcon = findViewById(R.id.tray_image);
addButton = findViewById(R.id.add_to_whatsapp_button);
shareButton = findViewById(R.id.share_pack_button);
deleteButton = findViewById(R.id.delete_pack_button);
alreadyAddedText = findViewById(R.id.already_added_text);
layoutManager = new GridLayoutManager(this, 1);
recyclerView = findViewById(R.id.sticker_list);
recyclerView.setLayoutManager(layoutManager);
recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(pageLayoutListener);
recyclerView.addOnScrollListener(dividerScrollListener);
divider = findViewById(R.id.divider);
if (stickerPreviewAdapter == null) {
stickerPreviewAdapter = new StickerPreviewAdapter(getLayoutInflater(), R.drawable.sticker_error, getResources().getDimensionPixelSize(R.dimen.sticker_pack_details_image_size), getResources().getDimensionPixelSize(R.dimen.sticker_pack_details_image_padding), stickerPack);
recyclerView.setAdapter(stickerPreviewAdapter);
}
packNameTextView.setText(stickerPack.name);
packPublisherTextView.setText(stickerPack.publisher);
packTrayIcon.setImageURI(stickerPack.getTrayImageUri());
findViewById(R.id.add_sticker_to_pack).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openFile();
}
});
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(stickerPack.getStickers().size()>=3) {
StickerPackDetailsActivity.this.addStickerPackToWhatsApp(stickerPack);
} else {
AlertDialog alertDialog = new AlertDialog.Builder(StickerPackDetailsActivity.this)
.setNegativeButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
}).create();
alertDialog.setTitle("Invalid Action");
alertDialog.setMessage("In order to be applied to WhatsApp, the sticker pack must have at least 3 stickers. Please add more stickers first.");
alertDialog.show();
}
}
});
shareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DataArchiver.createZipFileFromStickerPack(stickerPack, StickerPackDetailsActivity.this);
}
});
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
AlertDialog alertDialog = new AlertDialog.Builder(StickerPackDetailsActivity.this)
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
StickerBook.deleteStickerPackById(stickerPack.getIdentifier());
finish();
Intent intent = new Intent(StickerPackDetailsActivity.this, StickerPackListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
Toast.makeText(StickerPackDetailsActivity.this, "Sticker Pack deleted", Toast.LENGTH_SHORT).show();
}
}).create();
alertDialog.setTitle("Are you sure?");
alertDialog.setMessage("Deleting this package will also remove it from your WhatsApp app.");
alertDialog.show();
}
});
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(showUpButton);
getSupportActionBar().setTitle(showUpButton ? R.string.title_activity_sticker_pack_details_multiple_pack : R.string.title_activity_sticker_pack_details_single_pack);
}
}
private void launchInfoActivity(String publisherWebsite, String publisherEmail, String privacyPolicyWebsite, String trayIconUriString) {
Intent intent = new Intent(StickerPackDetailsActivity.this, StickerPackInfoActivity.class);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_ID, stickerPack.identifier);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_WEBSITE, publisherWebsite);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_EMAIL, publisherEmail);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_PRIVACY_POLICY, privacyPolicyWebsite);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_TRAY_ICON, stickerPack.getTrayImageUri().toString());
startActivity(intent);
}
private void openFile() {
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT);
i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
i.setType("image/*");
startActivityForResult(i, 3000);
}
@Override
public void onBackPressed() {
super.onBackPressed();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_info && stickerPack != null) {
final String publisherWebsite = stickerPack.publisherWebsite;
final String publisherEmail = stickerPack.publisherEmail;
final String privacyPolicyWebsite = stickerPack.privacyPolicyWebsite;
Uri trayIconUri = stickerPack.getTrayImageUri();
launchInfoActivity(publisherWebsite, publisherEmail, privacyPolicyWebsite, trayIconUri.toString());
return true;
}
return super.onOptionsItemSelected(item);
}
private void addStickerPackToWhatsApp(StickerPack sp) {
Intent intent = new Intent();
intent.setAction("com.whatsapp.intent.action.ENABLE_STICKER_PACK");
Log.w("IS IT A NEW IDENTIFIER?", sp.getIdentifier());
intent.putExtra(EXTRA_STICKER_PACK_ID, sp.getIdentifier());
intent.putExtra(EXTRA_STICKER_PACK_AUTHORITY, BuildConfig.CONTENT_PROVIDER_AUTHORITY);
intent.putExtra(EXTRA_STICKER_PACK_NAME, sp.getName());
try {
startActivityForResult(intent, 200);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, R.string.error_adding_sticker_pack, Toast.LENGTH_LONG).show();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ADD_PACK) {
if (resultCode == Activity.RESULT_CANCELED && data != null) {
final String validationError = data.getStringExtra("validation_error");
if (validationError != null) {
if (BuildConfig.DEBUG) {
//validation error should be shown to developer only, not users.
MessageDialogFragment.newInstance(R.string.title_validation_error, validationError).show(getSupportFragmentManager(), "validation error");
}
Log.e(TAG, "Validation failed:" + validationError);
}
} else {
if (mInterstitialAd.isLoaded()) {
mInterstitialAd.show();
mInterstitialAd = new InterstitialAd(this);
mInterstitialAd.setAdUnitId(getString(R.string.admob_fullscreen_adding_pack_unit_id));
mInterstitialAd.loadAd(new AdRequest.Builder().addTestDevice(getString(R.string.test_device)).build());
}
}
} else if(requestCode == 3000){
if(data!=null){
if(data.getClipData()!=null){
ClipData clipData = data.getClipData();
for(int i = 0; i < clipData.getItemCount(); i++)
{
ClipData.Item path = clipData.getItemAt(i);
Uri uri = path.getUri();
getContentResolver().takePersistableUriPermission(Objects.requireNonNull(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION);
stickerPack.addSticker(uri, this);
}
} else {
Uri uri = data.getData();
getContentResolver().takePersistableUriPermission(Objects.requireNonNull(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION);
stickerPack.addSticker(uri, this);
}
finish();
startActivity(getIntent());
}
}
}
private final ViewTreeObserver.OnGlobalLayoutListener pageLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
setNumColumns(recyclerView.getWidth() / recyclerView.getContext().getResources().getDimensionPixelSize(R.dimen.sticker_pack_details_image_size));
}
};
private void setNumColumns(int numColumns) {
if (this.numColumns != numColumns) {
layoutManager.setSpanCount(numColumns);
this.numColumns = numColumns;
if (stickerPreviewAdapter != null) {
stickerPreviewAdapter.notifyDataSetChanged();
}
}
}
private final RecyclerView.OnScrollListener dividerScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) {
super.onScrollStateChanged(recyclerView, newState);
updateDivider(recyclerView);
}
@Override
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
super.onScrolled(recyclerView, dx, dy);
updateDivider(recyclerView);
}
private void updateDivider(RecyclerView recyclerView) {
boolean showDivider = recyclerView.computeVerticalScrollOffset() > 0;
if (divider != null) {
divider.setVisibility(showDivider ? View.VISIBLE : View.INVISIBLE);
}
}
};
@Override
protected void onResume() {
super.onResume();
whiteListCheckAsyncTask = new WhiteListCheckAsyncTask(this);
whiteListCheckAsyncTask.execute(stickerPack);
}
@Override
protected void onPause() {
super.onPause();
if (whiteListCheckAsyncTask != null && !whiteListCheckAsyncTask.isCancelled()) {
whiteListCheckAsyncTask.cancel(true);
}
}
private void updateAddUI(Boolean isWhitelisted) {
if (isWhitelisted) {
addButton.setVisibility(View.GONE);
alreadyAddedText.setVisibility(View.VISIBLE);
} else {
addButton.setVisibility(View.VISIBLE);
alreadyAddedText.setVisibility(View.GONE);
}
}
static class WhiteListCheckAsyncTask extends AsyncTask<StickerPack, Void, Boolean> {
private final WeakReference<StickerPackDetailsActivity> stickerPackDetailsActivityWeakReference;
WhiteListCheckAsyncTask(StickerPackDetailsActivity stickerPackListActivity) {
this.stickerPackDetailsActivityWeakReference = new WeakReference<>(stickerPackListActivity);
}
@Override
protected final Boolean doInBackground(StickerPack... stickerPacks) {
StickerPack stickerPack = stickerPacks[0];
final StickerPackDetailsActivity stickerPackDetailsActivity = stickerPackDetailsActivityWeakReference.get();
//noinspection SimplifiableIfStatement
if (stickerPackDetailsActivity == null) {
return false;
}
return WhitelistCheck.isWhitelisted(stickerPackDetailsActivity, stickerPack.identifier);
}
@Override
protected void onPostExecute(Boolean isWhitelisted) {
final StickerPackDetailsActivity stickerPackDetailsActivity = stickerPackDetailsActivityWeakReference.get();
if (stickerPackDetailsActivity != null) {
stickerPackDetailsActivity.updateAddUI(isWhitelisted);
}
}
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import com.idoideas.stickermaker.R;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class StickerPackInfoActivity extends BaseActivity {
private static final String TAG = "StickerPackInfoActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sticker_pack_info);
final String trayIconUriString = getIntent().getStringExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_TRAY_ICON);
final String website = getIntent().getStringExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_WEBSITE);
final String email = getIntent().getStringExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_EMAIL);
final String privacyPolicy = getIntent().getStringExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_PRIVACY_POLICY);
final TextView trayIcon = findViewById(R.id.tray_icon);
try {
final InputStream inputStream = getContentResolver().openInputStream(Uri.parse(trayIconUriString));
final BitmapDrawable trayDrawable = new BitmapDrawable(inputStream);
final Drawable emailDrawable = getResources().getDrawable(R.drawable.sticker_3rdparty_email);
trayDrawable.setBounds(new Rect(0, 0, emailDrawable.getIntrinsicWidth(), emailDrawable.getIntrinsicHeight()));
trayIcon.setCompoundDrawables(trayDrawable, null, null, null);
} catch (FileNotFoundException e) {
Log.e(TAG, "could not find the uri for the tray image:" + trayIconUriString);
}
final TextView viewWebpage = findViewById(R.id.view_webpage);
if (TextUtils.isEmpty(website)) {
viewWebpage.setVisibility(View.GONE);
} else {
viewWebpage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StickerPackInfoActivity.this.launchWebpage(website);
}
});
}
final TextView sendEmail = findViewById(R.id.send_email);
if (TextUtils.isEmpty(email)) {
sendEmail.setVisibility(View.GONE);
} else {
sendEmail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StickerPackInfoActivity.this.launchEmailClient(email);
}
});
}
final TextView viewPrivacyPolicy = findViewById(R.id.privacy_policy);
if (TextUtils.isEmpty(privacyPolicy)) {
viewPrivacyPolicy.setVisibility(View.GONE);
} else {
viewPrivacyPolicy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StickerPackInfoActivity.this.launchWebpage(privacyPolicy);
}
});
}
}
private void launchEmailClient(String email) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("plain/text");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
startActivity(Intent.createChooser(intent, "Send email with"));
}
private void launchWebpage(String website) {
Uri uri = Uri.parse(website);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
}

View file

@ -0,0 +1,586 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.billingclient.api.BillingClient;
import com.android.billingclient.api.BillingClientStateListener;
import com.android.billingclient.api.BillingFlowParams;
import com.android.billingclient.api.ConsumeResponseListener;
import com.android.billingclient.api.Purchase;
import com.android.billingclient.api.PurchasesUpdatedListener;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.soloader.SoLoader;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;
import com.google.android.gms.ads.MobileAds;
import com.idoideas.stickermaker.BuildConfig;
import com.idoideas.stickermaker.DataArchiver;
import com.idoideas.stickermaker.NewUserIntroActivity;
import com.idoideas.stickermaker.R;
import com.idoideas.stickermaker.StickerBook;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import co.mobiwise.materialintro.shape.Focus;
import co.mobiwise.materialintro.shape.FocusGravity;
import co.mobiwise.materialintro.shape.ShapeType;
import co.mobiwise.materialintro.view.MaterialIntroView;
import static com.idoideas.stickermaker.NewUserIntroActivity.verifyStoragePermissions;
public class StickerPackListActivity extends BaseActivity {
public static final String EXTRA_STICKER_PACK_LIST_DATA = "sticker_pack_list";
private static final int STICKER_PREVIEW_DISPLAY_LIMIT = 5;
private static final String TAG = "StickerPackList";
private LinearLayoutManager packLayoutManager;
private static RecyclerView packRecyclerView;
private static StickerPackListAdapter allStickerPacksListAdapter;
WhiteListCheckAsyncTask whiteListCheckAsyncTask;
ArrayList<StickerPack> stickerPackList;
public static Context context;
public static String newName, newCreator;
private AdView mAdView;
private InterstitialAd mInterstitialAd;
private BillingClient mBillingClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sticker_pack_list);
MobileAds.initialize(this, getString(R.string.admob_ad_id));
mAdView = findViewById(R.id.adView);
AdRequest adRequest = new AdRequest
.Builder()
.addTestDevice(getString(R.string.test_device))
.build();
mAdView.loadAd(adRequest);
mInterstitialAd = new InterstitialAd(this);
mInterstitialAd.setAdUnitId(getString(R.string.admob_fullscreen_adding_pack_unit_id));
mInterstitialAd.loadAd(new AdRequest.Builder().addTestDevice(getString(R.string.test_device)).build());
StickerBook.init(this);
Fresco.initialize(this);
context = getApplicationContext();
SoLoader.init(this, /* native exopackage */ false);
mBillingClient = BillingClient.newBuilder(this).setListener(new PurchasesUpdatedListener() {
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases) {
}
}).build();
mBillingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(@BillingClient.BillingResponse int billingResponseCode) {
if (billingResponseCode == BillingClient.BillingResponse.OK) {
// The billing client is ready. You can query purchases here.
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
packRecyclerView = findViewById(R.id.sticker_pack_list);
stickerPackList = StickerBook.getAllStickerPacks();//getIntent().getParcelableArrayListExtra( EXTRA_STICKER_PACK_LIST_DATA);
showStickerPackList(stickerPackList);
if (Intent.ACTION_SEND.equals(getIntent().getAction())) {
Bundle extras = getIntent().getExtras();
if (extras.containsKey(Intent.EXTRA_STREAM)) {
Uri uri = (Uri) extras.getParcelable(Intent.EXTRA_STREAM);
if(uri!=null){
DataArchiver.importZipFileToStickerPack(uri, StickerPackListActivity.this);
}
}
}
if(toShowIntro()){
startActivityForResult(new Intent(this, NewUserIntroActivity.class), 1114);
}
}
@Override
protected void onResume() {
super.onResume();
String action = getIntent().getAction();
if(action == null) {
Log.v("Example", "Force restart");
Intent intent = new Intent(this, StickerPackListActivity.class);
intent.setAction("Already created");
startActivity(intent);
finish();
}
whiteListCheckAsyncTask = new WhiteListCheckAsyncTask(this);
//noinspection unchecked
whiteListCheckAsyncTask.execute(stickerPackList);
}
@Override
protected void onPause() {
super.onPause();
DataArchiver.writeStickerBookJSON(StickerBook.getAllStickerPacks(), this);
if (whiteListCheckAsyncTask != null && !whiteListCheckAsyncTask.isCancelled()) {
whiteListCheckAsyncTask.cancel(true);
}
}
@Override
protected void onDestroy() {
DataArchiver.writeStickerBookJSON(StickerBook.getAllStickerPacks(), this);
super.onDestroy();
}
public void showStickerPackList(List<StickerPack> stickerPackList) {
allStickerPacksListAdapter = new StickerPackListAdapter(stickerPackList, onAddButtonClickedListener);
packRecyclerView.setAdapter(allStickerPacksListAdapter);
packLayoutManager = new LinearLayoutManager(this);
packLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
packRecyclerView.getContext(),
packLayoutManager.getOrientation()
);
packRecyclerView.addItemDecoration(dividerItemDecoration);
packRecyclerView.setLayoutManager(packLayoutManager);
packRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(this::recalculateColumnCount);
}
private StickerPackListAdapter.OnAddButtonClickedListener onAddButtonClickedListener = new StickerPackListAdapter.OnAddButtonClickedListener() {
@Override
public void onAddButtonClicked(StickerPack pack) {
if(pack.getStickers().size()>=3) {
Intent intent = new Intent();
intent.setAction("com.whatsapp.intent.action.ENABLE_STICKER_PACK");
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_ID, pack.identifier);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_AUTHORITY, BuildConfig.CONTENT_PROVIDER_AUTHORITY);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_NAME, pack.name);
try {
StickerPackListActivity.this.startActivityForResult(intent, StickerPackDetailsActivity.ADD_PACK);
} catch (ActivityNotFoundException e) {
Toast.makeText(StickerPackListActivity.this, R.string.error_adding_sticker_pack, Toast.LENGTH_LONG).show();
}
} else {
AlertDialog alertDialog = new AlertDialog.Builder(StickerPackListActivity.this)
.setNegativeButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
}).create();
alertDialog.setTitle("Invalid Action");
alertDialog.setMessage("In order to be applied to WhatsApp, the sticker pack must have at least 3 stickers. Please add more stickers first.");
alertDialog.show();
}
}
};
private void recalculateColumnCount() {
final int previewSize = getResources().getDimensionPixelSize(R.dimen.sticker_pack_list_item_preview_image_size);
int firstVisibleItemPosition = packLayoutManager.findFirstVisibleItemPosition();
StickerPackListItemViewHolder viewHolder = (StickerPackListItemViewHolder) packRecyclerView.findViewHolderForAdapterPosition(firstVisibleItemPosition);
if (viewHolder != null) {
final int max = Math.max(viewHolder.imageRowView.getMeasuredWidth() / previewSize, 1);
int numColumns = Math.min(STICKER_PREVIEW_DISPLAY_LIMIT, max);
allStickerPacksListAdapter.setMaxNumberOfStickersInARow(numColumns);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == StickerPackDetailsActivity.ADD_PACK) {
if (resultCode == Activity.RESULT_CANCELED && data != null) {
final String validationError = data.getStringExtra("validation_error");
if (validationError != null) {
if (BuildConfig.DEBUG) {
//validation error should be shown to developer only, not users.
MessageDialogFragment.newInstance(R.string.title_validation_error, validationError).show(getSupportFragmentManager(), "validation error");
}
Log.e(TAG, "Validation failed:" + validationError);
}
} else {
if (mInterstitialAd.isLoaded()) {
mInterstitialAd.show();
mInterstitialAd = new InterstitialAd(this);
mInterstitialAd.setAdUnitId(getString(R.string.admob_fullscreen_adding_pack_unit_id));
mInterstitialAd.loadAd(new AdRequest.Builder().addTestDevice(getString(R.string.test_device)).build());
}
}
} else if (data!=null && requestCode==2319){
Uri uri = data.getData();
getContentResolver().takePersistableUriPermission(Objects.requireNonNull(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION);
createNewStickerPackAndOpenIt(newName, newCreator, uri);
} else if(requestCode == 1114){
makeIntroNotRunAgain();
new MaterialIntroView.Builder(this)
.enableIcon(false)
.setFocusGravity(FocusGravity.CENTER)
.setFocusType(Focus.MINIMUM)
.setDelayMillis(500)
.enableFadeAnimation(true)
.performClick(true)
.setInfoText("To add new sticker packs, click here.")
.setShape(ShapeType.CIRCLE)
.setTarget(findViewById(R.id.action_add))
.setUsageId("intro_card") //THIS SHOULD BE UNIQUE ID
.show();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.toolbar_main, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_add) {
addNewStickerPackInInterface();
return true;
} else if(item.getItemId() == R.id.action_info){
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
LayoutInflater inflater = this.getLayoutInflater();
View dialogView = inflater.inflate(R.layout.about_layout, null);
dialogView.findViewById(R.id.redditlogo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.reddit.com/u/idoideas"));
startActivity(browserIntent);
}
});
dialogView.findViewById(R.id.twitterlogo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.twitter.com/idoideas"));
startActivity(browserIntent);
}
});
dialogView.findViewById(R.id.githublogo).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.github.com/idoideas"));
startActivity(browserIntent);
}
});
dialogBuilder.setView(dialogView);
AlertDialog alertDialog = dialogBuilder.create();
alertDialog.show();
} else if (item.getItemId() == R.id.action_donate){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Thank you for donation!");
builder.setMessage("We appreciate your support in great apps and open-source projects.\n\nHow much would you like to donate?");
builder.setPositiveButton("A Sandwich - 5$",
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialog.cancel();
startInAppPurchase("5_dollar_donation");
}
});
builder.setNeutralButton("A Piece of Gum - 1$",
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialog.cancel();
startInAppPurchase("1_dollar_donation");
}
});
builder.setNegativeButton("A Coffee - 3$",
new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int id)
{
dialog.cancel();
startInAppPurchase("3_dollar_donation");
}
});
builder.create().show();
}
return super.onOptionsItemSelected(item);
}
static class WhiteListCheckAsyncTask extends AsyncTask<List<StickerPack>, Void, List<StickerPack>> {
private final WeakReference<StickerPackListActivity> stickerPackListActivityWeakReference;
WhiteListCheckAsyncTask(StickerPackListActivity stickerPackListActivity) {
this.stickerPackListActivityWeakReference = new WeakReference<>(stickerPackListActivity);
}
@SafeVarargs
@Override
protected final List<StickerPack> doInBackground(List<StickerPack>... lists) {
List<StickerPack> stickerPackList = lists[0];
final StickerPackListActivity stickerPackListActivity = stickerPackListActivityWeakReference.get();
if (stickerPackListActivity == null) {
return stickerPackList;
}
for (StickerPack stickerPack : stickerPackList) {
stickerPack.setIsWhitelisted(WhitelistCheck.isWhitelisted(stickerPackListActivity, stickerPack.identifier));
}
return stickerPackList;
}
@Override
protected void onPostExecute(List<StickerPack> stickerPackList) {
final StickerPackListActivity stickerPackListActivity = stickerPackListActivityWeakReference.get();
if (stickerPackListActivity != null) {
stickerPackListActivity.allStickerPacksListAdapter.notifyDataSetChanged();
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
AlertDialog alertDialog = new AlertDialog.Builder(this)
.setPositiveButton("Let's Go", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
verifyStoragePermissions(StickerPackListActivity.this);
}
})
.create();
alertDialog.setTitle("Notice!");
alertDialog.setMessage("We've recognized you denied the storage access permission for this app."
+ "\n\nIn order for this app to work, storage access is required.");
alertDialog.show();
}
break;
}
}
private void addNewStickerPackInInterface(){
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("Create New Sticker Pack");
dialog.setMessage("Please specify title and creator for the pack.");
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
final EditText nameBox = new EditText(this);
nameBox.setLines(1);
LinearLayout.LayoutParams buttonLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
buttonLayoutParams.setMargins(50, 0, 50, 10);
nameBox.setLayoutParams(buttonLayoutParams);
nameBox.setHint("Pack Name");
nameBox.setInputType(InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
layout.addView(nameBox);
final EditText creatorBox = new EditText(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
creatorBox.setAutofillHints("name");
}
creatorBox.setLines(1);
creatorBox.setLayoutParams(buttonLayoutParams);
creatorBox.setInputType(InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
creatorBox.setHint("Creator");
layout.addView(creatorBox);
dialog.setView(layout);
dialog.setPositiveButton("OK", null);
dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
final AlertDialog ad = dialog.create();
ad.show();
Button b = ad.getButton(AlertDialog.BUTTON_POSITIVE);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if(TextUtils.isEmpty(nameBox.getText())){
nameBox.setError("Package name is required!");
}
if(TextUtils.isEmpty(creatorBox.getText())){
creatorBox.setError("Creator is required!");
}
if(!TextUtils.isEmpty(nameBox.getText()) && !TextUtils.isEmpty(creatorBox.getText())) {
ad.dismiss();
createDialogForPickingIconImage(nameBox, creatorBox);
}
}
});
creatorBox.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
b.performClick();
}
return false;
}
});
}
private void createDialogForPickingIconImage(EditText nameBox, EditText creatorBox){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Pick your pack's icon image");
builder.setMessage("Now you will pick the new sticker pack's icon image.")
.setCancelable(false)
.setPositiveButton("Let's go", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
openFileTray(nameBox.getText().toString(), creatorBox.getText().toString());
}
});
AlertDialog alert = builder.create();
alert.show();
}
private void createNewStickerPackAndOpenIt(String name, String creator, Uri trayImage){
String newId = UUID.randomUUID().toString();
StickerPack sp = new StickerPack(
newId,
name,
creator,
trayImage,
"",
"",
"",
"",
this);
StickerBook.addStickerPackExisting(sp);
Intent intent = new Intent(this, StickerPackDetailsActivity.class);
intent.putExtra(StickerPackDetailsActivity.EXTRA_SHOW_UP_BUTTON, true);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_DATA, newId);
intent.putExtra("isNewlyCreated", true);
this.startActivity(intent);
}
private void openFileTray(String name, String creator) {
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT);
i.setType("image/*");
newName = name;
newCreator = creator;
startActivityForResult(i, 2319);
}
private void makeIntroNotRunAgain(){
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
boolean previouslyStarted = prefs.getBoolean("isAlreadyShown", false);
if(!previouslyStarted) {
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("isAlreadyShown", false);
edit.commit();
}
}
private boolean toShowIntro(){
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
return prefs.getBoolean("isAlreadyShown", true);
}
private void startInAppPurchase(String sku){
mBillingClient.consumeAsync("", new ConsumeResponseListener() {
@Override
public void onConsumeResponse(int responseCode, String purchaseToken) {
}
});
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSku(sku)
.setType(BillingClient.SkuType.INAPP)
.build();
mBillingClient.launchBillingFlow(StickerPackListActivity.this, flowParams);
Purchase.PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
ConsumeResponseListener listener = new ConsumeResponseListener() {
@Override
public void onConsumeResponse(@BillingClient.BillingResponse int responseCode, String outToken) {
if (responseCode == BillingClient.BillingResponse.OK) {
Toast.makeText(getApplicationContext(), "Thank you so much for your donation!", Toast.LENGTH_LONG).show();
}
}};
if (purchasesResult!=null){
if(purchasesResult.getPurchasesList()!=null){
if(purchasesResult.getPurchasesList().size()>0){
for (int i = 0; i<purchasesResult.getPurchasesList().size(); i++){
mBillingClient.consumeAsync(purchasesResult.getPurchasesList().get(i).getPurchaseToken(), listener);
}
}
}
}
}
}

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.facebook.drawee.view.SimpleDraweeView;
import com.idoideas.stickermaker.DataArchiver;
import com.idoideas.stickermaker.R;
import java.util.List;
public class StickerPackListAdapter extends RecyclerView.Adapter<StickerPackListItemViewHolder> {
@NonNull
private List<StickerPack> stickerPacks;
@NonNull
private final OnAddButtonClickedListener onAddButtonClickedListener;
private int maxNumberOfStickersInARow;
StickerPackListAdapter(@NonNull List<StickerPack> stickerPacks, @NonNull OnAddButtonClickedListener onAddButtonClickedListener) {
this.stickerPacks = stickerPacks;
this.onAddButtonClickedListener = onAddButtonClickedListener;
}
@NonNull
@Override
public StickerPackListItemViewHolder onCreateViewHolder(@NonNull final ViewGroup viewGroup, final int i) {
final Context context = viewGroup.getContext();
final LayoutInflater layoutInflater = LayoutInflater.from(context);
final View stickerPackRow = layoutInflater.inflate(R.layout.sticker_packs_list_item, viewGroup, false);
return new StickerPackListItemViewHolder(stickerPackRow);
}
@Override
public void onBindViewHolder(@NonNull final StickerPackListItemViewHolder viewHolder, final int index) {
StickerPack pack = stickerPacks.get(index);
final Context context = viewHolder.publisherView.getContext();
viewHolder.publisherView.setText("by " + pack.publisher);
//viewHolder.filesizeView.setText(Formatter.formatShortFileSize(context, pack.getTotalSize()));
viewHolder.titleView.setText(pack.name);
viewHolder.container.setOnClickListener(view -> {
Intent intent = new Intent(view.getContext(), StickerPackDetailsActivity.class);
intent.putExtra(StickerPackDetailsActivity.EXTRA_SHOW_UP_BUTTON, true);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_DATA, pack.identifier);
view.getContext().startActivity(intent);
});
viewHolder.imageRowView.removeAllViews();
//if this sticker pack contains less stickers than the max, then take the smaller size.
int actualNumberOfStickersToShow = Math.min(maxNumberOfStickersInARow, pack.getStickers().size());
for (int i = 0; i < actualNumberOfStickersToShow; i++) {
final SimpleDraweeView rowImage = (SimpleDraweeView) LayoutInflater.from(context).inflate(R.layout.sticker_pack_list_item_image, viewHolder.imageRowView, false);
rowImage.setImageURI(pack.getSticker(i).getUri());
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) rowImage.getLayoutParams();
final int marginBetweenImages = (viewHolder.imageRowView.getMeasuredWidth() - maxNumberOfStickersInARow * viewHolder.imageRowView.getContext().getResources().getDimensionPixelSize(R.dimen.sticker_pack_list_item_preview_image_size)) / (maxNumberOfStickersInARow - 1) - lp.leftMargin - lp.rightMargin;
if (i != actualNumberOfStickersToShow - 1 && marginBetweenImages > 0) { //do not set the margin for the last image
lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin + marginBetweenImages, lp.bottomMargin);
rowImage.setLayoutParams(lp);
}
viewHolder.imageRowView.addView(rowImage);
}
setAddButtonAppearance(viewHolder.addButton, pack);
viewHolder.shareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DataArchiver.createZipFileFromStickerPack(pack, context);
}
});
}
private void setAddButtonAppearance(ImageView addButton, StickerPack pack) {
if (pack.getIsWhitelisted()) {
addButton.setImageResource(R.drawable.sticker_3rdparty_added);
addButton.setClickable(false);
addButton.setOnClickListener(null);
addButton.setBackgroundDrawable(null);
} else {
addButton.setImageResource(android.R.drawable.stat_sys_download);
addButton.setOnClickListener(v -> onAddButtonClickedListener.onAddButtonClicked(pack));
TypedValue outValue = new TypedValue();
addButton.getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
addButton.setBackgroundResource(outValue.resourceId);
}
}
@Override
public int getItemCount() {
return stickerPacks.size();
}
void setMaxNumberOfStickersInARow(int maxNumberOfStickersInARow) {
if (this.maxNumberOfStickersInARow != maxNumberOfStickersInARow) {
this.maxNumberOfStickersInARow = maxNumberOfStickersInARow;
notifyDataSetChanged();
}
}
public interface OnAddButtonClickedListener {
void onAddButtonClicked(StickerPack stickerPack);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.idoideas.stickermaker.R;
public class StickerPackListItemViewHolder extends RecyclerView.ViewHolder {
View container;
TextView titleView;
TextView publisherView;
//TextView filesizeView;
ImageView addButton;
ImageView shareButton;
LinearLayout imageRowView;
StickerPackListItemViewHolder(final View itemView) {
super(itemView);
container = itemView;
titleView = itemView.findViewById(R.id.sticker_pack_title);
publisherView = itemView.findViewById(R.id.sticker_pack_publisher);
//filesizeView = itemView.findViewById(R.id.sticker_pack_filesize);
addButton = itemView.findViewById(R.id.add_button_on_list);
shareButton = itemView.findViewById(R.id.export_button_on_list);
imageRowView = itemView.findViewById(R.id.sticker_packs_list_item_image_list);
}
}

View file

@ -0,0 +1,154 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.facebook.animated.webp.WebPImage;
import com.idoideas.stickermaker.BuildConfig;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class StickerPackValidator {
private static final int STICKER_FILE_SIZE_LIMIT_KB = 100;
private static final int EMOJI_LIMIT = 3;
private static final int IMAGE_HEIGHT = 512;
private static final int IMAGE_WIDTH = 512;
private static final int STICKER_SIZE_MIN = 3;
private static final int STICKER_SIZE_MAX = 30;
private static final int CHAR_COUNT_MAX = 128;
private static final long ONE_KIBIBYTE = 8 * 1024;
private static final int TRAY_IMAGE_FILE_SIZE_MAX_KB = 50;
private static final int TRAY_IMAGE_DIMENSION_MIN = 24;
private static final int TRAY_IMAGE_DIMENSION_MAX = 512;
/**
* Checks whether a sticker pack contains valid data
*/
public static void verifyStickerPackValidity(@NonNull Context context, @NonNull StickerPack stickerPack) throws IllegalStateException {
if (TextUtils.isEmpty(stickerPack.identifier)) {
throw new IllegalStateException("sticker pack identifier is empty");
}
if (stickerPack.identifier.length() > CHAR_COUNT_MAX) {
throw new IllegalStateException("sticker pack identifier cannot exceed " + CHAR_COUNT_MAX + " characters");
}
checkStringValidity(stickerPack.identifier);
if (TextUtils.isEmpty(stickerPack.publisher)) {
throw new IllegalStateException("sticker pack publisher is empty, sticker pack identifier:" + stickerPack.identifier);
}
if (stickerPack.publisher.length() > CHAR_COUNT_MAX) {
throw new IllegalStateException("sticker pack publisher cannot exceed " + CHAR_COUNT_MAX + " characters, sticker pack identifier:" + stickerPack.identifier);
}
if (TextUtils.isEmpty(stickerPack.name)) {
throw new IllegalStateException("sticker pack name is empty, sticker pack identifier:" + stickerPack.identifier);
}
if (stickerPack.name.length() > CHAR_COUNT_MAX) {
throw new IllegalStateException("sticker pack name cannot exceed " + CHAR_COUNT_MAX + " characters, sticker pack identifier:" + stickerPack.identifier);
}
if (TextUtils.isEmpty(stickerPack.trayImageFile)) {
throw new IllegalStateException("sticker pack tray id is empty, sticker pack identifier:" + stickerPack.identifier);
}
try {
final byte[] bytes = fetchStickerAsset(stickerPack.identifier, stickerPack.trayImageFile, context.getContentResolver());
if (bytes.length > TRAY_IMAGE_FILE_SIZE_MAX_KB * ONE_KIBIBYTE) {
throw new IllegalStateException("tray image should be less than " + TRAY_IMAGE_FILE_SIZE_MAX_KB + " KB, tray image file: " + stickerPack.trayImageFile);
}
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bitmap.getHeight() > TRAY_IMAGE_DIMENSION_MAX || bitmap.getHeight() < TRAY_IMAGE_DIMENSION_MIN) {
throw new IllegalStateException("tray image height should between " + TRAY_IMAGE_DIMENSION_MIN + " and " + TRAY_IMAGE_DIMENSION_MAX + " pixels, current tray image height is " + bitmap.getHeight() + ", tray image file: " + stickerPack.trayImageFile);
}
if (bitmap.getWidth() > TRAY_IMAGE_DIMENSION_MAX || bitmap.getWidth() < TRAY_IMAGE_DIMENSION_MIN) {
throw new IllegalStateException("tray image width should be between " + TRAY_IMAGE_DIMENSION_MIN + " and " + TRAY_IMAGE_DIMENSION_MAX + " pixels, current tray image width is " + bitmap.getWidth() + ", tray image file: " + stickerPack.trayImageFile);
}
} catch (IOException e) {
throw new IllegalStateException("Cannot open tray image, " + stickerPack.trayImageFile, e);
}
final List<Sticker> stickers = stickerPack.getStickers();
if (stickers.size() < STICKER_SIZE_MIN || stickers.size() > STICKER_SIZE_MAX) {
throw new IllegalStateException("sticker pack sticker count should be between 3 to 30 inclusive, it currently has " + stickers.size() + ", sticker pack identifier:" + stickerPack.identifier);
}
for (final Sticker sticker : stickers) {
validateSticker(context, stickerPack.identifier, sticker);
}
}
private static void validateSticker(@NonNull Context context, @NonNull final String identifier, @NonNull final Sticker sticker) throws IllegalStateException {
if (sticker.emojis.size() > EMOJI_LIMIT) {
throw new IllegalStateException("emoji count exceed limit, sticker pack identifier:" + identifier + ", filename:" + sticker.imageFileName);
}
if (TextUtils.isEmpty(sticker.imageFileName)) {
throw new IllegalStateException("no file path for sticker, sticker pack identifier:" + identifier);
}
validateStickerFile(context, identifier, sticker.imageFileName);
}
private static void validateStickerFile(@NonNull Context context, @NonNull String identifier, @NonNull final String fileName) throws IllegalStateException {
try {
final byte[] bytes = fetchStickerAsset(identifier, fileName, context.getContentResolver());
if (bytes.length > STICKER_FILE_SIZE_LIMIT_KB * ONE_KIBIBYTE) {
throw new IllegalStateException("sticker should be less than " + STICKER_FILE_SIZE_LIMIT_KB + "KB, sticker pack identifier:" + identifier + ", filename:" + fileName);
}
try {
final WebPImage webPImage = WebPImage.create(bytes);
if (webPImage.getHeight() != IMAGE_HEIGHT) {
throw new IllegalStateException("sticker height should be " + IMAGE_HEIGHT + ", sticker pack identifier:" + identifier + ", filename:" + fileName);
}
if (webPImage.getWidth() != IMAGE_WIDTH) {
throw new IllegalStateException("sticker width should be " + IMAGE_WIDTH + ", sticker pack identifier:" + identifier + ", filename:" + fileName);
}
if (webPImage.getFrameCount() > 1) {
throw new IllegalStateException("sticker shoud be a static image, no animated sticker support at the moment, sticker pack identifier:" + identifier + ", filename:" + fileName);
}
} catch (IllegalArgumentException e) {
throw new IllegalStateException("Error parsing webp image, sticker pack identifier:" + identifier + ", filename:" + fileName, e);
}
} catch (IOException e) {
throw new IllegalStateException("cannot open sticker file: sticker pack identifier:" + identifier + ", filename:" + fileName, e);
}
}
private static void checkStringValidity(@NonNull String string) {
String pattern = "[\\w-.,'\\s]+"; // [a-zA-Z0-9_-.' ]
if (!string.matches(pattern)) {
throw new IllegalStateException(string + " contains invalid characters, allowed characters are a to z, A to Z, _ , ' - . and space character");
}
if (string.contains("..")) {
throw new IllegalStateException(string + " cannot contain ..");
}
}
private static byte[] fetchStickerAsset(@NonNull final String identifier, @NonNull final String name, ContentResolver contentResolver) throws IOException {
try (final InputStream inputStream = contentResolver.openInputStream(getStickerAssetUri(identifier, name));
final ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
if (inputStream == null) {
throw new IOException("cannot read sticker asset:" + identifier + "/" + name);
}
int read;
byte[] data = new byte[16384];
while ((read = inputStream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, read);
}
return buffer.toByteArray();
}
}
private static Uri getStickerAssetUri(String identifier, String stickerName) {
return new Uri.Builder().scheme(StickerContentProvider.CONTENT_SCHEME).authority(BuildConfig.CONTENT_PROVIDER_AUTHORITY).appendPath(StickerContentProvider.STICKERS_ASSET).appendPath("2").appendPath(stickerName).build();
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.Toast;
import com.idoideas.stickermaker.R;
public class StickerPreviewAdapter extends RecyclerView.Adapter<StickerPreviewViewHolder> {
@NonNull
private StickerPack stickerPack;
private final int cellSize;
private int cellLimit;
private int cellPadding;
private final int errorResource;
private final LayoutInflater layoutInflater;
StickerPreviewAdapter(
@NonNull final LayoutInflater layoutInflater,
final int errorResource,
final int cellSize,
final int cellPadding,
@NonNull final StickerPack stickerPack) {
this.cellSize = cellSize;
this.cellPadding = cellPadding;
this.cellLimit = 0;
this.layoutInflater = layoutInflater;
this.errorResource = errorResource;
this.stickerPack = stickerPack;
}
@NonNull
@Override
public StickerPreviewViewHolder onCreateViewHolder(@NonNull final ViewGroup viewGroup, final int i) {
View itemView = layoutInflater.inflate(R.layout.sticker_image, viewGroup, false);
StickerPreviewViewHolder vh = new StickerPreviewViewHolder(itemView);
ViewGroup.LayoutParams layoutParams = vh.stickerPreviewView.getLayoutParams();
layoutParams.height = cellSize;
layoutParams.width = cellSize;
vh.stickerPreviewView.setLayoutParams(layoutParams);
vh.stickerPreviewView.setPadding(cellPadding, cellPadding, cellPadding, cellPadding);
return vh;
}
@Override
public void onBindViewHolder(@NonNull final StickerPreviewViewHolder stickerPreviewViewHolder, final int i) {
Sticker thisSticker = stickerPack.getSticker(i);
Context thisContext = stickerPreviewViewHolder.stickerPreviewView.getContext();
stickerPreviewViewHolder.stickerPreviewView.setImageResource(errorResource);
stickerPreviewViewHolder.stickerPreviewView.setImageURI(thisSticker.getUri());
stickerPreviewViewHolder.stickerPreviewView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ImageView image = new ImageView(thisContext);
image.setImageURI(thisSticker.getUri());
AlertDialog alertDialog = new AlertDialog.Builder(thisContext)
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
})
.setPositiveButton("Confirm", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
if(stickerPack.getStickers().size()>3 || !WhitelistCheck.isWhitelisted(thisContext, stickerPack.getIdentifier())){
dialogInterface.dismiss();
stickerPack.deleteSticker(thisSticker);
Activity thisActivity = ((Activity)thisContext);
thisActivity.finish();
thisActivity.startActivity(thisActivity.getIntent());
Toast.makeText(thisContext, "Sticker Pack deleted", Toast.LENGTH_SHORT).show();
} else {
AlertDialog alertDialog = new AlertDialog.Builder(thisContext)
.setNegativeButton("Ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
dialogInterface.dismiss();
}
}).create();
alertDialog.setTitle("Invalid Action");
alertDialog.setMessage("A sticker pack that is already applied to WhatsApp cannot have less than 3 stickers. " +
"In order to remove additional stickers, please add more to the pack first or remove the pack from the WhatsApp app.");
alertDialog.show();
}
}
})
.setView(image)
.create();
alertDialog.setTitle("Do you want to delete this sticker?");
alertDialog.setMessage("Deleting this sticker will also remove it from your WhatsApp app.");
alertDialog.show();
}
});
}
@Override
public int getItemCount() {
int numberOfPreviewImagesInPack;
numberOfPreviewImagesInPack = stickerPack.getStickers().size();
if (cellLimit > 0) {
return Math.min(numberOfPreviewImagesInPack, cellLimit);
}
return numberOfPreviewImagesInPack;
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.facebook.drawee.view.SimpleDraweeView;
import com.idoideas.stickermaker.R;
public class StickerPreviewViewHolder extends RecyclerView.ViewHolder {
public SimpleDraweeView stickerPreviewView;
StickerPreviewViewHolder(final View itemView) {
super(itemView);
stickerPreviewView = itemView.findViewById(R.id.sticker_preview);
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) WhatsApp Inc. and its affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.idoideas.stickermaker.WhatsAppBasedCode;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import com.idoideas.stickermaker.BuildConfig;
@SuppressWarnings("FieldCanBeLocal")
public class WhitelistCheck {
private static final String AUTHORITY_QUERY_PARAM = "authority";
private static final String IDENTIFIER_QUERY_PARAM = "identifier";
private static String STICKER_APP_AUTHORITY = BuildConfig.CONTENT_PROVIDER_AUTHORITY;
private static String CONSUMER_WHATSAPP_PACKAGE_NAME = "com.whatsapp";
private static String SMB_WHATSAPP_PACKAGE_NAME = "com.whatsapp.w4b";
private static String CONTENT_PROVIDER = ".provider.sticker_whitelist_check";
private static String QUERY_PATH = "is_whitelisted";
private static String QUERY_RESULT_COLUMN_NAME = "result";
static boolean isWhitelisted(@NonNull Context context, @NonNull String identifier) {
try {
boolean consumerResult = isWhitelistedFromProvider(context, identifier, CONSUMER_WHATSAPP_PACKAGE_NAME);
boolean smbResult = isWhitelistedFromProvider(context, identifier, SMB_WHATSAPP_PACKAGE_NAME);
return consumerResult && smbResult;
} catch (Exception e) {
return false;
}
}
private static boolean isWhitelistedFromProvider(@NonNull Context context, @NonNull String identifier, String whatsappPackageName) {
final PackageManager packageManager = context.getPackageManager();
if (isPackageInstalled(whatsappPackageName, packageManager)) {
final String whatsappProviderAuthority = whatsappPackageName + CONTENT_PROVIDER;
final ProviderInfo providerInfo = packageManager.resolveContentProvider(whatsappProviderAuthority, PackageManager.GET_META_DATA);
// provider is not there.
if (providerInfo == null) {
return false;
}
final Uri queryUri = new Uri.Builder().scheme(StickerContentProvider.CONTENT_SCHEME).authority(whatsappProviderAuthority).appendPath(QUERY_PATH).appendQueryParameter(AUTHORITY_QUERY_PARAM, STICKER_APP_AUTHORITY).appendQueryParameter(IDENTIFIER_QUERY_PARAM, identifier).build();
try (final Cursor cursor = context.getContentResolver().query(queryUri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
final int whiteListResult = cursor.getInt(cursor.getColumnIndexOrThrow(QUERY_RESULT_COLUMN_NAME));
return whiteListResult == 1;
}
}
} else {
//if app is not installed, then don't need to take into its whitelist info into account.
return true;
}
return false;
}
private static boolean isPackageInstalled(String packageName, PackageManager packageManager) {
try {
final ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
//noinspection SimplifiableIfStatement
if (applicationInfo != null) {
return applicationInfo.enabled;
} else {
return false;
}
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@color/darkBlue" />
<item android:state_enabled="true" android:state_focused="true" android:drawable="@color/darkBlue" />
<item android:drawable="@color/lightBlue" />
</selector>

View file

@ -0,0 +1,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/btn_green_pressed" />
<item android:state_enabled="true" android:state_focused="true" android:drawable="@drawable/btn_green_pressed" />
<item android:drawable="@drawable/btn_green_normal" />
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

View file

@ -0,0 +1,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@color/darkRed" />
<item android:state_enabled="true" android:state_focused="true" android:drawable="@color/darkRed" />
<item android:drawable="@color/lightRed" />
</selector>

View file

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeWidth="1"
android:strokeColor="#00000000">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View file

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent">
<ImageView
android:layout_width="match_parent"
android:layout_height="150dp"
android:src="@drawable/stickerbanner"
android:id="@+id/banner"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:textStyle="bold|italic"
android:text="Made by Idoideas"
android:textSize="20sp"
android:textColor="@android:color/white"
android:id="@+id/madebytag"
android:layout_below="@id/banner"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Email: Idoideasmail@gmail.com"
android:layout_centerHorizontal="true"
android:textStyle="bold|italic"
android:textSize="15sp"
android:textColor="@android:color/white"
android:id="@+id/emailtag"
android:layout_below="@id/madebytag"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="© Idoideas, 2018"
android:layout_centerHorizontal="true"
android:textStyle="bold|italic"
android:textSize="15sp"
android:textColor="@android:color/white"
android:id="@+id/copyright"
android:layout_below="@id/emailtag"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Find me also in:"
android:layout_centerHorizontal="true"
android:textStyle="bold|italic"
android:textSize="15sp"
android:textColor="@android:color/white"
android:layout_below="@id/copyright"
android:id="@+id/findmein"
android:layout_marginTop="20dp"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/findmein"
android:layout_centerHorizontal="true"
android:layout_marginTop="10dp"
android:layout_marginBottom="20dp">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/redditlogo"
android:id="@+id/redditlogo"
android:foreground="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:layout_marginEnd="10dp"/>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/githublogo"
android:id="@+id/githublogo"
android:foreground="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:layout_toEndOf="@id/redditlogo"
android:layout_marginEnd="10dp"/>
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/twitterlogo"
android:id="@+id/twitterlogo"
android:foreground="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:layout_toEndOf="@id/githublogo"/>
</RelativeLayout>
</RelativeLayout>

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/infoPageBackgroundColor"
android:orientation="vertical"
android:paddingTop="8dp"
tools:context=".WhatsAppBasedCode.StickerPackInfoActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:orientation="vertical">
<TextView
android:id="@+id/tray_icon"
style="@style/InfoButtonStyle"
android:textColor="#B9B9B9"
tools:drawableLeft="@drawable/sticker_3rdparty_web"
tools:drawableStart="@drawable/sticker_3rdparty_web"
android:clickable="false"
android:text="@string/info_screen_tray_icon" />
<TextView
android:id="@+id/view_webpage"
style="@style/InfoButtonStyle"
android:drawableLeft="@drawable/sticker_3rdparty_web"
android:drawableStart="@drawable/sticker_3rdparty_web"
android:text="@string/view_webpage" />
<TextView
android:id="@+id/send_email"
style="@style/InfoButtonStyle"
android:drawableLeft="@drawable/sticker_3rdparty_email"
android:drawableStart="@drawable/sticker_3rdparty_email"
android:text="@string/send_email" />
<TextView
android:id="@+id/privacy_policy"
style="@style/InfoButtonStyle"
android:drawableLeft="@drawable/sticker_3rdparty_privacy"
android:drawableStart="@drawable/sticker_3rdparty_privacy"
android:text="@string/privacy_policy" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WhatsAppBasedCode.StickerPackListActivity"
tools:showIn="@layout/activity_sticker_pack_list">
<TextView
android:id="@+id/error_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@android:color/holo_red_dark" />
<android.support.v7.widget.RecyclerView
android:id="@+id/sticker_pack_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent">
<com.google.android.gms.ads.AdView
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
ads:adSize="BANNER"
ads:adUnitId="@string/admob_banner_main_unit_id"
>
</com.google.android.gms.ads.AdView>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:background="#000">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/sticker_preview"
android:layout_width="@dimen/sticker_pack_details_image_size"
android:layout_height="@dimen/sticker_pack_details_image_size"
android:padding="@dimen/sticker_pack_details_image_padding"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
tools:visibility="visible" />
<ProgressBar
android:id="@+id/loading_progress"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center"
android:visibility="gone" />
</FrameLayout>

View file

@ -0,0 +1,258 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:orientation="vertical">
<android.support.constraint.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:layout_marginEnd="@dimen/preview_side_margin"
android:layout_marginLeft="@dimen/preview_side_margin"
android:layout_marginRight="@dimen/preview_side_margin"
android:layout_marginStart="@dimen/preview_side_margin">
<ImageView
android:id="@+id/tray_image"
android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
android:layout_width="36dp"
android:layout_height="36dp"
android:contentDescription="@string/tray_image_content_description"
android:src="@color/colorAccent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@color/colorAccent"
android:layout_marginStart="16dp" />
<TextView
android:id="@+id/pack_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_from_tray_to_name"
android:layout_marginStart="@dimen/margin_from_tray_to_name"
android:layout_marginTop="8dp"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@+id/tray_image"
app:layout_constraintTop_toTopOf="parent"
tools:text="Pack Name" />
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_from_tray_to_name"
android:layout_marginStart="@dimen/margin_from_tray_to_name"
android:layout_marginTop="16dp"
app:layout_constraintStart_toEndOf="@+id/tray_image"
app:layout_constraintTop_toBottomOf="@+id/pack_name"
tools:text="Author" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="?attr/selectableItemBackground"
android:id="@+id/add_sticker_to_pack">
<ImageView
android:id="@+id/plus_icon_add_sticker"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_centerVertical="true"
android:contentDescription="@string/add_button_content_description"
android:scaleType="center"
android:src="@drawable/sticker_3rdparty_add"
android:tint="@color/colorAccent"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add Sticker"
android:layout_centerVertical="true"
android:textColor="@color/colorAccent"
android:textSize="17sp"
android:layout_marginEnd="16dp"
android:layout_toEndOf="@id/plus_icon_add_sticker"/>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.idoideas.stickermaker.WhatsAppBasedCode.BottomFadingRecyclerView
android:id="@+id/sticker_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:fadingEdgeLength="@dimen/fading_edge_length"
android:paddingEnd="@dimen/preview_side_margin"
android:paddingLeft="@dimen/preview_side_margin"
android:paddingRight="@dimen/preview_side_margin"
android:paddingStart="@dimen/preview_side_margin"
android:requiresFadingEdge="vertical" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:alpha="0.1"
android:background="#000000"
android:visibility="invisible"
tools:visibility="visible" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true">
<FrameLayout
android:layout_width="0dp"
android:layout_marginEnd="@dimen/preview_side_margin"
android:layout_marginLeft="@dimen/preview_side_margin"
android:layout_marginRight="@dimen/preview_side_margin"
android:layout_marginStart="@dimen/preview_side_margin"
android:layout_height="60dp"
android:id="@+id/share_pack_button"
android:layout_gravity="bottom"
android:background="@drawable/btn_blue"
android:clickable="true"
android:focusable="true"
android:layout_weight="1">
<TextView
style="@style/StickerPreviewButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:clickable="false"
android:drawableLeft="@android:drawable/ic_menu_share"
android:drawablePadding="8dp"
android:drawableTint="@android:color/white"
android:drawableStart="@android:drawable/ic_menu_share"
android:focusable="false"
android:foreground="@android:color/transparent"
android:text="@string/share_pack" />
</FrameLayout>
<FrameLayout
android:layout_width="0dp"
android:layout_marginEnd="@dimen/preview_side_margin"
android:layout_marginLeft="@dimen/preview_side_margin"
android:layout_marginRight="@dimen/preview_side_margin"
android:layout_marginStart="@dimen/preview_side_margin"
android:layout_height="60dp"
android:id="@+id/delete_pack_button"
android:layout_gravity="bottom"
android:background="@drawable/btn_red"
android:clickable="true"
android:focusable="true"
android:layout_weight="1">
<TextView
style="@style/StickerPreviewButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:clickable="false"
android:drawableLeft="@android:drawable/ic_menu_delete"
android:drawablePadding="8dp"
android:drawableTint="@android:color/white"
android:drawableStart="@android:drawable/ic_menu_delete"
android:focusable="false"
android:foreground="@android:color/transparent"
android:text="@string/delete_pack" />
</FrameLayout>
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_marginEnd="@dimen/preview_side_margin"
android:layout_marginLeft="@dimen/preview_side_margin"
android:layout_marginRight="@dimen/preview_side_margin"
android:layout_marginStart="@dimen/preview_side_margin"
android:layout_height="60dp">
<TextView
android:id="@+id/already_added_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/details_pack_already_added"
android:textColor="#9B9B9B"
android:textSize="14sp"
android:visibility="gone"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/add_to_whatsapp_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@drawable/btn_green"
android:clickable="true"
android:focusable="true">
<TextView
style="@style/StickerPreviewButtonText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:clickable="false"
android:drawableLeft="@drawable/sticker_3rdparty_wa"
android:drawablePadding="8dp"
android:drawableStart="@drawable/sticker_3rdparty_wa"
android:focusable="false"
android:foreground="@android:color/transparent"
android:text="@string/add_to_whatsapp" />
</FrameLayout>
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
>
<com.google.android.gms.ads.AdView
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
ads:adSize="BANNER"
ads:adUnitId="@string/admob_banner_packview_unit_id">
</com.google.android.gms.ads.AdView>
</FrameLayout>
</LinearLayout>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<com.facebook.drawee.view.SimpleDraweeView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sticker_pack_list_item_image"
android:layout_width="@dimen/sticker_pack_list_item_preview_image_size"
android:layout_height="@dimen/sticker_pack_list_item_preview_image_size"
android:padding="@dimen/sticker_pack_list_item_preview_image_padding"
android:scaleType="fitCenter" />

View file

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/sticker_store_row_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical"
android:paddingBottom="4dp"
android:paddingEnd="16dp"
android:paddingStart="16dp"
android:paddingTop="8dp">
<ImageView
android:id="@+id/add_button_on_list"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/add_button_content_description"
android:scaleType="center"
android:src="@android:drawable/stat_sys_download"
android:tint="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/export_button_on_list"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="60dp"
android:scaleX="0.75"
android:scaleY="0.75"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/share_button_content_description"
android:scaleType="center"
android:src="@android:drawable/ic_menu_share"
android:tint="@color/colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/sticker_pack_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/sticker_pack_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:textColor="@android:color/black"
android:textSize="16sp"
android:gravity="center_vertical"
tools:text="Pusheen" />
<TextView
android:id="@+id/sticker_pack_publisher"
style="@style/sticker_packs_list_item_author_style"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:gravity="center_vertical"
tools:text="petellison" />
<!--TextView
android:id="@+id/sticker_pack_list_item_dot"
style="@style/sticker_packs_list_item_author_style"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingEnd="@dimen/sticker_pack_list_item_row_dot_horizontal_padding"
android:paddingLeft="@dimen/sticker_pack_list_item_row_dot_horizontal_padding"
android:paddingRight="@dimen/sticker_pack_list_item_row_dot_horizontal_padding"
android:paddingStart="@dimen/sticker_pack_list_item_row_dot_horizontal_padding"
android:text="@string/dot" /-->
<!--TextView
android:id="@+id/sticker_pack_filesize"
style="@style/sticker_packs_list_item_author_style"
android:layout_width="wrap_content"
android:layout_height="match_parent"
tools:text="700kb" /-->
</LinearLayout>
<LinearLayout
android:id="@+id/sticker_packs_list_item_image_list"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="60dp"
android:layout_marginRight="60dp"
android:layout_marginTop="8dp"
android:orientation="horizontal"
app:layout_constraintEnd_toStartOf="@id/add_button_on_list"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sticker_pack_info" />
</android.support.constraint.ConstraintLayout>

View file

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.idoideas.stickermaker.Activities.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_info"
android:icon="@drawable/sticker_3rdparty_info"
android:title="@string/more_info_button"
app:showAsAction="always" />
</menu>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_donate"
android:title="donate"
app:showAsAction="always"
/>
<item
android:id="@+id/action_add"
android:icon="@drawable/sticker_3rdparty_add"
android:title="Add new sticker pack"
app:showAsAction="always" />
<item
android:id="@+id/action_info"
android:icon="@drawable/sticker_3rdparty_info"
android:title="About the developer"
app:showAsAction="always" />
</menu>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mobile_navigation">
</navigation>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">@android:color/transparent</color>
<color name="colorPrimaryDark">#BABABA</color>
<color name="colorAccent">#128C7E</color>
<color name="inactiveAddButtonColor">#B3B3B3</color>
<color name="infoPageBackgroundColor">#EBEBEB</color>
<color name="actionBarArrowColor">@color/colorAccent</color>
<color name="lightBlue">#4286f4</color>
<color name="darkBlue">#3065ba</color>
<color name="lightRed">#f44141</color>
<color name="darkRed">#cc3939</color>
</resources>

View file

@ -0,0 +1,11 @@
<resources>
<dimen name="fab_margin">16dp</dimen>
<dimen name="preview_side_margin">16dp</dimen>
<dimen name="margin_from_tray_to_name">16dp</dimen>
<dimen name="sticker_pack_details_image_size">80dp</dimen>
<dimen name="sticker_pack_details_image_padding">8dp</dimen>
<dimen name="fading_edge_length">8dp</dimen>
<dimen name="sticker_pack_list_item_preview_image_size">50dp</dimen>
<dimen name="sticker_pack_list_item_preview_image_padding">4dp</dimen>
<dimen name="sticker_pack_list_item_row_dot_horizontal_padding">4dp</dimen>
</resources>

View file

@ -0,0 +1,42 @@
<resources>
<string name="app_name">StickerMaker</string>
<string name="action_settings">Settings</string>
<string name="tray_image_content_description">Sticker Pack Tray Image</string>
<string name="more_info_button">More info about sticker pack</string>
<string name="add_to_whatsapp">Add to WhatsApp</string>
<string name="share_pack">Share Pack</string>
<string name="delete_pack">Delete Pack</string>
<string name="add_button_content_description">Add sticker pack to WhatsApp</string>
<string name="share_button_content_description">Share sticker pack to other users</string>
<string name="title_activity_sticker_packs_list">StickerMaker</string>
<string name="dot"></string>
<string name="title_activity_sticker_pack_details_single_pack">Sticker Pack</string>
<string name="title_activity_sticker_pack_details_multiple_pack">Sticker details</string>
<string name="title_activity_sticker_pack_info">Info</string>
<string name="view_webpage">View webpage</string>
<string name="send_email">Send email</string>
<string name="privacy_policy">Privacy policy</string>
<string name="error_adding_sticker_pack">"Could not add this sticker pack. Please install the latest version of WhatsApp before adding sticker pack"</string>
<string name="error_message">Error: %s \nSee logcat for stack trace.</string>
<string name="title_validation_error">Error with the pack</string>
<string name="info_screen_tray_icon">Tray icon</string>
<string name="details_pack_already_added">Sticker pack added to WhatsApp</string>
<string name="test_device">00C4003037A3F623DA3CC00DE9B803BB</string>
<!-- Admob data -->
<!--
<string name="admob_ad_id"></string>
<string name="admob_banner_main_unit_id"></string>
<string name="admob_banner_packview_unit_id"></string>
<string name="admob_fullscreen_adding_pack_unit_id"></string>
-->
<!--Test Data-->
<string name="admob_ad_id">ca-app-pub-3940256099942544~3347511713</string>
<string name="admob_banner_main_unit_id">ca-app-pub-3940256099942544/6300978111</string>
<string name="admob_banner_packview_unit_id">ca-app-pub-3940256099942544/6300978111</string>
<string name="admob_fullscreen_adding_pack_unit_id">ca-app-pub-3940256099942544/1033173712</string>
</resources>

View file

@ -0,0 +1,64 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="actionBarTheme">@style/MyApp.ActionBarTheme</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="MyApp.ActionBarTheme" parent="@style/ThemeOverlay.AppCompat.ActionBar">
<item name="colorControlNormal">@color/actionBarArrowColor</item>
</style>
<style name="TextStyle">
<item name="android:padding">16dp</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">18sp</item>
</style>
<style name="StickerPreviewButtonText" parent="Widget.AppCompat.Button.Borderless">
<item name="android:textColor">@android:color/white</item>
<item name="android:textSize">14sp</item>
<item name="android:textStyle">bold</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
<item name="android:textAllCaps">true</item>
</style>
<style name="AppTheme.ActionBar">
<item name="windowActionBar">true</item>
<item name="windowNoTitle">false</item>
</style>
<style name="sticker_packs_list_item_author_style">
<item name="android:gravity">center_vertical</item>
<item name="android:textSize">12sp</item>
</style>
<style name="InfoButtonStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:minHeight">48dp</item>
<item name="android:gravity">center_vertical</item>
<item name="android:paddingLeft">31dp</item>
<item name="android:paddingStart" tools:targetApi="jelly_bean_mr1">24dp</item>
<item name="android:layout_marginTop">8dp</item>
<item name="android:drawablePadding">32dp</item>
<item name="android:clickable">true</item>
<item name="android:foreground">?android:attr/selectableItemBackground</item>
</style>
</resources>

View file

@ -0,0 +1,17 @@
package com.idoideas.stickermaker;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

29
build.gradle Normal file
View file

@ -0,0 +1,29 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url "https://maven.google.com"
}
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

15
gradle.properties Normal file
View file

@ -0,0 +1,15 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.enableBuildCache=true

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,6 @@
#Sat Nov 03 02:30:34 IST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

172
gradlew vendored Executable file
View file

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

Some files were not shown because too many files have changed in this diff Show more