diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 38e40af9..d195bd87 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -81,7 +81,7 @@ android { defaultConfig { minSdkVersion 8 targetSdkVersion 19 - versionCode 258 - versionName "1.5.5" + versionCode 260 + versionName "1.5.6" } } diff --git a/TMessagesProj/jni/Android.mk b/TMessagesProj/jni/Android.mk index a59d6aa3..a45cb8de 100755 --- a/TMessagesProj/jni/Android.mk +++ b/TMessagesProj/jni/Android.mk @@ -3,10 +3,10 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_PRELINK_MODULE := false LOCAL_MODULE := tmessages -LOCAL_CFLAGS := -w -std=gnu99 -O3 -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DLOG_DISABLED +LOCAL_CFLAGS := -w -std=gnu99 -O2 -DNULL=0 -DSOCKLEN_T=socklen_t -DLOCALE_NOT_USED -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -DLOG_DISABLED LOCAL_CFLAGS += -Drestrict='' -D__EMX__ -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT -DHAVE_LRINTF -fno-math-errno LOCAL_CFLAGS += -DANDROID_NDK -DDISABLE_IMPORTGL -fno-strict-aliasing -fprefetch-loop-arrays -DAVOID_TABLES -DANDROID_TILE_BASED_DECODE -DANDROID_ARMV6_IDCT -LOCAL_CPPFLAGS := -DBSD=1 -ffast-math -O3 -funroll-loops +LOCAL_CPPFLAGS := -DBSD=1 -ffast-math -O2 -funroll-loops #LOCAL_LDLIBS := -llog LOCAL_SRC_FILES := \ diff --git a/TMessagesProj/libs/armeabi-v7a/libtmessages.so b/TMessagesProj/libs/armeabi-v7a/libtmessages.so index 8dc7dfa9..9d9f7338 100755 Binary files a/TMessagesProj/libs/armeabi-v7a/libtmessages.so and b/TMessagesProj/libs/armeabi-v7a/libtmessages.so differ diff --git a/TMessagesProj/libs/armeabi/libtmessages.so b/TMessagesProj/libs/armeabi/libtmessages.so index b8cbef40..13a6c5a3 100755 Binary files a/TMessagesProj/libs/armeabi/libtmessages.so and b/TMessagesProj/libs/armeabi/libtmessages.so differ diff --git a/TMessagesProj/libs/x86/libtmessages.so b/TMessagesProj/libs/x86/libtmessages.so index cf6e040f..963e616e 100755 Binary files a/TMessagesProj/libs/x86/libtmessages.so and b/TMessagesProj/libs/x86/libtmessages.so differ diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java index 35748f4d..9b6161bd 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java @@ -2584,10 +2584,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public void tcpConnectionConnected(TcpConnection connection) { Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); if (datacenter.authKey != null) { - processRequestQueue(connection.transportRequestClass, connection.getDatacenterId()); if ((connection.transportRequestClass & RPCRequest.RPCRequestClassPush) != 0) { sendingPushPing = false; lastPushPingTime = System.currentTimeMillis() - 60000 * 3 + 10000; + } else { + processRequestQueue(connection.transportRequestClass, connection.getDatacenterId()); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java index 0b15d2d7..9ba56720 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java @@ -44,18 +44,22 @@ public class FileLog { return; } dateFormat = FastDateFormat.getInstance("dd_MM_yyyy_HH_mm_ss", Locale.US); - File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null); - if (sdCard == null) { - return; - } - File dir = new File(sdCard.getAbsolutePath() + "/logs"); - if (dir == null) { - return; - } - dir.mkdirs(); - currentFile = new File(dir, dateFormat.format(System.currentTimeMillis()) + ".txt"); - if (currentFile == null) { - return; + try { + File sdCard = ApplicationLoader.applicationContext.getExternalFilesDir(null); + if (sdCard == null) { + return; + } + File dir = new File(sdCard.getAbsolutePath() + "/logs"); + if (dir == null) { + return; + } + dir.mkdirs(); + currentFile = new File(dir, dateFormat.format(System.currentTimeMillis()) + ".txt"); + if (currentFile == null) { + return; + } + } catch (Exception e) { + e.printStackTrace(); } try { currentFile.createNewFile(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java index 39858fb4..76326583 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java @@ -12,7 +12,6 @@ import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.os.PowerManager; import org.json.JSONObject; import org.telegram.ui.ApplicationLoader; @@ -20,7 +19,6 @@ import org.telegram.ui.ApplicationLoader; public class GcmBroadcastReceiver extends BroadcastReceiver { public static final int NOTIFICATION_ID = 1; - private static PowerManager.WakeLock wakeLock = null; private static final Integer sync = 1; @Override @@ -28,27 +26,6 @@ public class GcmBroadcastReceiver extends BroadcastReceiver { FileLog.d("tmessages", "GCM received intent: " + intent); if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { - synchronized (sync) { - try { - if (wakeLock == null) { - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "lock"); - } - if (!wakeLock.isHeld()) { - wakeLock.acquire(5000); - } - } catch (Exception e) { - try { - if (wakeLock != null) { - wakeLock.release(); - } - } catch (Exception e2) { - FileLog.e("tmessages", e2); - } - FileLog.e("tmessages", e); - } - } - Utilities.RunOnUIThread(new Runnable() { @Override public void run() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index baa7dd20..e52b9247 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -4452,6 +4452,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter boolean needVibrate = false; String choosenSoundPath = null; + int ledColor = 0xff00ff00; if (chat_id != 0) { choosenSoundPath = preferences.getString("sound_chat_path_" + chat_id, null); @@ -4461,6 +4462,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter choosenSoundPath = preferences.getString("GroupSoundPath", defaultPath); } needVibrate = preferences.getBoolean("EnableVibrateGroup", true); + ledColor = preferences.getInt("GroupLed", 0xff00ff00); } else if (user_id != 0) { choosenSoundPath = preferences.getString("sound_path_" + user_id, null); if (choosenSoundPath != null && choosenSoundPath.equals(defaultPath)) { @@ -4469,6 +4471,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter choosenSoundPath = preferences.getString("GlobalSoundPath", defaultPath); } needVibrate = preferences.getBoolean("EnableVibrateAll", true); + ledColor = preferences.getInt("MessagesLed", 0xff00ff00); + } + if (preferences.contains("color_" + dialog_id)) { + ledColor = preferences.getInt("color_" + dialog_id, 0); } if (!needVibrate && vibrate_override == 1) { @@ -4478,6 +4484,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter } String name = Utilities.formatName(user.first_name, user.last_name); + if ((int)dialog_id == 0) { + name = LocaleController.getString("AppName", R.string.AppName); + } String msgShort = msg.replace(name + ": ", "").replace(name + " ", ""); intent.setAction("com.tmessages.openchat" + Math.random() + Integer.MAX_VALUE); @@ -4511,7 +4520,9 @@ public class MessagesController implements NotificationCenter.NotificationCenter mBuilder.setContentIntent(contentIntent); mNotificationManager.cancel(1); Notification notification = mBuilder.build(); - notification.ledARGB = 0xff00ff00; + if (ledColor != 0) { + notification.ledARGB = ledColor; + } notification.ledOnMS = 1000; notification.ledOffMS = 1000; if (needVibrate) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java index fc2088e7..58a96607 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NativeLoader.java @@ -16,80 +16,20 @@ import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; -import java.io.OutputStreamWriter; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class NativeLoader { private static final long sizes[] = new long[] { - 922256, //armeabi - 991908, //armeabi-v7a - 1713204, //x86 + 795280, //armeabi + 844452, //armeabi-v7a + 1242164, //x86 0, //mips }; private static volatile boolean nativeLoaded = false; - public static void cleanNativeLog(Context context) { - try { - File sdCard = context.getFilesDir(); - if (sdCard == null) { - return; - } - File file = new File(sdCard, "nativeer.log"); - if (file == null || !file.exists()) { - return; - } - file.delete(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private static OutputStreamWriter streamWriter = null; - private static FileOutputStream stream = null; - - private static void closeStream() { - try { - if (stream != null) { - streamWriter.close(); - stream.close(); - stream = null; - streamWriter = null; - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static void writeNativeError(Context context, String info, Throwable throwable) { - try { - if (stream == null) { - File sdCard = context.getFilesDir(); - if (sdCard == null) { - return; - } - File file = new File(sdCard, "nativeer.log"); - if (file == null) { - return; - } - - stream = new FileOutputStream(file); - streamWriter = new OutputStreamWriter(stream); - } - streamWriter.write(info + "\n"); - streamWriter.write(throwable + "\n"); - StackTraceElement[] stack = throwable.getStackTrace(); - for (StackTraceElement el : stack) { - streamWriter.write(el + "\n"); - } - streamWriter.flush(); - } catch (Exception e) { - e.printStackTrace(); - } - } - private static File getNativeLibraryDir(Context context) { File f = null; if (context != null) { @@ -133,12 +73,10 @@ public class NativeLoader { nativeLoaded = true; } catch (Error e) { FileLog.e("tmessages", e); - writeNativeError(context, "after zip", e); } return true; } catch (Exception e) { FileLog.e("tmessages", e); - writeNativeError(context, "zip", e); } finally { if (stream != null) { try { @@ -163,8 +101,6 @@ public class NativeLoader { return; } - cleanNativeLog(context); - try { String folder = null; long libSize = 0; @@ -193,12 +129,17 @@ public class NativeLoader { } } catch (Exception e) { FileLog.e("tmessages", e); - writeNativeError(context, "arch", e); folder = "armeabi"; libSize = sizes[0]; libSize2 = sizes[1]; } + String javaArch = System.getProperty("os.arch"); + if (javaArch != null && javaArch.contains("686")) { + folder = "x86"; + libSize = sizes[2]; + } + File destFile = getNativeLibraryDir(context); if (destFile != null) { destFile = new File(destFile, "libtmessages.so"); @@ -207,27 +148,23 @@ public class NativeLoader { try { System.loadLibrary("tmessages"); nativeLoaded = true; - closeStream(); return; } catch (Error e) { FileLog.e("tmessages", e); - writeNativeError(context, "normal", e); } } } File destLocalFile = new File(context.getFilesDir().getAbsolutePath() + "/libtmessages.so"); - if (destLocalFile.exists()) { + if (destLocalFile != null && destLocalFile.exists()) { if (destLocalFile.length() == libSize) { try { FileLog.d("tmessages", "Load local lib"); System.load(destLocalFile.getAbsolutePath()); nativeLoaded = true; - closeStream(); return; } catch (Error e) { FileLog.e("tmessages", e); - writeNativeError(context, "local", e); } } else { destLocalFile.delete(); @@ -236,21 +173,23 @@ public class NativeLoader { FileLog.e("tmessages", "Library not found, arch = " + folder); - if (!loadFromZip(context, destLocalFile, folder) && folder.equals("armeabi-v7a")) { - folder = "armeabi"; - loadFromZip(context, destLocalFile, folder); + if (!loadFromZip(context, destLocalFile, folder)) { + folder = "x86"; + destLocalFile = new File(context.getFilesDir().getAbsolutePath() + "/libtmessages86.so"); + if (!loadFromZip(context, destLocalFile, folder)) { + destLocalFile = new File(context.getFilesDir().getAbsolutePath() + "/libtmessagesarm.so"); + folder = "armeabi"; + loadFromZip(context, destLocalFile, folder); + } } } catch (Throwable e) { e.printStackTrace(); - writeNativeError(context, "", e); } try { System.loadLibrary("tmessages"); nativeLoaded = true; - closeStream(); } catch (Error e) { - writeNativeError(context, "last chance", e); FileLog.e("tmessages", e); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java index 2abdd5d0..3776de40 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ScreenReceiver.java @@ -19,9 +19,11 @@ public class ScreenReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { FileLog.e("tmessages", "screen off"); + ApplicationLoader.lastPauseTime = System.currentTimeMillis(); ApplicationLoader.isScreenOn = false; } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { FileLog.e("tmessages", "screen on"); + ApplicationLoader.resetLastPauseTime(); ApplicationLoader.isScreenOn = true; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index dc54ddaa..9536d2d8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -39,7 +39,6 @@ import net.hockeyapp.android.UpdateManager; import org.telegram.ui.ApplicationLoader; -import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; @@ -47,7 +46,6 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -997,41 +995,6 @@ public class Utilities { public boolean includeDeviceData() { return true; } - - @Override - public String getDescription() { - String description = ""; - - try { - File sdCard = ApplicationLoader.applicationContext.getFilesDir(); - if (sdCard == null) { - return description; - } - File file = new File(sdCard, "nativeer.log"); - if (file == null || !file.exists()) { - return description; - } - - FileInputStream inputStream = new FileInputStream(file); - BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - - StringBuilder log = new StringBuilder(); - String line; - while ((line = bufferedReader.readLine()) != null) { - log.append(line); - log.append("\n"); - } - bufferedReader.close(); - inputStream.close(); - description = log.toString(); - - NativeLoader.cleanNativeLog(ApplicationLoader.applicationContext); - } catch (Exception e) { - e.printStackTrace(); - } - - return description; - } }); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java index 36ee8a5f..0a1472e2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatProfileActivity.java @@ -47,6 +47,7 @@ import org.telegram.ui.Views.ActionBar.ActionBarMenu; import org.telegram.ui.Views.AvatarUpdater; import org.telegram.ui.Views.BackupImageView; import org.telegram.ui.Views.ActionBar.BaseFragment; +import org.telegram.ui.Views.ColorPickerView; import java.util.ArrayList; import java.util.Collections; @@ -71,10 +72,12 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen private int settingsSectionRow; private int settingsNotificationsRow; private int settingsVibrateRow; + private int settingsLedRow; private int settingsSoundRow; private int sharedMediaSectionRow; private int sharedMediaRow; private int membersSectionRow; + private int membersEndRow; private int addMemberRow; private int leaveGroupRow; private int rowCount = 0; @@ -140,18 +143,21 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen settingsSectionRow = rowCount++; settingsNotificationsRow = rowCount++; settingsVibrateRow = rowCount++; + settingsLedRow = rowCount++; settingsSoundRow = rowCount++; sharedMediaSectionRow = rowCount++; sharedMediaRow = rowCount++; if (info != null && !(info instanceof TLRPC.TL_chatParticipantsForbidden)) { membersSectionRow = rowCount++; rowCount += info.participants.size(); + membersEndRow = rowCount; if (info.participants.size() < 200) { addMemberRow = rowCount++; } else { addMemberRow = -1; } } else { + membersEndRow = -1; addMemberRow = -1; membersSectionRow = -1; } @@ -197,7 +203,7 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { - if (i > membersSectionRow && i < addMemberRow) { + if (i > membersSectionRow && i < membersEndRow) { if (getParentActivity() == null) { return false; } @@ -268,7 +274,7 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen presentFragment(new MediaActivity(args)); } else if (i == addMemberRow) { openAddMenu(); - } else if (i > membersSectionRow && i < addMemberRow) { + } else if (i > membersSectionRow && i < membersEndRow) { int user_id = info.participants.get(sortedUsers.get(i - membersSectionRow - 1)).user_id; if (user_id == UserConfig.getClientUserId()) { return; @@ -304,6 +310,56 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); + } else if (i == settingsLedRow) { + if (getParentActivity() == null) { + return; + } + + LayoutInflater li = (LayoutInflater)getParentActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.settings_color_dialog_layout, null, false); + final ColorPickerView colorPickerView = (ColorPickerView)view.findViewById(R.id.color_picker); + + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + if (preferences.contains("color_" + (-chat_id))) { + colorPickerView.setOldCenterColor(preferences.getInt("color_" + (-chat_id), 0xff00ff00)); + } else { + colorPickerView.setOldCenterColor(preferences.getInt("GroupLed", 0xff00ff00)); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("LedColor", R.string.LedColor)); + builder.setView(view); + builder.setPositiveButton(LocaleController.getString("Set", R.string.Set), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt("color_" + (-chat_id), colorPickerView.getColor()); + editor.commit(); + listView.invalidateViews(); + } + }); + builder.setNeutralButton(LocaleController.getString("Disabled", R.string.Disabled), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt("color_" + (-chat_id), 0); + editor.commit(); + listView.invalidateViews(); + } + }); + builder.setNegativeButton(LocaleController.getString("Default", R.string.Default), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.remove("color_" + (-chat_id)); + editor.commit(); + listView.invalidateViews(); + } + }); + showAlertDialog(builder); } } }); @@ -588,7 +644,7 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen @Override public boolean isEnabled(int i) { - return i == settingsNotificationsRow || i == settingsSoundRow || i == sharedMediaRow || i > membersSectionRow && i <= addMemberRow || i == settingsVibrateRow; + return i == settingsNotificationsRow || i == settingsSoundRow || i == sharedMediaRow || i == addMemberRow || i > membersSectionRow && i < membersEndRow || i == settingsVibrateRow || i == settingsLedRow; } @Override @@ -830,6 +886,23 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen textView.setText(LocaleController.getString("Sound", R.string.Sound)); divider.setVisibility(View.INVISIBLE); } + } else if (type == 7) { + if (view == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.settings_row_color_layout, viewGroup, false); + } + TextView textView = (TextView)view.findViewById(R.id.settings_row_text); + View colorView = view.findViewById(R.id.settings_color); + View divider = view.findViewById(R.id.settings_row_divider); + textView.setText(LocaleController.getString("LedColor", R.string.LedColor)); + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + + if (preferences.contains("color_" + (-chat_id))) { + colorView.setBackgroundColor(preferences.getInt("color_" + (-chat_id), 0xff00ff00)); + } else { + colorView.setBackgroundColor(preferences.getInt("GroupLed", 0xff00ff00)); + } + divider.setVisibility(View.VISIBLE); } return view; } @@ -848,15 +921,17 @@ public class ChatProfileActivity extends BaseFragment implements NotificationCen return 4; } else if (i == leaveGroupRow) { return 5; - } else if (i > membersSectionRow && i < addMemberRow) { + } else if (i > membersSectionRow && i < membersEndRow) { return 3; + } else if (i == settingsLedRow) { + return 7; } return 0; } @Override public int getViewTypeCount() { - return 7; + return 8; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java index 6c2feab3..22e8395f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java @@ -100,6 +100,9 @@ public class PhotoCropActivity extends BaseFragment { } else { draggingState = 0; } + if (draggingState != 0) { + PhotoCropView.this.requestDisallowInterceptTouchEvent(true); + } oldX = x; oldY = y; } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java index 35a1ee78..ae515043 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsNotificationsActivity.java @@ -39,6 +39,7 @@ import org.telegram.messenger.RPCRequest; import org.telegram.messenger.Utilities; import org.telegram.ui.Views.ActionBar.ActionBarLayer; import org.telegram.ui.Views.ActionBar.BaseFragment; +import org.telegram.ui.Views.ColorPickerView; public class SettingsNotificationsActivity extends BaseFragment { private ListView listView; @@ -50,11 +51,13 @@ public class SettingsNotificationsActivity extends BaseFragment { private int messagePreviewRow; private int messageVibrateRow; private int messageSoundRow; + private int messageLedRow; private int groupSectionRow; private int groupAlertRow; private int groupPreviewRow; private int groupVibrateRow; private int groupSoundRow; + private int groupLedRow; private int inappSectionRow; private int inappSoundRow; private int inappVibrateRow; @@ -74,11 +77,13 @@ public class SettingsNotificationsActivity extends BaseFragment { messageAlertRow = rowCount++; messagePreviewRow = rowCount++; messageVibrateRow = rowCount++; + messageLedRow = rowCount++; messageSoundRow = rowCount++; groupSectionRow = rowCount++; groupAlertRow = rowCount++; groupPreviewRow = rowCount++; groupVibrateRow = rowCount++; + groupLedRow = rowCount++; groupSoundRow = rowCount++; inappSectionRow = rowCount++; inappSoundRow = rowCount++; @@ -110,12 +115,12 @@ public class SettingsNotificationsActivity extends BaseFragment { }); fragmentView = inflater.inflate(R.layout.settings_layout, container, false); - ListAdapter listAdapter = new ListAdapter(getParentActivity()); + final ListAdapter listAdapter = new ListAdapter(getParentActivity()); listView = (ListView)fragmentView.findViewById(R.id.listView); listView.setAdapter(listAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { + public void onItemClick(AdapterView adapterView, View view, final int i, long l) { if (i == messageAlertRow || i == groupAlertRow) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); @@ -289,6 +294,54 @@ public class SettingsNotificationsActivity extends BaseFragment { builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); } + } else if (i == messageLedRow || i == groupLedRow) { + if (getParentActivity() == null) { + return; + } + + LayoutInflater li = (LayoutInflater)getParentActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.settings_color_dialog_layout, null, false); + final ColorPickerView colorPickerView = (ColorPickerView)view.findViewById(R.id.color_picker); + + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + if (i == messageLedRow) { + colorPickerView.setOldCenterColor(preferences.getInt("MessagesLed", 0xff00ff00)); + } else if (i == groupLedRow) { + colorPickerView.setOldCenterColor(preferences.getInt("GroupLed", 0xff00ff00)); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("LedColor", R.string.LedColor)); + builder.setView(view); + builder.setPositiveButton(LocaleController.getString("Set", R.string.Set), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + if (i == messageLedRow) { + editor.putInt("MessagesLed", colorPickerView.getColor()); + } else if (i == groupLedRow) { + editor.putInt("GroupLed", colorPickerView.getColor()); + } + editor.commit(); + listView.invalidateViews(); + } + }); + builder.setNeutralButton(LocaleController.getString("Disabled", R.string.Disabled), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + if (i == messageLedRow) { + editor.putInt("MessagesLed", 0); + } else if (i == groupLedRow) { + editor.putInt("GroupLed", 0); + } + editor.commit(); + listView.invalidateViews(); + } + }); + showAlertDialog(builder); } } }); @@ -501,8 +554,23 @@ public class SettingsNotificationsActivity extends BaseFragment { textViewDetail.setText(LocaleController.getString("UndoAllCustom", R.string.UndoAllCustom)); divider.setVisibility(View.INVISIBLE); } + } else if (type == 3) { + if (view == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.settings_row_color_layout, viewGroup, false); + } + TextView textView = (TextView)view.findViewById(R.id.settings_row_text); + View colorView = view.findViewById(R.id.settings_color); + View divider = view.findViewById(R.id.settings_row_divider); + textView.setText(LocaleController.getString("LedColor", R.string.LedColor)); + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + if (i == messageLedRow) { + colorView.setBackgroundColor(preferences.getInt("MessagesLed", 0xff00ff00)); + } else if (i == groupLedRow) { + colorView.setBackgroundColor(preferences.getInt("GroupLed", 0xff00ff00)); + } + divider.setVisibility(View.VISIBLE); } - return view; } @@ -516,6 +584,8 @@ public class SettingsNotificationsActivity extends BaseFragment { i == contactJoinedRow || i == pebbleAlertRow || i == notificationsServiceRow) { return 1; + } else if (i == messageLedRow || i == groupLedRow) { + return 3; } else { return 2; } @@ -523,7 +593,7 @@ public class SettingsNotificationsActivity extends BaseFragment { @Override public int getViewTypeCount() { - return 3; + return 4; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java index bd2f15c9..59b31b1c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java @@ -48,6 +48,7 @@ import org.telegram.ui.Views.ActionBar.ActionBarMenu; import org.telegram.ui.Views.ActionBar.ActionBarMenuItem; import org.telegram.ui.Views.BackupImageView; import org.telegram.ui.Views.ActionBar.BaseFragment; +import org.telegram.ui.Views.ColorPickerView; import org.telegram.ui.Views.IdenticonView; import java.util.ArrayList; @@ -79,6 +80,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen private int settingsSoundRow; private int sharedMediaSectionRow; private int sharedMediaRow; + private int settingsLedRow; private int rowCount = 0; public UserProfileActivity(Bundle args) { @@ -126,6 +128,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen } settingsNotificationsRow = rowCount++; settingsVibrateRow = rowCount++; + settingsLedRow = rowCount++; settingsSoundRow = rowCount++; sharedMediaSectionRow = rowCount++; sharedMediaRow = rowCount++; @@ -373,6 +376,57 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); + } else if (i == settingsLedRow) { + if (getParentActivity() == null) { + return; + } + + LayoutInflater li = (LayoutInflater)getParentActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.settings_color_dialog_layout, null, false); + final ColorPickerView colorPickerView = (ColorPickerView)view.findViewById(R.id.color_picker); + + final String key = dialog_id == 0 ? "color_" + user_id : "color_" + dialog_id; + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + if (preferences.contains(key)) { + colorPickerView.setOldCenterColor(preferences.getInt(key, 0xff00ff00)); + } else { + colorPickerView.setOldCenterColor(preferences.getInt("MessagesLed", 0xff00ff00)); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("LedColor", R.string.LedColor)); + builder.setView(view); + builder.setPositiveButton(LocaleController.getString("Set", R.string.Set), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt(key, colorPickerView.getColor()); + editor.commit(); + listView.invalidateViews(); + } + }); + builder.setNeutralButton(LocaleController.getString("Disabled", R.string.Disabled), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt(key, 0); + editor.commit(); + listView.invalidateViews(); + } + }); + builder.setNegativeButton(LocaleController.getString("Default", R.string.Default), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.remove(key); + editor.commit(); + listView.invalidateViews(); + } + }); + showAlertDialog(builder); } } }); @@ -589,7 +643,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen @Override public boolean isEnabled(int i) { - return i == phoneRow || i == settingsTimerRow || i == settingsKeyRow || i == settingsNotificationsRow || i == sharedMediaRow || i == settingsSoundRow || i == settingsVibrateRow; + return i == phoneRow || i == settingsTimerRow || i == settingsKeyRow || i == settingsNotificationsRow || i == sharedMediaRow || i == settingsSoundRow || i == settingsVibrateRow || i == settingsLedRow; } @Override @@ -846,8 +900,25 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen textView.setText(LocaleController.getString("Sound", R.string.Sound)); divider.setVisibility(View.INVISIBLE); } - } + } else if (type == 6) { + if (view == null) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + view = li.inflate(R.layout.settings_row_color_layout, viewGroup, false); + } + TextView textView = (TextView)view.findViewById(R.id.settings_row_text); + View colorView = view.findViewById(R.id.settings_color); + View divider = view.findViewById(R.id.settings_row_divider); + textView.setText(LocaleController.getString("LedColor", R.string.LedColor)); + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE); + String key = dialog_id == 0 ? "color_" + user_id : "color_" + dialog_id; + if (preferences.contains(key)) { + colorView.setBackgroundColor(preferences.getInt(key, 0xff00ff00)); + } else { + colorView.setBackgroundColor(preferences.getInt("MessagesLed", 0xff00ff00)); + } + divider.setVisibility(View.VISIBLE); + } return view; } @@ -865,13 +936,15 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen return 4; } else if (i == settingsSoundRow) { return 5; + } else if (i == settingsLedRow) { + return 6; } return 0; } @Override public int getViewTypeCount() { - return 6; + return 7; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarMenuItem.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarMenuItem.java index 33b977b7..a655d93d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarMenuItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarMenuItem.java @@ -9,13 +9,14 @@ package org.telegram.ui.Views.ActionBar; import android.content.Context; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.Rect; import android.os.Build; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.inputmethod.EditorInfo; @@ -38,13 +39,16 @@ public class ActionBarMenuItem extends ImageView { public abstract void onTextChanged(EditText editText); } - private LinearLayout popupLayout; + private ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout; private ActionBarMenu parentMenu; private ActionBarPopupWindow popupWindow; private ActionBar parentActionBar; private EditText searchField; private boolean isSearchField = false; private ActionBarMenuItemSearchListener listener; + private Rect rect = null; + private int[] location = null; + private View selectedMenuView = null; public ActionBarMenuItem(Context context, ActionBarMenu menu, ActionBar actionBar, int background) { super(context); @@ -65,11 +69,82 @@ public class ActionBarMenuItem extends ImageView { super(context, attrs, defStyleAttr); } + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_MOVE) { + if (hasSubMenu() && (popupWindow == null || popupWindow != null && !popupWindow.isShowing())) { + if (event.getY() > getHeight()) { + if (getParent() != null) { + getParent().requestDisallowInterceptTouchEvent(true); + } + toggleSubMenu(); + return true; + } + } else if (popupWindow != null && popupWindow.isShowing()) { + getLocationOnScreen(location); + float x = event.getX() + location[0]; + float y = event.getY() + location[1]; + popupLayout.getLocationOnScreen(location); + x -= location[0]; + y -= location[1]; + selectedMenuView = null; + for (int a = 0; a < popupLayout.getChildCount(); a++) { + View child = popupLayout.getChildAt(a); + child.getHitRect(rect); + if ((Integer)child.getTag() < 100) { + if (!rect.contains((int)x, (int)y)) { + child.setSelected(false); + } else { + child.setSelected(true); + selectedMenuView = child; + } + } + } + } + } else if (popupWindow != null && popupWindow.isShowing() && event.getActionMasked() == MotionEvent.ACTION_UP) { + if (selectedMenuView != null) { + selectedMenuView.setSelected(false); + parentMenu.onItemClick((Integer) selectedMenuView.getTag()); + } + popupWindow.dismiss(); + } else { + if (selectedMenuView != null) { + selectedMenuView.setSelected(false); + selectedMenuView = null; + } + } + return super.onTouchEvent(event); + } + public void addSubItem(int id, String text, int icon) { if (popupLayout == null) { - popupLayout = new LinearLayout(getContext()); + rect = new Rect(); + location = new int[2]; + popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getContext()); popupLayout.setOrientation(LinearLayout.VERTICAL); popupLayout.setBackgroundResource(R.drawable.popup_fixed); + popupLayout.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (popupWindow != null && popupWindow.isShowing()) { + v.getHitRect(rect); + if (!rect.contains((int)event.getX(), (int)event.getY())) { + popupWindow.dismiss(); + } + } + } + return false; + } + }); + popupLayout.setDispatchKeyEventListener(new ActionBarPopupWindow.OnDispatchKeyEventListener() { + @Override + public void onDispatchKeyEvent(KeyEvent keyEvent) { + if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(); + } + } + }); } if (popupLayout.getChildCount() != 0) { View delimeter = new View(getContext()); @@ -121,7 +196,7 @@ public class ActionBarMenuItem extends ImageView { } if (popupWindow == null) { popupWindow = new ActionBarPopupWindow(popupLayout, FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT); - popupWindow.setBackgroundDrawable(new BitmapDrawable()); + //popupWindow.setBackgroundDrawable(new BitmapDrawable()); popupWindow.setOutsideTouchable(true); popupWindow.setClippingEnabled(true); popupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarPopupWindow.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarPopupWindow.java index d9ce0163..698ab7c3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarPopupWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/ActionBar/ActionBarPopupWindow.java @@ -12,8 +12,10 @@ package org.telegram.ui.Views.ActionBar; import android.content.Context; import android.util.AttributeSet; +import android.view.KeyEvent; import android.view.View; import android.view.ViewTreeObserver; +import android.widget.LinearLayout; import android.widget.PopupWindow; import java.lang.reflect.Field; @@ -41,6 +43,39 @@ public class ActionBarPopupWindow extends PopupWindow { private ViewTreeObserver.OnScrollChangedListener mSuperScrollListener; private ViewTreeObserver mViewTreeObserver; + public static interface OnDispatchKeyEventListener { + public void onDispatchKeyEvent(KeyEvent keyEvent); + } + + public static class ActionBarPopupWindowLayout extends LinearLayout { + + private OnDispatchKeyEventListener mOnDispatchKeyEventListener; + + public ActionBarPopupWindowLayout(Context context) { + super(context); + } + + public ActionBarPopupWindowLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ActionBarPopupWindowLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setDispatchKeyEventListener(OnDispatchKeyEventListener listener) { + mOnDispatchKeyEventListener = listener; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (mOnDispatchKeyEventListener != null) { + mOnDispatchKeyEventListener.onDispatchKeyEvent(event); + } + return super.dispatchKeyEvent(event); + } + } + public ActionBarPopupWindow() { super(); init(); @@ -98,7 +133,6 @@ public class ActionBarPopupWindow extends PopupWindow { } private void unregisterListener() { - // Don't do anything if we haven't managed to patch the super listener if (mSuperScrollListener != null && mViewTreeObserver != null) { if (mViewTreeObserver.isAlive()) { mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener); @@ -108,13 +142,8 @@ public class ActionBarPopupWindow extends PopupWindow { } private void registerListener(View anchor) { - // Don't do anything if we haven't managed to patch the super listener. - // And don't bother attaching the listener if the anchor view isn't - // attached. This means we'll only have to deal with the real VTO owned - // by the ViewRoot. if (mSuperScrollListener != null) { - ViewTreeObserver vto = (anchor.getWindowToken() != null) ? anchor.getViewTreeObserver() - : null; + ViewTreeObserver vto = (anchor.getWindowToken() != null) ? anchor.getViewTreeObserver() : null; if (vto != mViewTreeObserver) { if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) { mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/ColorPickerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/ColorPickerView.java new file mode 100644 index 00000000..50e3850c --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/ColorPickerView.java @@ -0,0 +1,383 @@ +/* + * Copyright 2012 Lars Werkman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.telegram.ui.Views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.SweepGradient; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import org.telegram.messenger.Utilities; + +public class ColorPickerView extends View { + + private static final String STATE_PARENT = "parent"; + private static final String STATE_ANGLE = "angle"; + private static final String STATE_OLD_COLOR = "color"; + private static final String STATE_SHOW_OLD_COLOR = "showColor"; + + private static final int[] COLORS = new int[] { 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000 }; + + private Paint mColorWheelPaint; + private Paint mPointerHaloPaint; + private Paint mPointerColor; + private int mColorWheelThickness; + private int mColorWheelRadius; + private int mPreferredColorWheelRadius; + private int mColorCenterRadius; + private int mPreferredColorCenterRadius; + private int mColorCenterHaloRadius; + private int mPreferredColorCenterHaloRadius; + private int mColorPointerRadius; + private int mColorPointerHaloRadius; + private RectF mColorWheelRectangle = new RectF(); + private RectF mCenterRectangle = new RectF(); + private boolean mUserIsMovingPointer = false; + private int mCenterOldColor; + private boolean mShowCenterOldColor; + private int mCenterNewColor; + private float mTranslationOffset; + private float mSlopX; + private float mSlopY; + private float mAngle; + private Paint mCenterOldPaint; + private Paint mCenterNewPaint; + private Paint mCenterHaloPaint; + private float[] mHSV = new float[3]; + + private OnColorChangedListener onColorChangedListener; + private OnColorSelectedListener onColorSelectedListener; + + private int oldChangedListenerColor; + private int oldSelectedListenerColor; + + public ColorPickerView(Context context) { + super(context); + init(null, 0); + } + + public ColorPickerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public ColorPickerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs, defStyle); + } + + public interface OnColorChangedListener { + public void onColorChanged(int color); + } + + public interface OnColorSelectedListener { + public void onColorSelected(int color); + } + + public void setOnColorChangedListener(OnColorChangedListener listener) { + this.onColorChangedListener = listener; + } + + public void setOnColorSelectedListener(OnColorSelectedListener listener) { + this.onColorSelectedListener = listener; + } + + private void init(AttributeSet attrs, int defStyle) { + mColorWheelThickness = Utilities.dp(8); + mColorWheelRadius = Utilities.dp(124); + mPreferredColorWheelRadius = mColorWheelRadius; + mColorCenterRadius = Utilities.dp(54); + mPreferredColorCenterRadius = mColorCenterRadius; + mColorCenterHaloRadius = Utilities.dp(60); + mPreferredColorCenterHaloRadius = mColorCenterHaloRadius; + mColorPointerRadius = Utilities.dp(14); + mColorPointerHaloRadius = Utilities.dp(18); + + mAngle = (float) (-Math.PI / 2); + + Shader s = new SweepGradient(0, 0, COLORS, null); + + mColorWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mColorWheelPaint.setShader(s); + mColorWheelPaint.setStyle(Paint.Style.STROKE); + mColorWheelPaint.setStrokeWidth(mColorWheelThickness); + + mPointerHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPointerHaloPaint.setColor(Color.BLACK); + mPointerHaloPaint.setAlpha(0x50); + + mPointerColor = new Paint(Paint.ANTI_ALIAS_FLAG); + mPointerColor.setColor(calculateColor(mAngle)); + + mCenterNewPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mCenterNewPaint.setColor(calculateColor(mAngle)); + mCenterNewPaint.setStyle(Paint.Style.FILL); + + mCenterOldPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mCenterOldPaint.setColor(calculateColor(mAngle)); + mCenterOldPaint.setStyle(Paint.Style.FILL); + + mCenterHaloPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mCenterHaloPaint.setColor(Color.BLACK); + mCenterHaloPaint.setAlpha(0x00); + + mCenterNewColor = calculateColor(mAngle); + mCenterOldColor = calculateColor(mAngle); + mShowCenterOldColor = true; + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.translate(mTranslationOffset, mTranslationOffset); + canvas.drawOval(mColorWheelRectangle, mColorWheelPaint); + + float[] pointerPosition = calculatePointerPosition(mAngle); + + canvas.drawCircle(pointerPosition[0], pointerPosition[1], mColorPointerHaloRadius, mPointerHaloPaint); + canvas.drawCircle(pointerPosition[0], pointerPosition[1], mColorPointerRadius, mPointerColor); + canvas.drawCircle(0, 0, mColorCenterHaloRadius, mCenterHaloPaint); + + if (mShowCenterOldColor) { + canvas.drawArc(mCenterRectangle, 90, 180, true, mCenterOldPaint); + canvas.drawArc(mCenterRectangle, 270, 180, true, mCenterNewPaint); + } else { + canvas.drawArc(mCenterRectangle, 0, 360, true, mCenterNewPaint); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int intrinsicSize = 2 * (mPreferredColorWheelRadius + mColorPointerHaloRadius); + + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + int width; + int height; + + if (widthMode == MeasureSpec.EXACTLY) { + width = widthSize; + } else if (widthMode == MeasureSpec.AT_MOST) { + width = Math.min(intrinsicSize, widthSize); + } else { + width = intrinsicSize; + } + + if (heightMode == MeasureSpec.EXACTLY) { + height = heightSize; + } else if (heightMode == MeasureSpec.AT_MOST) { + height = Math.min(intrinsicSize, heightSize); + } else { + height = intrinsicSize; + } + + int min = Math.min(width, height); + setMeasuredDimension(min, min); + mTranslationOffset = min * 0.5f; + + mColorWheelRadius = min / 2 - mColorWheelThickness - mColorPointerHaloRadius; + mColorWheelRectangle.set(-mColorWheelRadius, -mColorWheelRadius, mColorWheelRadius, mColorWheelRadius); + + mColorCenterRadius = (int) ((float) mPreferredColorCenterRadius * ((float) mColorWheelRadius / (float) mPreferredColorWheelRadius)); + mColorCenterHaloRadius = (int) ((float) mPreferredColorCenterHaloRadius * ((float) mColorWheelRadius / (float) mPreferredColorWheelRadius)); + mCenterRectangle.set(-mColorCenterRadius, -mColorCenterRadius, mColorCenterRadius, mColorCenterRadius); + } + + private int ave(int s, int d, float p) { + return s + java.lang.Math.round(p * (d - s)); + } + + private int calculateColor(float angle) { + float unit = (float) (angle / (2 * Math.PI)); + if (unit < 0) { + unit += 1; + } + + if (unit <= 0) { + return COLORS[0]; + } + if (unit >= 1) { + return COLORS[COLORS.length - 1]; + } + + float p = unit * (COLORS.length - 1); + int i = (int) p; + p -= i; + + int c0 = COLORS[i]; + int c1 = COLORS[i + 1]; + int a = ave(Color.alpha(c0), Color.alpha(c1), p); + int r = ave(Color.red(c0), Color.red(c1), p); + int g = ave(Color.green(c0), Color.green(c1), p); + int b = ave(Color.blue(c0), Color.blue(c1), p); + + return Color.argb(a, r, g, b); + } + + public int getColor() { + return mCenterNewColor; + } + + public void setColor(int color) { + mAngle = colorToAngle(color); + mPointerColor.setColor(calculateColor(mAngle)); + mCenterNewPaint.setColor(calculateColor(mAngle)); + + invalidate(); + } + + private float colorToAngle(int color) { + float[] colors = new float[3]; + Color.colorToHSV(color, colors); + + return (float) Math.toRadians(-colors[0]); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + getParent().requestDisallowInterceptTouchEvent(true); + + float x = event.getX() - mTranslationOffset; + float y = event.getY() - mTranslationOffset; + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + float[] pointerPosition = calculatePointerPosition(mAngle); + if (x >= (pointerPosition[0] - mColorPointerHaloRadius) && x <= (pointerPosition[0] + mColorPointerHaloRadius) && y >= (pointerPosition[1] - mColorPointerHaloRadius) && y <= (pointerPosition[1] + mColorPointerHaloRadius)) { + mSlopX = x - pointerPosition[0]; + mSlopY = y - pointerPosition[1]; + mUserIsMovingPointer = true; + invalidate(); + } else if (x >= -mColorCenterRadius && x <= mColorCenterRadius && y >= -mColorCenterRadius && y <= mColorCenterRadius && mShowCenterOldColor) { + mCenterHaloPaint.setAlpha(0x50); + setColor(getOldCenterColor()); + invalidate(); + } else { + getParent().requestDisallowInterceptTouchEvent(false); + return false; + } + break; + case MotionEvent.ACTION_MOVE: + if (mUserIsMovingPointer) { + mAngle = (float) java.lang.Math.atan2(y - mSlopY, x - mSlopX); + mPointerColor.setColor(calculateColor(mAngle)); + setNewCenterColor(mCenterNewColor = calculateColor(mAngle)); + invalidate(); + } else { + getParent().requestDisallowInterceptTouchEvent(false); + return false; + } + break; + case MotionEvent.ACTION_UP: + mUserIsMovingPointer = false; + mCenterHaloPaint.setAlpha(0x00); + + if (onColorSelectedListener != null && mCenterNewColor != oldSelectedListenerColor) { + onColorSelectedListener.onColorSelected(mCenterNewColor); + oldSelectedListenerColor = mCenterNewColor; + } + + invalidate(); + break; + case MotionEvent.ACTION_CANCEL: + if (onColorSelectedListener != null && mCenterNewColor != oldSelectedListenerColor) { + onColorSelectedListener.onColorSelected(mCenterNewColor); + oldSelectedListenerColor = mCenterNewColor; + } + break; + } + return true; + } + + private float[] calculatePointerPosition(float angle) { + float x = (float) (mColorWheelRadius * Math.cos(angle)); + float y = (float) (mColorWheelRadius * Math.sin(angle)); + + return new float[] { x, y }; + } + + public void setNewCenterColor(int color) { + mCenterNewColor = color; + mCenterNewPaint.setColor(color); + if (mCenterOldColor == 0) { + mCenterOldColor = color; + mCenterOldPaint.setColor(color); + } + if (onColorChangedListener != null && color != oldChangedListenerColor ) { + onColorChangedListener.onColorChanged(color); + oldChangedListenerColor = color; + } + invalidate(); + } + + public void setOldCenterColor(int color) { + mCenterOldColor = color; + mCenterOldPaint.setColor(color); + invalidate(); + } + + public int getOldCenterColor() { + return mCenterOldColor; + } + + public void setShowOldCenterColor(boolean show) { + mShowCenterOldColor = show; + invalidate(); + } + + public boolean getShowOldCenterColor() { + return mShowCenterOldColor; + } + + @Override + protected Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + + Bundle state = new Bundle(); + state.putParcelable(STATE_PARENT, superState); + state.putFloat(STATE_ANGLE, mAngle); + state.putInt(STATE_OLD_COLOR, mCenterOldColor); + state.putBoolean(STATE_SHOW_OLD_COLOR, mShowCenterOldColor); + + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + Bundle savedState = (Bundle) state; + + Parcelable superState = savedState.getParcelable(STATE_PARENT); + super.onRestoreInstanceState(superState); + + mAngle = savedState.getFloat(STATE_ANGLE); + setOldCenterColor(savedState.getInt(STATE_OLD_COLOR)); + mShowCenterOldColor = savedState.getBoolean(STATE_SHOW_OLD_COLOR); + int currentColor = calculateColor(mAngle); + mPointerColor.setColor(currentColor); + setNewCenterColor(currentColor); + } +} diff --git a/TMessagesProj/src/main/res/layout-ar/settings_row_color_layout.xml b/TMessagesProj/src/main/res/layout-ar/settings_row_color_layout.xml new file mode 100644 index 00000000..389e955b --- /dev/null +++ b/TMessagesProj/src/main/res/layout-ar/settings_row_color_layout.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/settings_color_dialog_layout.xml b/TMessagesProj/src/main/res/layout/settings_color_dialog_layout.xml new file mode 100644 index 00000000..07548074 --- /dev/null +++ b/TMessagesProj/src/main/res/layout/settings_color_dialog_layout.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/settings_row_color_layout.xml b/TMessagesProj/src/main/res/layout/settings_row_color_layout.xml new file mode 100644 index 00000000..c0bf5112 --- /dev/null +++ b/TMessagesProj/src/main/res/layout/settings_row_color_layout.xml @@ -0,0 +1,33 @@ + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 9ccfad3a..69397359 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -273,6 +273,7 @@ بواسطة WiFi فقط الاسم الأول اسم العائلة + LED Color لا توجد وسائط بعد diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index ec96fd4f..7abcfc20 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -273,6 +273,7 @@ nur über WLAN Vorname Nachname + LED Color Noch keine geteilten Medien vorhanden diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 648fd6a7..96d29256 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -273,6 +273,7 @@ Sólo vía WiFi Nombre Apellido + LED Color No hay fotos ni vídeos compartidos aún diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 0b3a2942..fcb8cf81 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -273,6 +273,7 @@ Solo tramite WiFi Nome Cognome + LED Color Nessun media condiviso diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index 62e8b796..abe73183 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -273,6 +273,7 @@ Alleen via WiFi Voornaam Achternaam + LED Color Nog geen media gedeeld diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index 78e1cbb6..40fcdb90 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -273,6 +273,7 @@ Unicamente com WiFi Primeiro nome Sobrenome + LED Color Ainda não há mídia compartilhada diff --git a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml index faf698f0..110e2c47 100644 --- a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml @@ -273,6 +273,7 @@ Unicamente com WiFi Nome Apelidos + LED Color Ainda não há multimédia partilhado diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index d5cd5fee..8030a4c6 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -273,6 +273,7 @@ Via WiFi only First name Last name + LED Color No shared media yet diff --git a/Tools/EmojiTextureMaker.m b/Tools/EmojiTextureMaker.m new file mode 100644 index 00000000..0d13bacb --- /dev/null +++ b/Tools/EmojiTextureMaker.m @@ -0,0 +1,263 @@ +// +// AppDelegate.m +// texmaker +// +// Created by Nikolay Kudashov on 16.04.14. +// Copyright (c) 2014 telegram. All rights reserved. +// + +#import "AppDelegate.h" + +@implementation AppDelegate + +- (NSData *)reversedData:(NSData *)data { + NSData *myData = data; + + NSMutableData *reversedData = [[NSMutableData alloc] init]; + const char *bytes = [myData bytes]; + int datalength = (int)[myData length]; + + for (int i = datalength - 1; i >= 0; i--) { + [reversedData appendBytes:&bytes[i] length:1]; + } + + return reversedData; +} + +void CGImageWriteToFile(CGImageRef image, NSString *path) { + CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:path]; + CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL); + CGImageDestinationAddImage(destination, image, nil); + if (!CGImageDestinationFinalize(destination)) { + NSLog(@"Failed to write image to %@", path); + } + CFRelease(destination); +} + +NSInteger sortFunc(id obj1, id obj2, void *context) { + NSNumber *ov1 = (NSNumber *)obj1; + NSNumber *ov2 = (NSNumber *)obj2; + return [ov1 compare:ov2]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + NSArray *array2 = @[ + @[@0x00000000D83DDE04, @0x00000000D83DDE03, @0x00000000D83DDE00, @0x00000000D83DDE0A, @0x000000000000263A, @0x00000000D83DDE09, @0x00000000D83DDE0D, + @0x00000000D83DDE18, @0x00000000D83DDE1A, @0x00000000D83DDE17, @0x00000000D83DDE19, @0x00000000D83DDE1C, @0x00000000D83DDE1D, @0x00000000D83DDE1B, + @0x00000000D83DDE33, @0x00000000D83DDE01, @0x00000000D83DDE14, @0x00000000D83DDE0C, @0x00000000D83DDE12, @0x00000000D83DDE1E, @0x00000000D83DDE23, + @0x00000000D83DDE22, @0x00000000D83DDE02, @0x00000000D83DDE2D, @0x00000000D83DDE2A, @0x00000000D83DDE25, @0x00000000D83DDE30, @0x00000000D83DDE05, + @0x00000000D83DDE13, @0x00000000D83DDE29, @0x00000000D83DDE2B, @0x00000000D83DDE28, @0x00000000D83DDE31, @0x00000000D83DDE20, @0x00000000D83DDE21, + @0x00000000D83DDE24, @0x00000000D83DDE16, @0x00000000D83DDE06, @0x00000000D83DDE0B, @0x00000000D83DDE37, @0x00000000D83DDE0E, @0x00000000D83DDE34, + @0x00000000D83DDE35, @0x00000000D83DDE32, @0x00000000D83DDE1F, @0x00000000D83DDE26, @0x00000000D83DDE27, @0x00000000D83DDE08, @0x00000000D83DDC7F, + @0x00000000D83DDE2E, @0x00000000D83DDE2C, @0x00000000D83DDE10, @0x00000000D83DDE15, @0x00000000D83DDE2F, @0x00000000D83DDE36, @0x00000000D83DDE07, + @0x00000000D83DDE0F, @0x00000000D83DDE11, @0x00000000D83DDC72, @0x00000000D83DDC73, @0x00000000D83DDC6E, @0x00000000D83DDC77, @0x00000000D83DDC82, + @0x00000000D83DDC76, @0x00000000D83DDC66, @0x00000000D83DDC67, @0x00000000D83DDC68, @0x00000000D83DDC69, @0x00000000D83DDC74, @0x00000000D83DDC75, + @0x00000000D83DDC71, @0x00000000D83DDC7C, @0x00000000D83DDC78, @0x00000000D83DDE3A, @0x00000000D83DDE38, @0x00000000D83DDE3B, @0x00000000D83DDE3D, + @0x00000000D83DDE3C, @0x00000000D83DDE40, @0x00000000D83DDE3F, @0x00000000D83DDE39, @0x00000000D83DDE3E, @0x00000000D83DDC79, @0x00000000D83DDC7A, + @0x00000000D83DDE48, @0x00000000D83DDE49, @0x00000000D83DDE4A, @0x00000000D83DDC80, @0x00000000D83DDC7D, @0x00000000D83DDCA9, @0x00000000D83DDD25, + @0x0000000000002728, @0x00000000D83CDF1F, @0x00000000D83DDCAB, @0x00000000D83DDCA5, @0x00000000D83DDCA2, @0x00000000D83DDCA6, @0x00000000D83DDCA7, + @0x00000000D83DDCA4, @0x00000000D83DDCA8, @0x00000000D83DDC42, @0x00000000D83DDC40, @0x00000000D83DDC43, @0x00000000D83DDC45, @0x00000000D83DDC44, + @0x00000000D83DDC4D, @0x00000000D83DDC4E, @0x00000000D83DDC4C, @0x00000000D83DDC4A, @0x000000000000270A, @0x000000000000270C, @0x00000000D83DDC4B, + @0x000000000000270B, @0x00000000D83DDC50, @0x00000000D83DDC46, @0x00000000D83DDC47, @0x00000000D83DDC49, @0x00000000D83DDC48, @0x00000000D83DDE4C, + @0x00000000D83DDE4F, @0x000000000000261D, @0x00000000D83DDC4F, @0x00000000D83DDCAA, @0x00000000D83DDEB6, @0x00000000D83CDFC3, @0x00000000D83DDC83, + @0x00000000D83DDC6B, @0x00000000D83DDC6A, @0x00000000D83DDC6C, @0x00000000D83DDC6D, @0x00000000D83DDC8F, @0x00000000D83DDC91, @0x00000000D83DDC6F, + @0x00000000D83DDE46, @0x00000000D83DDE45, @0x00000000D83DDC81, @0x00000000D83DDE4B, @0x00000000D83DDC86, @0x00000000D83DDC87, @0x00000000D83DDC85, + @0x00000000D83DDC70, @0x00000000D83DDE4E, @0x00000000D83DDE4D, @0x00000000D83DDE47, @0x00000000D83CDFA9, @0x00000000D83DDC51, @0x00000000D83DDC52, + @0x00000000D83DDC5F, @0x00000000D83DDC5E, @0x00000000D83DDC61, @0x00000000D83DDC60, @0x00000000D83DDC62, @0x00000000D83DDC55, @0x00000000D83DDC54, + @0x00000000D83DDC5A, @0x00000000D83DDC57, @0x00000000D83CDFBD, @0x00000000D83DDC56, @0x00000000D83DDC58, @0x00000000D83DDC59, @0x00000000D83DDCBC, + @0x00000000D83DDC5C, @0x00000000D83DDC5D, @0x00000000D83DDC5B, @0x00000000D83DDC53, @0x00000000D83CDF80, @0x00000000D83CDF02, @0x00000000D83DDC84, + @0x00000000D83DDC9B, @0x00000000D83DDC99, @0x00000000D83DDC9C, @0x00000000D83DDC9A, @0x0000000000002764, @0x00000000D83DDC94, @0x00000000D83DDC97, + @0x00000000D83DDC93, @0x00000000D83DDC95, @0x00000000D83DDC96, @0x00000000D83DDC9E, @0x00000000D83DDC98, @0x00000000D83DDC8C, @0x00000000D83DDC8B, + @0x00000000D83DDC8D, @0x00000000D83DDC8E, @0x00000000D83DDC64, @0x00000000D83DDC65, @0x00000000D83DDCAC, @0x00000000D83DDC63, @0x00000000D83DDCAD], + + @[@0x00000000D83DDC36, @0x00000000D83DDC3A, @0x00000000D83DDC31, @0x00000000D83DDC2D, @0x00000000D83DDC39, @0x00000000D83DDC30, @0x00000000D83DDC38, + @0x00000000D83DDC2F, @0x00000000D83DDC28, @0x00000000D83DDC3B, @0x00000000D83DDC37, @0x00000000D83DDC3D, @0x00000000D83DDC2E, @0x00000000D83DDC17, + @0x00000000D83DDC35, @0x00000000D83DDC12, @0x00000000D83DDC34, @0x00000000D83DDC11, @0x00000000D83DDC18, @0x00000000D83DDC3C, @0x00000000D83DDC27, + @0x00000000D83DDC26, @0x00000000D83DDC24, @0x00000000D83DDC25, @0x00000000D83DDC23, @0x00000000D83DDC14, @0x00000000D83DDC0D, @0x00000000D83DDC22, + @0x00000000D83DDC1B, @0x00000000D83DDC1D, @0x00000000D83DDC1C, @0x00000000D83DDC1E, @0x00000000D83DDC0C, @0x00000000D83DDC19, @0x00000000D83DDC1A, + @0x00000000D83DDC20, @0x00000000D83DDC1F, @0x00000000D83DDC2C, @0x00000000D83DDC33, @0x00000000D83DDC0B, @0x00000000D83DDC04, @0x00000000D83DDC0F, + @0x00000000D83DDC00, @0x00000000D83DDC03, @0x00000000D83DDC05, @0x00000000D83DDC07, @0x00000000D83DDC09, @0x00000000D83DDC0E, @0x00000000D83DDC10, + @0x00000000D83DDC13, @0x00000000D83DDC15, @0x00000000D83DDC16, @0x00000000D83DDC01, @0x00000000D83DDC02, @0x00000000D83DDC32, @0x00000000D83DDC21, + @0x00000000D83DDC0A, @0x00000000D83DDC2B, @0x00000000D83DDC2A, @0x00000000D83DDC06, @0x00000000D83DDC08, @0x00000000D83DDC29, @0x00000000D83DDC3E, + @0x00000000D83DDC90, @0x00000000D83CDF38, @0x00000000D83CDF37, @0x00000000D83CDF40, @0x00000000D83CDF39, @0x00000000D83CDF3B, @0x00000000D83CDF3A, + @0x00000000D83CDF41, @0x00000000D83CDF43, @0x00000000D83CDF42, @0x00000000D83CDF3F, @0x00000000D83CDF3E, @0x00000000D83CDF44, @0x00000000D83CDF35, + @0x00000000D83CDF34, @0x00000000D83CDF32, @0x00000000D83CDF33, @0x00000000D83CDF30, @0x00000000D83CDF31, @0x00000000D83CDF3C, @0x00000000D83CDF10, + @0x00000000D83CDF1E, @0x00000000D83CDF1D, @0x00000000D83CDF1A, @0x00000000D83CDF11, @0x00000000D83CDF12, @0x00000000D83CDF13, @0x00000000D83CDF14, + @0x00000000D83CDF15, @0x00000000D83CDF16, @0x00000000D83CDF17, @0x00000000D83CDF18, @0x00000000D83CDF1C, @0x00000000D83CDF1B, @0x00000000D83CDF19, + @0x00000000D83CDF0D, @0x00000000D83CDF0E, @0x00000000D83CDF0F, @0x00000000D83CDF0B, @0x00000000D83CDF0C, @0x00000000D83CDF20, @0x0000000000002B50, + @0x0000000000002600, @0x00000000000026C5, @0x0000000000002601, @0x00000000000026A1, @0x0000000000002614, @0x0000000000002744, @0x00000000000026C4, + @0x00000000D83CDF00, @0x00000000D83CDF01, @0x00000000D83CDF08, @0x00000000D83CDF0A], + + @[@0x00000000D83CDF8D, @0x00000000D83DDC9D, @0x00000000D83CDF8E, @0x00000000D83CDF92, @0x00000000D83CDF93, @0x00000000D83CDF8F, @0x00000000D83CDF86, @0x00000000D83CDF87, + @0x00000000D83CDF90, @0x00000000D83CDF91, @0x00000000D83CDF83, @0x00000000D83DDC7B, @0x00000000D83CDF85, @0x00000000D83CDF84, @0x00000000D83CDF81, + @0x00000000D83CDF8B, @0x00000000D83CDF89, @0x00000000D83CDF8A, @0x00000000D83CDF88, @0x00000000D83CDF8C, @0x00000000D83DDD2E, @0x00000000D83CDFA5, + @0x00000000D83DDCF7, @0x00000000D83DDCF9, @0x00000000D83DDCFC, @0x00000000D83DDCBF, @0x00000000D83DDCC0, @0x00000000D83DDCBD, @0x00000000D83DDCBE, + @0x00000000D83DDCBB, @0x00000000D83DDCF1, @0x000000000000260E, @0x00000000D83DDCDE, @0x00000000D83DDCDF, @0x00000000D83DDCE0, @0x00000000D83DDCE1, + @0x00000000D83DDCFA, @0x00000000D83DDCFB, @0x00000000D83DDD0A, @0x00000000D83DDD09, @0x00000000D83DDD08, @0x00000000D83DDD07, @0x00000000D83DDD14, @0x00000000D83DDD15, + @0x00000000D83DDCE2, @0x00000000D83DDCE3, @0x00000000000023F3, @0x000000000000231B, @0x00000000000023F0, @0x000000000000231A, + @0x00000000D83DDD13, @0x00000000D83DDD12, @0x00000000D83DDD0F, @0x00000000D83DDD10, @0x00000000D83DDD11, @0x00000000D83DDD0E, @0x00000000D83DDCA1, + @0x00000000D83DDD26, @0x00000000D83DDD06, @0x00000000D83DDD05, @0x00000000D83DDD0C, @0x00000000D83DDD0B, @0x00000000D83DDD0D, @0x00000000D83DDEC1, @0x00000000D83DDEC0, + @0x00000000D83DDEBF, @0x00000000D83DDEBD, @0x00000000D83DDD27, @0x00000000D83DDD29, @0x00000000D83DDD28, @0x00000000D83DDEAA, @0x00000000D83DDEAC, + @0x00000000D83DDCA3, @0x00000000D83DDD2B, @0x00000000D83DDD2A, @0x00000000D83DDC8A, @0x00000000D83DDC89, @0x00000000D83DDCB0, @0x00000000D83DDCB4, + @0x00000000D83DDCB5, @0x00000000D83DDCB7, @0x00000000D83DDCB6, @0x00000000D83DDCB3, @0x00000000D83DDCB8, @0x00000000D83DDCF2, @0x00000000D83DDCE7, + @0x00000000D83DDCE5, @0x00000000D83DDCE4, @0x0000000000002709, @0x00000000D83DDCE9, @0x00000000D83DDCE8, @0x00000000D83DDCEF, @0x00000000D83DDCEB, + @0x00000000D83DDCEA, @0x00000000D83DDCEC, @0x00000000D83DDCED, @0x00000000D83DDCEE, @0x00000000D83DDCE6, @0x00000000D83DDCDD, @0x00000000D83DDCC4, + @0x00000000D83DDCC3, @0x00000000D83DDCD1, @0x00000000D83DDCCA, @0x00000000D83DDCC8, @0x00000000D83DDCC9, @0x00000000D83DDCDC, @0x00000000D83DDCCB, + @0x00000000D83DDCC5, @0x00000000D83DDCC6, @0x00000000D83DDCC7, @0x00000000D83DDCC1, @0x00000000D83DDCC2, @0x0000000000002702, @0x00000000D83DDCCC, + @0x00000000D83DDCCE, @0x0000000000002712, @0x000000000000270F, @0x00000000D83DDCCF, @0x00000000D83DDCD0, @0x00000000D83DDCD5, @0x00000000D83DDCD7, + @0x00000000D83DDCD8, @0x00000000D83DDCD9, @0x00000000D83DDCD3, @0x00000000D83DDCD4, @0x00000000D83DDCD2, @0x00000000D83DDCDA, @0x00000000D83DDCD6, + @0x00000000D83DDD16, @0x00000000D83DDCDB, @0x00000000D83DDD2C, @0x00000000D83DDD2D, @0x00000000D83DDCF0, @0x00000000D83CDFA8, @0x00000000D83CDFAC, + @0x00000000D83CDFA4, @0x00000000D83CDFA7, @0x00000000D83CDFBC, @0x00000000D83CDFB5, @0x00000000D83CDFB6, @0x00000000D83CDFB9, @0x00000000D83CDFBB, + @0x00000000D83CDFBA, @0x00000000D83CDFB7, @0x00000000D83CDFB8, @0x00000000D83DDC7E, @0x00000000D83CDFAE, @0x00000000D83CDCCF, @0x00000000D83CDFB4, + @0x00000000D83CDC04, @0x00000000D83CDFB2, @0x00000000D83CDFAF, @0x00000000D83CDFC8, @0x00000000D83CDFC0, @0x00000000000026BD, @0x00000000000026BE, + @0x00000000D83CDFBE, @0x00000000D83CDFB1, @0x00000000D83CDFC9, @0x00000000D83CDFB3, @0x00000000000026F3, @0x00000000D83DDEB5, @0x00000000D83DDEB4, + @0x00000000D83CDFC1, @0x00000000D83CDFC7, @0x00000000D83CDFC6, @0x00000000D83CDFBF, @0x00000000D83CDFC2, @0x00000000D83CDFCA, @0x00000000D83CDFC4, + @0x00000000D83CDFA3, @0x0000000000002615, @0x00000000D83CDF75, @0x00000000D83CDF76, @0x00000000D83CDF7C, @0x00000000D83CDF7A, @0x00000000D83CDF7B, + @0x00000000D83CDF78, @0x00000000D83CDF79, @0x00000000D83CDF77, @0x00000000D83CDF74, @0x00000000D83CDF55, @0x00000000D83CDF54, @0x00000000D83CDF5F, + @0x00000000D83CDF57, @0x00000000D83CDF56, @0x00000000D83CDF5D, @0x00000000D83CDF5B, @0x00000000D83CDF64, @0x00000000D83CDF71, @0x00000000D83CDF63, + @0x00000000D83CDF65, @0x00000000D83CDF59, @0x00000000D83CDF58, @0x00000000D83CDF5A, @0x00000000D83CDF5C, @0x00000000D83CDF72, @0x00000000D83CDF62, + @0x00000000D83CDF61, @0x00000000D83CDF73, @0x00000000D83CDF5E, @0x00000000D83CDF69, @0x00000000D83CDF6E, @0x00000000D83CDF66, @0x00000000D83CDF68, + @0x00000000D83CDF67, @0x00000000D83CDF82, @0x00000000D83CDF70, @0x00000000D83CDF6A, @0x00000000D83CDF6B, @0x00000000D83CDF6C, @0x00000000D83CDF6D, + @0x00000000D83CDF6F, @0x00000000D83CDF4E, @0x00000000D83CDF4F, @0x00000000D83CDF4A, @0x00000000D83CDF4B, @0x00000000D83CDF52, @0x00000000D83CDF47, + @0x00000000D83CDF49, @0x00000000D83CDF53, @0x00000000D83CDF51, @0x00000000D83CDF48, @0x00000000D83CDF4C, @0x00000000D83CDF50, @0x00000000D83CDF4D, + @0x00000000D83CDF60, @0x00000000D83CDF46, @0x00000000D83CDF45, @0x00000000D83CDF3D], + + @[@0x00000000D83CDFE0, @0x00000000D83CDFE1, @0x00000000D83CDFEB, @0x00000000D83CDFE2, @0x00000000D83CDFE3, @0x00000000D83CDFE5, @0x00000000D83CDFE6, @0x00000000D83CDFEA, + @0x00000000D83CDFE9, @0x00000000D83CDFE8, @0x00000000D83DDC92, @0x00000000000026EA, @0x00000000D83CDFEC, @0x00000000D83CDFE4, @0x00000000D83CDF07, + @0x00000000D83CDF06, @0x00000000D83CDFEF, @0x00000000D83CDFF0, @0x00000000000026FA, @0x00000000D83CDFED, @0x00000000D83DDDFC, @0x00000000D83DDDFE, + @0x00000000D83DDDFB, @0x00000000D83CDF04, @0x00000000D83CDF05, @0x00000000D83CDF03, @0x00000000D83DDDFD, @0x00000000D83CDF09, @0x00000000D83CDFA0, + @0x00000000D83CDFA1, @0x00000000000026F2, @0x00000000D83CDFA2, @0x00000000D83DDEA2, @0x00000000000026F5, @0x00000000D83DDEA4, @0x00000000D83DDEA3, + @0x0000000000002693, @0x00000000D83DDE80, @0x0000000000002708, @0x00000000D83DDCBA, @0x00000000D83DDE81, @0x00000000D83DDE82, @0x00000000D83DDE8A, + @0x00000000D83DDE89, @0x00000000D83DDE9E, @0x00000000D83DDE86, @0x00000000D83DDE84, @0x00000000D83DDE85, @0x00000000D83DDE88, @0x00000000D83DDE87, + @0x00000000D83DDE9D, @0x00000000D83DDE8B, @0x00000000D83DDE83, @0x00000000D83DDE8E, @0x00000000D83DDE8C, @0x00000000D83DDE8D, @0x00000000D83DDE99, + @0x00000000D83DDE98, @0x00000000D83DDE97, @0x00000000D83DDE95, @0x00000000D83DDE96, @0x00000000D83DDE9B, @0x00000000D83DDE9A, @0x00000000D83DDEA8, + @0x00000000D83DDE93, @0x00000000D83DDE94, @0x00000000D83DDE92, @0x00000000D83DDE91, @0x00000000D83DDE90, @0x00000000D83DDEB2, @0x00000000D83DDEA1, + @0x00000000D83DDE9F, @0x00000000D83DDEA0, @0x00000000D83DDE9C, @0x00000000D83DDC88, @0x00000000D83DDE8F, @0x00000000D83CDFAB, @0x00000000D83DDEA6, + @0x00000000D83DDEA5, @0x00000000000026A0, @0x00000000D83DDEA7, @0x00000000D83DDD30, @0x00000000000026FD, @0x00000000D83CDFEE, @0x00000000D83CDFB0, + @0x0000000000002668, @0x00000000D83DDDFF, @0x00000000D83CDFAA, @0x00000000D83CDFAD, @0x00000000D83DDCCD, @0x00000000D83DDEA9, @0xD83CDDEFD83CDDF5, + @0xD83CDDF0D83CDDF7, @0xD83CDDE9D83CDDEA, @0xD83CDDE8D83CDDF3, @0xD83CDDFAD83CDDF8, @0xD83CDDEBD83CDDF7, @0xD83CDDEAD83CDDF8, @0xD83CDDEED83CDDF9, + @0xD83CDDF7D83CDDFA, @0xD83CDDECD83CDDE7], + + @[@0x00000000003120E3, @0x00000000003220E3, @0x00000000003320E3, @0x00000000003420E3, @0x00000000003520E3, @0x00000000003620E3, @0x00000000003720E3, @0x00000000003820E3, + @0x00000000003920E3, @0x00000000003020E3, @0x00000000D83DDD1F, @0x00000000D83DDD22, @0x00000000002320E3, @0x00000000D83DDD23, @0x0000000000002B06, + @0x0000000000002B07, @0x0000000000002B05, @0x00000000000027A1, @0x00000000D83DDD20, @0x00000000D83DDD21, @0x00000000D83DDD24, @0x0000000000002197, + @0x0000000000002196, @0x0000000000002198, @0x0000000000002199, @0x0000000000002194, @0x0000000000002195, @0x00000000D83DDD04, @0x00000000000025C0, + @0x00000000000025B6, @0x00000000D83DDD3C, @0x00000000D83DDD3D, @0x00000000000021A9, @0x00000000000021AA, @0x0000000000002139, @0x00000000000023EA, + @0x00000000000023E9, @0x00000000000023EB, @0x00000000000023EC, @0x0000000000002935, @0x0000000000002934, @0x00000000D83CDD97, @0x00000000D83DDD00, + @0x00000000D83DDD01, @0x00000000D83DDD02, @0x00000000D83CDD95, @0x00000000D83CDD99, @0x00000000D83CDD92, @0x00000000D83CDD93, @0x00000000D83CDD96, + @0x00000000D83DDCF6, @0x00000000D83CDFA6, @0x00000000D83CDE01, @0x00000000D83CDE2F, @0x00000000D83CDE33, @0x00000000D83CDE35, @0x00000000D83CDE32, + @0x00000000D83CDE34, @0x00000000D83CDE50, @0x00000000D83CDE39, @0x00000000D83CDE3A, @0x00000000D83CDE36, @0x00000000D83CDE1A, + @0x00000000D83DDEBB, @0x00000000D83DDEB9, @0x00000000D83DDEBA, @0x00000000D83DDEBC, @0x00000000D83DDEBE, @0x00000000D83DDEB0, @0x00000000D83DDEAE, + @0x00000000D83CDD7F, @0x000000000000267F, @0x00000000D83DDEAD, @0x00000000D83CDE37, @0x00000000D83CDE38, @0x00000000D83CDE02, @0x00000000000024C2, + @0x00000000D83DDEC2, @0x00000000D83DDEC4, @0x00000000D83DDEC5, @0x00000000D83DDEC3, + @0x00000000D83CDE51, @0x0000000000003299, @0x0000000000003297, @0x00000000D83CDD91, @0x00000000D83CDD98, @0x00000000D83CDD94, @0x00000000D83DDEAB, + @0x00000000D83DDD1E, @0x00000000D83DDCF5, @0x00000000D83DDEAF, @0x00000000D83DDEB1, @0x00000000D83DDEB3, @0x00000000D83DDEB7, @0x00000000D83DDEB8, + @0x00000000000026D4, @0x0000000000002733, @0x0000000000002747, @0x000000000000274E, @0x0000000000002705, @0x0000000000002734, @0x00000000D83DDC9F, + @0x00000000D83CDD9A, @0x00000000D83DDCF3, @0x00000000D83DDCF4, @0x00000000D83CDD70, @0x00000000D83CDD71, @0x00000000D83CDD8E, @0x00000000D83CDD7E, + @0x00000000D83DDCA0, @0x00000000000027BF, @0x000000000000267B, @0x0000000000002648, @0x0000000000002649, @0x000000000000264A, @0x000000000000264B, + @0x000000000000264C, @0x000000000000264D, @0x000000000000264E, @0x000000000000264F, @0x0000000000002650, @0x0000000000002651, @0x0000000000002652, + @0x0000000000002653, @0x00000000000026CE, @0x00000000D83DDD2F, @0x00000000D83CDFE7, @0x00000000D83DDCB9, @0x00000000D83DDCB2, @0x00000000D83DDCB1, + @0x00000000000000A9, @0x00000000000000AE, @0x0000000000002122, @0x000000000000303D, @0x0000000000003030, @0x00000000D83DDD1D, @0x00000000D83DDD1A, + @0x00000000D83DDD19, @0x00000000D83DDD1B, @0x00000000D83DDD1C, @0x000000000000274C, @0x0000000000002B55, @0x0000000000002757, @0x000000000000203C, @0x0000000000002049, @0x0000000000002753, + @0x0000000000002755, @0x0000000000002754, @0x00000000D83DDD03, @0x00000000D83DDD5B, @0x00000000D83DDD67, @0x00000000D83DDD50, @0x00000000D83DDD5C, + @0x00000000D83DDD51, @0x00000000D83DDD5D, @0x00000000D83DDD52, @0x00000000D83DDD5E, @0x00000000D83DDD53, @0x00000000D83DDD5F, @0x00000000D83DDD54, + @0x00000000D83DDD60, @0x00000000D83DDD55, @0x00000000D83DDD56, @0x00000000D83DDD57, @0x00000000D83DDD58, @0x00000000D83DDD59, @0x00000000D83DDD5A, + @0x00000000D83DDD61, @0x00000000D83DDD62, @0x00000000D83DDD63, @0x00000000D83DDD64, @0x00000000D83DDD65, @0x00000000D83DDD66, @0x0000000000002716, + @0x0000000000002795, @0x0000000000002796, @0x0000000000002797, @0x0000000000002660, @0x0000000000002665, @0x0000000000002663, @0x0000000000002666, + @0x00000000D83DDCAE, @0x00000000D83DDCAF, @0x0000000000002714, @0x0000000000002611, @0x00000000D83DDD18, @0x00000000D83DDD17, @0x00000000000027B0, + @0x00000000D83DDD31, @0x00000000D83DDD32, @0x00000000D83DDD33, @0x00000000000025FC, @0x00000000000025FB, @0x00000000000025FE, @0x00000000000025FD, + @0x00000000000025AA, @0x00000000000025AB, @0x00000000D83DDD3A, @0x0000000000002B1C, @0x0000000000002B1B, @0x00000000000026AB, @0x00000000000026AA, + @0x00000000D83DDD34, @0x00000000D83DDD35, @0x00000000D83DDD3B, @0x00000000D83DDD36, @0x00000000D83DDD37, @0x00000000D83DDD38, @0x00000000D83DDD39]]; + + float scales[] = {1.0f, 1.5f, 2.0f, 3.0f}; + + for (int s = 0; s < 4; s++) { + float scale = scales[s]; + + CTFontRef font = CTFontCreateWithName(CFSTR("AppleColorEmoji"), 30.0f * scale, nil); + NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(font), kCTFontAttributeName, nil]; + + for (int b = 0; b < array2.count; b++) { + NSArray *array = [array2 objectAtIndex:b]; + int countPerRow = sqrt(array.count); + int rowsCount = ceil(array.count / (float)countPerRow); + + // On iOS 4.0 and Mac OS X v10.6 you can pass null for data + size_t width = countPerRow * 30 * scale; + size_t height = rowsCount * 30 * scale; + char *data = malloc(width * height * 4); + char *dataGray = malloc(width * height); + memset(data, 0, width * height * 4); + memset(dataGray, 0, width * height); + + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = (CGBitmapInfo)kCGImageAlphaPremultipliedLast; + CGContextRef ctx = CGBitmapContextCreate(data, width, height, 8, width * 4, space, bitmapInfo); + CGColorSpaceRelease(space); + CGContextSetRGBFillColor(ctx, 0.0, 0.0, 0.0, 0.0); + CGContextFillRect(ctx, CGRectMake(0.0, 0.0, width, height)); + + CGColorSpaceRef spaceGray = CGColorSpaceCreateDeviceGray(); + CGBitmapInfo bitmapInfoGray = (CGBitmapInfo)kCGImageAlphaNone; + CGContextRef ctxGray = CGBitmapContextCreate(dataGray, width, height, 8, width, spaceGray, bitmapInfoGray); + CGColorSpaceRelease(spaceGray); + CGContextSetRGBFillColor(ctxGray, 0.0, 0.0, 0.0, 0.0); + CGContextFillRect(ctxGray, CGRectMake(0.0, 0.0, width, height)); + + + for (int a = 0; a < array.count; a++) { + unsigned long long val = [[array objectAtIndex:a] unsignedLongLongValue]; + NSData *buff = [NSData dataWithBytes:&val length:sizeof(unsigned long long)]; + buff = [self reversedData:buff]; + NSString *str = [[NSString alloc] initWithData:buff encoding:NSUnicodeStringEncoding]; + NSLog(@"%llu %@", val, str); + + NSAttributedString *as = [[NSAttributedString alloc] initWithString:str attributes:attributes]; + CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)as); + CGFloat ascent, descent, leading; + size_t fWidth = CTLineGetTypographicBounds(line, &ascent, &descent, &leading); + CGRect rect = CTLineGetImageBounds(line, NULL); + + CGFloat x = a % countPerRow * 30 * scale + (30 * scale - fWidth); + CGFloat y = height - (a / countPerRow * 30 * scale) - rect.origin.y - 30 * scale; + CGContextSetTextPosition(ctx, x, y); + CTLineDraw(line, ctx); + + CFRelease(line); + } + + // Save as JPEG + CGImageRef imageRef = CGBitmapContextCreateImage(ctx); + NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:imageRef]; + NSAssert(imageRep, @"imageRep must not be nil"); + NSData *imageData = [imageRep representationUsingType:NSJPEGFileType properties:@{NSImageCompressionFactor:@0.4f, NSImageFallbackBackgroundColor:[NSColor blackColor]}]; + NSString *fileName = [NSString stringWithFormat:@"Desktop/emoji/emoji%.01fx_%d.jpg", scale, b]; + NSString *fileDirectory = NSHomeDirectory(); + NSString *filePath = [fileDirectory stringByAppendingPathComponent:fileName]; + [imageData writeToFile:filePath atomically:YES]; + CGImageRelease(imageRef); + + for (int a = 0; a < width * height; a++) { + char alpha = data[a * 4 + 3]; + if (alpha == 0) { + dataGray[a] = 0; + } else { + dataGray[a] = alpha; + } + } + + imageRef = CGBitmapContextCreateImage(ctxGray); + imageRep = [[NSBitmapImageRep alloc] initWithCGImage:imageRef]; + NSAssert(imageRep, @"imageRep must not be nil"); + imageData = [imageRep representationUsingType:NSJPEGFileType properties:@{NSImageCompressionFactor:@0.01f, NSImageColorSyncProfileData:[NSColorSpace deviceGrayColorSpace]}]; + fileName = [NSString stringWithFormat:@"Desktop/emoji/emoji%.01fx_a_%d.jpg", scale, b]; + filePath = [fileDirectory stringByAppendingPathComponent:fileName]; + [imageData writeToFile:filePath atomically:YES]; + CGImageRelease(imageRef); + free(data); + } + CFRelease(font); + } +} + +@end