Unverified Commit 3c039d73 authored by Simon Xu's avatar Simon Xu Committed by GitHub
Browse files

Support animated stickers on Android (#737)



* support animated stickers on Android

* [Android] Add WHO pack as animated pack

* [Android] add animated pack indicator in sticker pack list

* [Android] set sticker animation indicator on details view

* [Android] add tap to preview

* [Android] add animated sticker file validation

* [Android] Add existing file dimension and size when showing error

* [Android]improve format of error message.
Co-authored-by: default avatarSimon Xu <simonxuzhe@fb.com>
parent 4257cfdb
......@@ -7,112 +7,370 @@
"name": "Cuppy",
"publisher": "Jane Doe",
"tray_image_file": "tray_Cuppy.png",
"image_data_version":"1",
"avoid_cache":false,
"publisher_email":"",
"image_data_version": "1",
"avoid_cache": false,
"publisher_email": "",
"publisher_website": "",
"privacy_policy_website": "",
"license_agreement_website": "",
"stickers": [
{
"image_file": "01_Cuppy_smile.webp",
"emojis": ["☕","🙂"]
"emojis": [
"☕",
"🙂"
]
},
{
"image_file": "02_Cuppy_lol.webp",
"emojis": ["😄","😀"]
"emojis": [
"😄",
"😀"
]
},
{
"image_file": "03_Cuppy_rofl.webp",
"emojis": ["😆","😂"]
"emojis": [
"😆",
"😂"
]
},
{
"image_file": "04_Cuppy_sad.webp",
"emojis": ["😩","😰"]
"emojis": [
"😩",
"😰"
]
},
{
"image_file": "05_Cuppy_cry.webp",
"emojis": ["😭","💧"]
"emojis": [
"😭",
"💧"
]
},
{
"image_file": "06_Cuppy_love.webp",
"emojis": ["😍","♥"]
"emojis": [
"😍",
"♥"
]
},
{
"image_file": "07_Cuppy_hate.webp",
"emojis": ["💔","👎"]
"emojis": [
"💔",
"👎"
]
},
{
"image_file": "08_Cuppy_lovewithmug.webp",
"emojis": ["😍","💑"]
"emojis": [
"😍",
"💑"
]
},
{
"image_file": "09_Cuppy_lovewithcookie.webp",
"emojis": ["😘","🍪"]
"emojis": [
"😘",
"🍪"
]
},
{
"image_file": "10_Cuppy_hmm.webp",
"emojis": ["🤔","😐"]
"emojis": [
"🤔",
"😐"
]
},
{
"image_file": "11_Cuppy_upset.webp",
"emojis": ["😱","😵"]
"emojis": [
"😱",
"😵"
]
},
{
"image_file": "12_Cuppy_angry.webp",
"emojis": ["😡","😠"]
"emojis": [
"😡",
"😠"
]
},
{
"image_file": "13_Cuppy_curious.webp",
"emojis": ["❓","🤔"]
"emojis": [
"❓",
"🤔"
]
},
{
"image_file": "14_Cuppy_weird.webp",
"emojis": ["🌈","😜"]
"emojis": [
"🌈",
"😜"
]
},
{
"image_file": "15_Cuppy_bluescreen.webp",
"emojis": ["💻","😩"]
"emojis": [
"💻",
"😩"
]
},
{
"image_file": "16_Cuppy_angry.webp",
"emojis": ["😡","😤"]
"emojis": [
"😡",
"😤"
]
},
{
"image_file": "17_Cuppy_tired.webp",
"emojis": ["😩","😨"]
"emojis": [
"😩",
"😨"
]
},
{
"image_file": "18_Cuppy_workhard.webp",
"emojis": ["💻","🌃"]
"emojis": [
"💻",
"🌃"
]
},
{
"image_file": "19_Cuppy_shine.webp",
"emojis": ["🎉","✨"]
"emojis": [
"🎉",
"✨"
]
},
{
"image_file": "20_Cuppy_disgusting.webp",
"emojis": ["🤮","👎"]
"emojis": [
"🤮",
"👎"
]
},
{
"image_file": "21_Cuppy_hi.webp",
"emojis": ["🖐","🙋"]
"emojis": [
"🖐",
"🙋"
]
},
{
"image_file": "22_Cuppy_bye.webp",
"emojis": ["🖐","👋"]
"emojis": [
"🖐",
"👋"
]
},
{
"image_file": "23_Cuppy_greentea.webp",
"emojis": ["🍵","😌"]
"emojis": [
"🍵",
"😌"
]
},
{
"image_file": "24_Cuppy_phone.webp",
"emojis": ["📱","😦"]
"emojis": [
"📱",
"😦"
]
},
{
"image_file": "25_Cuppy_battery.webp",
"emojis": ["🔋","😵"]
"emojis": [
"🔋",
"😵"
]
}
]
},
{
"identifier": "2",
"name": "Together While Apart",
"publisher": "World Health Organization",
"tray_image_file": "01.png",
"image_data_version": "1",
"avoid_cache": false,
"publisher_email": "",
"publisher_website": "",
"privacy_policy_website": "",
"license_agreement_website": "",
"animated_sticker_pack": true,
"stickers": [
{
"image_file": "01_SendingLove.webp",
"emojis": [
"🥰",
"😘",
"❤️"
]
},
{
"image_file": "02_WellDoThisTogether.webp",
"emojis": [
"✊",
"💪",
"🙏"
]
},
{
"image_file": "03_Heart.webp",
"emojis": [
"❤️",
"🥰",
"💕"
]
},
{
"image_file": "04_AirHighFive.webp",
"emojis": [
"🖐",
"🙌",
"✋"
]
},
{
"image_file": "05_GroupVideoCalling.webp",
"emojis": [
"📱",
"👯",
"👋"
]
},
{
"image_file": "06_StayConnected.webp",
"emojis": [
"📱",
"🙋‍♀",
"🙋‍♂"
]
},
{
"image_file": "07_OK.webp",
"emojis": [
"👍",
"👌",
"🙂"
]
},
{
"image_file": "08_AreYouOK.webp",
"emojis": [
"👋",
"❓",
"📱"
]
},
{
"image_file": "09_StayingHomeMug.webp",
"emojis": [
"☕",
"🍵",
"🏠"
]
},
{
"image_file": "10_WorkingFromBed.webp",
"emojis": [
"👩‍💻",
"👨‍💻",
"🛏"
]
},
{
"image_file": "11_StayCalm.webp",
"emojis": [
"🧘♀",
"🧘♂",
"🤙"
]
},
{
"image_file": "12_Gymnastics.webp",
"emojis": [
"🤸",
"🐩",
"💪"
]
},
{
"image_file": "13_DoubleChecking.webp",
"emojis": [
"📰",
"🔎",
"🔍"
]
},
{
"image_file": "14_CatOnTheLaptop.webp",
"emojis": [
"🐈",
"🐱",
"💻"
]
},
{
"image_file": "15_WorkingFromHomeF.webp",
"emojis": [
"👩‍💻",
"🏠",
"💻"
]
},
{
"image_file": "16_WorkingFromHomeM.webp",
"emojis": [
"👨‍💻",
"🏠",
"💻"
]
},
{
"image_file": "17_WashingHands.webp",
"emojis": [
"✋",
"💦",
"🧼"
]
},
{
"image_file": "18_DontTouchYourFace.webp",
"emojis": [
"😷",
"🤒",
"🤧"
]
},
{
"image_file": "19_SocialDistancing.webp",
"emojis": [
"😷",
"🤒",
"🏠"
]
},
{
"image_file": "20_SuperheroNurse.webp",
"emojis": [
"👩‍⚕",
"👨‍⚕️",
"🩺"
]
},
{
"image_file": "21_YouAreMyHero.webp",
"emojis": [
"🙏",
"🦸‍♀️",
"🦸"
]
}
]
}
......
......@@ -75,6 +75,7 @@ class ContentFileParser {
String licenseAgreementWebsite = null;
String imageDataVersion = "";
boolean avoidCache = false;
boolean animatedStickerPack = false;
List<Sticker> stickerList = null;
while (reader.hasNext()) {
String key = reader.nextName();
......@@ -112,6 +113,9 @@ class ContentFileParser {
case "avoid_cache":
avoidCache = reader.nextBoolean();
break;
case "animated_sticker_pack":
animatedStickerPack = reader.nextBoolean();
break;
default:
reader.skipValue();
}
......@@ -138,7 +142,7 @@ class ContentFileParser {
throw new IllegalStateException("image_data_version should not be empty");
}
reader.endObject();
final StickerPack stickerPack = new StickerPack(identifier, name, publisher, trayImageFile, publisherEmail, publisherWebsite, privacyPolicyWebsite, licenseAgreementWebsite, imageDataVersion, avoidCache);
final StickerPack stickerPack = new StickerPack(identifier, name, publisher, trayImageFile, publisherEmail, publisherWebsite, privacyPolicyWebsite, licenseAgreementWebsite, imageDataVersion, avoidCache, animatedStickerPack);
stickerPack.setStickers(stickerList);
return stickerPack;
}
......@@ -165,6 +169,7 @@ class ContentFileParser {
}
}
reader.endArray();
} else {
throw new IllegalStateException("unknown field in json: " + key);
}
......
......@@ -48,6 +48,7 @@ public class StickerContentProvider extends ContentProvider {
public static final String LICENSE_AGREENMENT_WEBSITE = "sticker_pack_license_agreement_website";
public static final String IMAGE_DATA_VERSION = "image_data_version";
public static final String AVOID_CACHE = "whatsapp_will_not_cache_stickers";
public static final String ANIMATED_STICKER_PACK = "animated_sticker_pack";
public static final String STICKER_FILE_NAME_IN_QUERY = "sticker_file_name";
public static final String STICKER_FILE_EMOJI_IN_QUERY = "sticker_emoji";
......@@ -191,6 +192,7 @@ public class StickerContentProvider extends ContentProvider {
LICENSE_AGREENMENT_WEBSITE,
IMAGE_DATA_VERSION,
AVOID_CACHE,
ANIMATED_STICKER_PACK,
});
for (StickerPack stickerPack : stickerPackList) {
MatrixCursor.RowBuilder builder = cursor.newRow();
......@@ -206,6 +208,7 @@ public class StickerContentProvider extends ContentProvider {
builder.add(stickerPack.licenseAgreementWebsite);
builder.add(stickerPack.imageDataVersion);
builder.add(stickerPack.avoidCache ? 1 : 0);
builder.add(stickerPack.animatedStickerPack ? 1 : 0);
}
cursor.setNotificationUri(Objects.requireNonNull(getContext()).getContentResolver(), uri);
return cursor;
......
......@@ -24,6 +24,7 @@ class StickerPack implements Parcelable {
final String licenseAgreementWebsite;
final String imageDataVersion;
final boolean avoidCache;
final boolean animatedStickerPack;
String iosAppStoreLink;
private List<Sticker> stickers;
......@@ -31,7 +32,7 @@ class StickerPack implements Parcelable {
String androidPlayStoreLink;
private boolean isWhitelisted;
StickerPack(String identifier, String name, String publisher, String trayImageFile, String publisherEmail, String publisherWebsite, String privacyPolicyWebsite, String licenseAgreementWebsite, String imageDataVersion, boolean avoidCache) {
StickerPack(String identifier, String name, String publisher, String trayImageFile, String publisherEmail, String publisherWebsite, String privacyPolicyWebsite, String licenseAgreementWebsite, String imageDataVersion, boolean avoidCache, boolean animatedStickerPack) {
this.identifier = identifier;
this.name = name;
this.publisher = publisher;
......@@ -42,6 +43,7 @@ class StickerPack implements Parcelable {
this.licenseAgreementWebsite = licenseAgreementWebsite;
this.imageDataVersion = imageDataVersion;
this.avoidCache = avoidCache;
this.animatedStickerPack = animatedStickerPack;
}
void setIsWhitelisted(boolean isWhitelisted) {
......@@ -68,6 +70,7 @@ class StickerPack implements Parcelable {
isWhitelisted = in.readByte() != 0;
imageDataVersion = in.readString();
avoidCache = in.readByte() != 0;
animatedStickerPack = in.readByte() != 0;
}
public static final Creator<StickerPack> CREATOR = new Creator<StickerPack>() {
......@@ -128,5 +131,6 @@ class StickerPack implements Parcelable {
dest.writeByte((byte) (isWhitelisted ? 1 : 0));
dest.writeString(imageDataVersion);
dest.writeByte((byte) (avoidCache ? 1 : 0));
dest.writeByte((byte) (animatedStickerPack ? 1 : 0));
}
}
......@@ -24,6 +24,8 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.drawee.view.SimpleDraweeView;
import java.lang.ref.WeakReference;
public class StickerPackDetailsActivity extends AddStickerPackActivity {
......@@ -43,6 +45,7 @@ public class StickerPackDetailsActivity extends AddStickerPackActivity {
public static final String EXTRA_SHOW_UP_BUTTON = "show_up_button";
public static final String EXTRA_STICKER_PACK_DATA = "sticker_pack";
private RecyclerView recyclerView;
private GridLayoutManager layoutManager;
private StickerPreviewAdapter stickerPreviewAdapter;
......@@ -53,6 +56,7 @@ public class StickerPackDetailsActivity extends AddStickerPackActivity {
private View divider;
private WhiteListCheckAsyncTask whiteListCheckAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -63,6 +67,7 @@ public class StickerPackDetailsActivity extends AddStickerPackActivity {
TextView packPublisherTextView = findViewById(R.id.author);
ImageView packTrayIcon = findViewById(R.id.tray_image);
TextView packSizeTextView = findViewById(R.id.pack_size);
SimpleDraweeView expandedStickerView = findViewById(R.id.sticker_details_expanded_sticker);
addButton = findViewById(R.id.add_to_whatsapp_button);
alreadyAddedText = findViewById(R.id.already_added_text);
......@@ -73,7 +78,7 @@ public class StickerPackDetailsActivity extends AddStickerPackActivity {
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);
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, expandedStickerView);
recyclerView.setAdapter(stickerPreviewAdapter);
}
packNameTextView.setText(stickerPack.name);
......@@ -85,9 +90,9 @@ public class StickerPackDetailsActivity extends AddStickerPackActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(showUpButton);
getSupportActionBar().setTitle(showUpButton ? getResources().getString(R.string.title_activity_sticker_pack_details_multiple_pack) : getResources().getQuantityString(R.plurals.title_activity_sticker_packs_list, 1));
}
findViewById(R.id.sticker_pack_animation_indicator).setVisibility(stickerPack.animatedStickerPack ? View.VISIBLE : View.GONE);
}
private void launchInfoActivity(String publisherWebsite, String publisherEmail, String privacyPolicyWebsite, String licenseAgreementWebsite, String trayIconUriString) {
Intent intent = new Intent(StickerPackDetailsActivity.this, StickerPackInfoActivity.class);
intent.putExtra(StickerPackDetailsActivity.EXTRA_STICKER_PACK_ID, stickerPack.identifier);
......@@ -173,9 +178,11 @@ public class StickerPackDetailsActivity extends AddStickerPackActivity {
if (isWhitelisted) {
addButton.setVisibility(View.GONE);
alreadyAddedText.setVisibility(View.VISIBLE);
findViewById(R.id.sticker_pack_details_tap_to_preview).setVisibility(View.GONE);
} else {
addButton.setVisibility(View.VISIBLE);
alreadyAddedText.setVisibility(View.GONE);
findViewById(R.id.sticker_pack_details_tap_to_preview).setVisibility(View.VISIBLE);
}
}
......
......@@ -78,6 +78,7 @@ public class StickerPackListAdapter extends RecyclerView.Adapter<StickerPackList
viewHolder.imageRowView.addView(rowImage);
}
setAddButtonAppearance(viewHolder.addButton, pack);
viewHolder.animatedStickerPackIndicator.setVisibility(pack.animatedStickerPack ? View.VISIBLE : View.GONE);
}
private void setAddButtonAppearance(ImageView addButton, StickerPack pack) {
......
......@@ -22,6 +22,7 @@ class StickerPackListItemViewHolder extends RecyclerView.ViewHolder {
final TextView publisherView;
final TextView filesizeView;
final ImageView addButton;
final ImageView animatedStickerPackIndicator;
final LinearLayout imageRowView;
StickerPackListItemViewHolder(final View itemView) {
......@@ -32,5 +33,6 @@ class StickerPackListItemViewHolder extends RecyclerView.ViewHolder {
filesizeView = itemView.findViewById(R.id.sticker_pack_filesize);
addButton = itemView.findViewById(R.id.add_button_on_list);
imageRowView = itemView.findViewById(R.id.sticker_packs_list_item_image_list);
animatedStickerPackIndicator = itemView.findViewById(R.id.sticker_pack_animation_indicator);
}
}
\ No newline at end of file
......@@ -12,9 +12,9 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;