From 2f23635e4a78851356141c0d72014608dd22e723 Mon Sep 17 00:00:00 2001 From: DrKLO Date: Wed, 15 Oct 2014 00:36:15 +0400 Subject: [PATCH] UI update --- .../telegram/android/AndroidUtilities.java | 95 ++ .../org/telegram/android/MessageObject.java | 22 +- .../telegram/android/MessagesController.java | 10 +- .../org/telegram/android/MessagesStorage.java | 10 +- .../telegram/android/SendMessagesHelper.java | 2 +- .../org/telegram/ui/Cells/ChatActionCell.java | 143 +++ .../org/telegram/ui/Cells/ChatBaseCell.java | 21 +- .../java/org/telegram/ui/ChatActivity.java | 66 +- .../java/org/telegram/ui/LoginActivity.java | 2 + .../java/org/telegram/ui/PhotoViewer.java | 12 +- .../org/telegram/ui/SettingsActivity.java | 32 +- .../org/telegram/ui/UserProfileActivity.java | 64 +- .../telegram/ui/Views/HorizontalListView.java | 10 +- .../org/telegram/ui/Views/NumberPicker.java | 1058 +++++++++++++++++ .../org/telegram/ui/Views/TimerButton.java | 41 +- .../res/drawable-hdpi/list_focused_holo.9.png | Bin 0 -> 191 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 158 bytes .../list_pressed_holo_light.9.png | Bin 0 -> 159 bytes .../list_selector_disabled_holo_light.9.png | Bin 0 -> 189 bytes .../numberpicker_selection_divider.9.png | Bin 0 -> 141 bytes .../res/drawable-mdpi/list_focused_holo.9.png | Bin 0 -> 171 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 155 bytes .../list_pressed_holo_light.9.png | Bin 0 -> 158 bytes .../list_selector_disabled_holo_light.9.png | Bin 0 -> 171 bytes .../numberpicker_selection_divider.9.png | Bin 0 -> 135 bytes .../drawable-xhdpi/list_focused_holo.9.png | Bin 0 -> 203 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 162 bytes .../list_pressed_holo_light.9.png | Bin 0 -> 163 bytes .../list_selector_disabled_holo_light.9.png | Bin 0 -> 188 bytes .../numberpicker_selection_divider.9.png | Bin 0 -> 142 bytes .../drawable-xxhdpi/list_focused_holo.9.png | Bin 0 -> 1079 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 1051 bytes .../list_pressed_holo_light.9.png | Bin 0 -> 1051 bytes .../numberpicker_selection_divider.9.png | Bin 0 -> 217 bytes .../drawable/item_background_holo_light.xml | 10 + ...ector_background_transition_holo_light.xml | 6 + .../src/main/res/values-ar/strings.xml | 44 +- .../src/main/res/values-de/strings.xml | 44 +- .../src/main/res/values-es/strings.xml | 48 +- .../src/main/res/values-it/strings.xml | 44 +- .../src/main/res/values-ko/strings.xml | 44 +- .../src/main/res/values-nl/strings.xml | 44 +- .../src/main/res/values-pt-rBR/strings.xml | 46 +- .../src/main/res/values-pt-rPT/strings.xml | 46 +- TMessagesProj/src/main/res/values/strings.xml | 44 +- 45 files changed, 1692 insertions(+), 316 deletions(-) create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/list_focused_holo.9.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/list_longpressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/list_pressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/list_selector_disabled_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/numberpicker_selection_divider.9.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/list_focused_holo.9.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/list_longpressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/list_pressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/list_selector_disabled_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/numberpicker_selection_divider.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/list_focused_holo.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/list_longpressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/list_pressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/list_selector_disabled_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/numberpicker_selection_divider.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/list_focused_holo.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/list_longpressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/list_pressed_holo_light.9.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/numberpicker_selection_divider.9.png create mode 100644 TMessagesProj/src/main/res/drawable/item_background_holo_light.xml create mode 100644 TMessagesProj/src/main/res/drawable/list_selector_background_transition_holo_light.xml diff --git a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java index 3c2dda04f..21c5bc62f 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/android/AndroidUtilities.java @@ -9,7 +9,9 @@ package org.telegram.android; import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Point; @@ -24,8 +26,10 @@ import android.view.inputmethod.InputMethodManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; +import org.telegram.messenger.TLRPC; import org.telegram.messenger.UserConfig; import org.telegram.ui.ApplicationLoader; +import org.telegram.ui.Views.NumberPicker; import java.io.File; import java.util.Hashtable; @@ -240,6 +244,10 @@ public class AndroidUtilities { return (int)Math.ceil(density * value); } + public static float dpf2(float value) { + return density * value; + } + public static void checkDisplaySize() { try { WindowManager manager = (WindowManager)ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); @@ -388,4 +396,91 @@ public class AndroidUtilities { } return photoSize; } + + public static String formatTTLString(int ttl) { + if (ttl < 60) { + return LocaleController.formatPluralString("Seconds", ttl); + } else if (ttl < 60 * 60) { + return LocaleController.formatPluralString("Minutes", ttl / 60); + } else if (ttl < 60 * 60 * 24) { + return LocaleController.formatPluralString("Hours", ttl / 60 / 60); + } else if (ttl < 60 * 60 * 24 * 7) { + return LocaleController.formatPluralString("Days", ttl / 60 / 60 / 24); + } else { + int days = ttl / 60 / 60 / 24; + if (ttl % 7 == 0) { + return LocaleController.formatPluralString("Weeks", days / 7); + } else { + return String.format("%s %s", LocaleController.formatPluralString("Weeks", days / 7), LocaleController.formatPluralString("Days", days % 7)); + } + } + } + + public static AlertDialog.Builder buildTTLAlert(Context context, final TLRPC.EncryptedChat encryptedChat) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(LocaleController.getString("MessageLifetime", R.string.MessageLifetime)); + final NumberPicker numberPicker = new NumberPicker(context); + numberPicker.setMinValue(0); + numberPicker.setMaxValue(20); + if (encryptedChat.ttl >= 0 && encryptedChat.ttl < 16) { + numberPicker.setValue(encryptedChat.ttl); + } else if (encryptedChat.ttl == 30) { + numberPicker.setValue(16); + } else if (encryptedChat.ttl == 60) { + numberPicker.setValue(17); + } else if (encryptedChat.ttl == 60 * 60) { + numberPicker.setValue(18); + } else if (encryptedChat.ttl == 60 * 60 * 24) { + numberPicker.setValue(19); + } else if (encryptedChat.ttl == 60 * 60 * 24 * 7) { + numberPicker.setValue(20); + } + numberPicker.setFormatter(new NumberPicker.Formatter() { + @Override + public String format(int value) { + if (value == 0) { + return LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever); + } else if (value >= 1 && value < 16) { + return AndroidUtilities.formatTTLString(value); + } else if (value == 16) { + return AndroidUtilities.formatTTLString(30); + } else if (value == 17) { + return AndroidUtilities.formatTTLString(60); + } else if (value == 18) { + return AndroidUtilities.formatTTLString(60 * 60); + } else if (value == 19) { + return AndroidUtilities.formatTTLString(60 * 60 * 24); + } else if (value == 20) { + return AndroidUtilities.formatTTLString(60 * 60 * 24 * 7); + } + return ""; + } + }); + builder.setView(numberPicker); + builder.setNegativeButton(LocaleController.getString("Done", R.string.Done), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int oldValue = encryptedChat.ttl; + which = numberPicker.getValue(); + if (which >= 0 && which < 16) { + encryptedChat.ttl = which; + } else if (which == 16) { + encryptedChat.ttl = 30; + } else if (which == 17) { + encryptedChat.ttl = 60; + } else if (which == 18) { + encryptedChat.ttl = 60 * 60; + } else if (which == 19) { + encryptedChat.ttl = 60 * 60 * 24; + } else if (which == 20) { + encryptedChat.ttl = 60 * 60 * 24 * 7; + } + if (oldValue != encryptedChat.ttl) { + SendMessagesHelper.getInstance().sendTTLMessage(encryptedChat); + MessagesStorage.getInstance().updateEncryptedChatTTL(encryptedChat); + } + } + }); + return builder; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java index 20c7c11b7..f6f92394a 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessageObject.java @@ -178,29 +178,13 @@ public class MessageObject { } } else if (message.action instanceof TLRPC.TL_messageActionTTLChange) { if (message.action.ttl != 0) { - String timeString; - if (message.action.ttl == 2) { - timeString = LocaleController.getString("MessageLifetime2s", R.string.MessageLifetime2s); - } else if (message.action.ttl == 5) { - timeString = LocaleController.getString("MessageLifetime5s", R.string.MessageLifetime5s); - } else if (message.action.ttl == 60) { - timeString = LocaleController.getString("MessageLifetime1m", R.string.MessageLifetime1m); - } else if (message.action.ttl == 60 * 60) { - timeString = LocaleController.getString("MessageLifetime1h", R.string.MessageLifetime1h); - } else if (message.action.ttl == 60 * 60 * 24) { - timeString = LocaleController.getString("MessageLifetime1d", R.string.MessageLifetime1d); - } else if (message.action.ttl == 60 * 60 * 24 * 7) { - timeString = LocaleController.getString("MessageLifetime1w", R.string.MessageLifetime1w); - } else { - timeString = String.format("%d", message.action.ttl); - } if (isFromMe()) { - messageText = LocaleController.formatString("MessageLifetimeChangedOutgoing", R.string.MessageLifetimeChangedOutgoing, timeString); + messageText = LocaleController.formatString("MessageLifetimeChangedOutgoing", R.string.MessageLifetimeChangedOutgoing, AndroidUtilities.formatTTLString(message.action.ttl)); } else { if (fromUser != null) { - messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, fromUser.first_name, timeString); + messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, fromUser.first_name, AndroidUtilities.formatTTLString(message.action.ttl)); } else { - messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, "", timeString); + messageText = LocaleController.formatString("MessageLifetimeChanged", R.string.MessageLifetimeChanged, "", AndroidUtilities.formatTTLString(message.action.ttl)); } } } else { diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java index f2ee4c2f3..f11d4fc5f 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesController.java @@ -1700,7 +1700,8 @@ public class MessagesController implements NotificationCenter.NotificationCenter random_ids.add(random_id); SendMessagesHelper.getInstance().sendMessagesReadMessage(random_ids, chat); if (chat.ttl > 0) { - MessagesStorage.getInstance().createTaskForSecretChat(chat.id, 0, ConnectionsManager.getInstance().getCurrentTime(), 0, random_ids); + int time = ConnectionsManager.getInstance().getCurrentTime(); + MessagesStorage.getInstance().createTaskForSecretChat(chat.id, time, time, 0, random_ids); } //TODO resend request } @@ -3707,12 +3708,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter TLRPC.TL_messageService newMessage = new TLRPC.TL_messageService(); if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL) { newMessage.action = new TLRPC.TL_messageActionTTLChange(); + if (serviceMessage.action.ttl_seconds < 0 || serviceMessage.action.ttl_seconds > 60 * 60 * 24 * 365) { + serviceMessage.action.ttl_seconds = 60 * 60 * 24 * 365; + } newMessage.action.ttl = chat.ttl = serviceMessage.action.ttl_seconds; } else if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionScreenshotMessages) { newMessage.action = new TLRPC.TL_messageEcryptedAction(); newMessage.action.encryptedAction = serviceMessage.action; - } else { - return null; } newMessage.local_id = newMessage.id = UserConfig.getNewMessageId(); UserConfig.saveConfig(false); @@ -3761,7 +3763,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter return null; } else if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionReadMessages) { if (!serviceMessage.action.random_ids.isEmpty()) { - MessagesStorage.getInstance().createTaskForSecretChat(chat.id, 0, message.date, 1, serviceMessage.action.random_ids); + MessagesStorage.getInstance().createTaskForSecretChat(chat.id, ConnectionsManager.getInstance().getCurrentTime(), message.date, 1, serviceMessage.action.random_ids); } } else if (serviceMessage.action instanceof TLRPC.TL_decryptedMessageActionNotifyLayer) { AndroidUtilities.RunOnUIThread(new Runnable() { diff --git a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java index ff3b6c2d4..abbec5234 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/android/MessagesStorage.java @@ -862,7 +862,7 @@ public class MessagesStorage { } while (cursor.next()) { int mid = cursor.intValue(0); - int date = readTime + cursor.intValue(1); + int date = Math.min(readTime, time) + cursor.intValue(1); minDate = Math.min(minDate, date); ArrayList arr = messages.get(date); if (arr == null) { @@ -2198,8 +2198,8 @@ public class MessagesStorage { state.bindByteBuffer(2, data2.buffer); state.bindByteBuffer(3, data3.buffer); state.bindInteger(4, chat.ttl); - state.bindInteger(5, chat.id); - state.bindInteger(6, chat.layer); + state.bindInteger(5, chat.layer); + state.bindInteger(6, chat.id); state.step(); buffersStorage.reuseFreeBuffer(data); buffersStorage.reuseFreeBuffer(data2); @@ -2473,8 +2473,10 @@ public class MessagesStorage { private int getMessageMediaType(TLRPC.Message message) { if (message instanceof TLRPC.TL_message_secret && message.media instanceof TLRPC.TL_messageMediaPhoto && message.ttl != 0 && message.ttl <= 60) { return 1; + } else if (message.media instanceof TLRPC.TL_messageMediaPhoto || message.media instanceof TLRPC.TL_messageMediaVideo) { + return 0; } - return 0; + return -1; } private void putMessagesInternal(final ArrayList messages, final boolean withTransaction, final boolean isBroadcast, final int downloadMask) { diff --git a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java index f8fb052be..b98618c52 100644 --- a/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/android/SendMessagesHelper.java @@ -1542,7 +1542,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter @Override public void run() { TLRPC.EncryptedChat chat = MessagesController.getInstance().getEncryptedChat(encryptedChat.id); - sendingNotifyLayer.remove(chat.id); + sendingNotifyLayer.remove((Integer)chat.id); chat.layer = AndroidUtilities.setMyLayerVersion(chat.layer, CURRENT_SECRET_CHAT_LAYER); MessagesStorage.getInstance().updateEncryptedChatLayer(chat); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java new file mode 100644 index 000000000..b9ec3da65 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -0,0 +1,143 @@ +/* + * This is the source code of Telegram for Android v. 1.7.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2014. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; + +import org.telegram.android.AndroidUtilities; +import org.telegram.android.MessageObject; +import org.telegram.android.MessagesController; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; + +public class ChatActionCell extends BaseCell { + + private static Drawable backgroundBlack; + private static Drawable backgroundBlue; + private static TextPaint textPaint; + + private StaticLayout textLayout; + private int textWidth = 0; + private int textHeight = 0; + private int textX = 0; + private int textXLeft = 0; + private int textY = 0; + private boolean useBlackBackground = false; + private boolean wasLayout = false; + + private MessageObject currentMessageObject; + + public ChatActionCell(Context context) { + super(context); + if (backgroundBlack == null) { + backgroundBlack = getResources().getDrawable(R.drawable.system_black); + backgroundBlue = getResources().getDrawable(R.drawable.system_blue); + + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setColor(0xffffffff); + } + textPaint.setTextSize(AndroidUtilities.dp(MessagesController.getInstance().fontSize)); + } + + public void setMessageObject(MessageObject messageObject) { + if (currentMessageObject == messageObject) { + return; + } + currentMessageObject = messageObject; + int size; + if (AndroidUtilities.isTablet()) { + size = AndroidUtilities.getMinTabletSide(); + } else { + size = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y); + } + textLayout = new StaticLayout(currentMessageObject.messageText, textPaint, size - AndroidUtilities.dp(30), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + textHeight = 0; + textWidth = 0; + try { + int linesCount = textLayout.getLineCount(); + boolean hasNonRTL = false; + for (int a = 0; a < linesCount; a++) { + float lineWidth = 0; + float lineLeft = 0; + try { + lineWidth = textLayout.getLineWidth(a); + lineLeft = textLayout.getLineLeft(a); + textHeight = (int)Math.max(textHeight, Math.ceil(textLayout.getLineBottom(a))); + } catch (Exception e) { + FileLog.e("tmessages", e); + return; + } + + if (lineLeft == 0) { + hasNonRTL = true; + } + textWidth = (int)Math.max(textWidth, Math.ceil(lineWidth)); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + textY = AndroidUtilities.dp(7); + wasLayout = false; + requestLayout(); + } + + public void setUseBlackBackground(boolean value) { + useBlackBackground = value; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), textHeight + AndroidUtilities.dp(14)); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (currentMessageObject == null) { + super.onLayout(changed, left, top, right, bottom); + return; + } + if (!wasLayout || changed) { + textX = (right - left - textWidth) / 2; + textXLeft = (right - left - textLayout.getWidth()) / 2; + + wasLayout = true; + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (currentMessageObject == null) { + return; + } + if (!wasLayout) { + requestLayout(); + return; + } + + Drawable backgroundDrawable = null; + if (useBlackBackground) { + backgroundDrawable = backgroundBlack; + } else { + backgroundDrawable = backgroundBlue; + } + backgroundDrawable.setBounds(textX - AndroidUtilities.dp(5), AndroidUtilities.dp(5), textX + textWidth + AndroidUtilities.dp(5), getMeasuredHeight() - AndroidUtilities.dp(5)); + backgroundDrawable.draw(canvas); + + canvas.save(); + canvas.translate(textXLeft, textY); + textLayout.draw(canvas); + canvas.restore(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java index 3ce742774..5d99a660f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatBaseCell.java @@ -149,18 +149,6 @@ public class ChatBaseCell extends BaseCell { public ChatBaseCell(Context context) { super(context); - init(); - avatarImage = new ImageReceiver(this); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - avatarImage.clearImage(); - currentPhoto = null; - } - - private void init() { if (backgroundDrawableIn == null) { backgroundDrawableIn = getResources().getDrawable(R.drawable.msg_in); backgroundDrawableInSelected = getResources().getDrawable(R.drawable.msg_in_selected); @@ -199,6 +187,14 @@ public class ChatBaseCell extends BaseCell { forwardNamePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG); forwardNamePaint.setTextSize(AndroidUtilities.dp(14)); } + avatarImage = new ImageReceiver(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + avatarImage.clearImage(); + currentPhoto = null; } @Override @@ -466,7 +462,6 @@ public class ChatBaseCell extends BaseCell { } - @Override protected void onDraw(Canvas canvas) { if (currentMessageObject == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 5d913818e..2fb13499e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -72,6 +72,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.Adapters.BaseFragmentAdapter; +import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.Cells.ChatAudioCell; import org.telegram.ui.Cells.ChatBaseCell; import org.telegram.ui.Cells.ChatMediaCell; @@ -393,6 +394,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not typingDotsDrawable = new TypingDotsDrawable(); typingDotsDrawable.setIsChat(currentChat != null); + if (currentEncryptedChat != null && AndroidUtilities.getMyLayerVersion(currentEncryptedChat.layer) != SendMessagesHelper.CURRENT_SECRET_CHAT_LAYER) { + SendMessagesHelper.getInstance().sendNotifyLayerMessage(currentEncryptedChat); + } + return true; } @@ -801,6 +806,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (messageObject == null || !messageObject.isSecretMedia() || !cell.getPhotoImage().isInsideImage(x, y - top)) { break; } + File file = FileLoader.getPathToMessage(messageObject.messageOwner); + if (!file.exists()) { + break; + } startX = x; startY = y; openSecretPhotoRunnable = new Runnable() { @@ -1113,44 +1122,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (getParentActivity() == null) { return; } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("MessageLifetime", R.string.MessageLifetime)); - builder.setItems(new CharSequence[]{ - LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever), - LocaleController.getString("ShortMessageLifetime2s", R.string.ShortMessageLifetime2s), - LocaleController.getString("ShortMessageLifetime5s", R.string.ShortMessageLifetime5s), - LocaleController.getString("ShortMessageLifetime1m", R.string.ShortMessageLifetime1m), - LocaleController.getString("ShortMessageLifetime1h", R.string.ShortMessageLifetime1h), - LocaleController.getString("ShortMessageLifetime1d", R.string.ShortMessageLifetime1d), - LocaleController.getString("ShortMessageLifetime1w", R.string.ShortMessageLifetime1w) - - }, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - int oldValue = currentEncryptedChat.ttl; - if (which == 0) { - currentEncryptedChat.ttl = 0; - } else if (which == 1) { - currentEncryptedChat.ttl = 2; - } else if (which == 2) { - currentEncryptedChat.ttl = 5; - } else if (which == 3) { - currentEncryptedChat.ttl = 60; - } else if (which == 4) { - currentEncryptedChat.ttl = 60 * 60; - } else if (which == 5) { - currentEncryptedChat.ttl = 60 * 60 * 24; - } else if (which == 6) { - currentEncryptedChat.ttl = 60 * 60 * 24 * 7; - } - if (oldValue != currentEncryptedChat.ttl) { - SendMessagesHelper.getInstance().sendTTLMessage(currentEncryptedChat); - MessagesStorage.getInstance().updateEncryptedChat(currentEncryptedChat); - } - } - }); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showAlertDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat)); } }); timerButton.setTime(currentEncryptedChat.ttl); @@ -3460,41 +3432,48 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not offset = 0; } if (i == 0 && !endReached || !unread_end_reached && i == (messages.size() + 1 - offset)) { + View progressBar = null; if (view == null) { LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = li.inflate(R.layout.chat_loading_layout, viewGroup, false); - View progressBar = view.findViewById(R.id.progressLayout); + progressBar = view.findViewById(R.id.progressLayout); if (isCustomTheme) { progressBar.setBackgroundResource(R.drawable.system_loader2); } else { progressBar.setBackgroundResource(R.drawable.system_loader1); } - progressBar.setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); + } else { + progressBar = view.findViewById(R.id.progressLayout); } + progressBar.setVisibility(loadsCount > 1 ? View.VISIBLE : View.INVISIBLE); + return view; } } final MessageObject message = messages.get(messages.size() - i - offset); int type = message.contentType; if (view == null) { - LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (type == 0) { view = new ChatMessageCell(mContext); } if (type == 1) { view = new ChatMediaCell(mContext); } else if (type == 7) { - view = li.inflate(R.layout.chat_action_message_layout, viewGroup, false); + view = new ChatActionCell(mContext); } else if (type == 8) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = li.inflate(R.layout.chat_action_change_photo_layout, viewGroup, false); } else if (type == 3) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = li.inflate(R.layout.chat_outgoing_contact_layout, viewGroup, false); } else if (type == 4) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (currentChat != null) { view = li.inflate(R.layout.chat_group_incoming_contact_layout, viewGroup, false); } else { view = li.inflate(R.layout.chat_incoming_contact_layout, viewGroup, false); } } else if (type == 6) { + LayoutInflater li = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = li.inflate(R.layout.chat_unread_layout, viewGroup, false); } else if (type == 2) { view = new ChatAudioCell(mContext); @@ -3638,6 +3617,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (view instanceof ChatAudioCell && MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_AUDIO)) { ((ChatAudioCell)view).downloadAudioIfNeed(); } + } else if (view instanceof ChatActionCell) { + ((ChatActionCell)view).setMessageObject(message); + ((ChatActionCell)view).setUseBlackBackground(isCustomTheme); } else { ChatListRowHolderEx holder = (ChatListRowHolderEx)view.getTag(); if (holder == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index 4dc2997f9..990fbc452 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -60,6 +60,7 @@ public class LoginActivity extends BaseFragment implements SlideView.SlideViewDe } catch (Exception e) { FileLog.e("tmessages", e); } + progressDialog = null; } } @@ -260,6 +261,7 @@ public class LoginActivity extends BaseFragment implements SlideView.SlideViewDe } catch (Exception e) { FileLog.e("tmessages", e); } + progressDialog = null; } public void setPage(int page, boolean animated, Bundle params, boolean back) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 7fdf85c42..635ed9043 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -611,12 +611,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } try { - int size[] = new int[1]; - TLRPC.FileLocation fileLocation = getFileLocation(currentIndex, size); - if (fileLocation == null) { - return; + File f = null; + + if (currentMessageObject != null) { + f = FileLoader.getPathToMessage(currentMessageObject.messageOwner); + } else if (currentFileLocation != null) { + f = FileLoader.getPathToAttach(currentFileLocation, avatarsUserId != 0); } - File f = FileLoader.getPathToAttach(fileLocation, avatarsUserId != 0); + if (f.exists()) { Intent intent = new Intent(Intent.ACTION_SEND); if (f.toString().endsWith("mp4")) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index 0bff501e5..8a0b4e9ee 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -59,6 +59,7 @@ import org.telegram.ui.Views.ActionBar.ActionBarLayer; import org.telegram.ui.Views.AvatarUpdater; import org.telegram.ui.Views.BackupImageView; import org.telegram.ui.Views.ActionBar.BaseFragment; +import org.telegram.ui.Views.NumberPicker; import java.io.File; import java.util.ArrayList; @@ -245,39 +246,24 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("TextSize", R.string.TextSize)); - builder.setItems(new CharSequence[] { - String.format("%d", 12), - String.format("%d", 13), - String.format("%d", 14), - String.format("%d", 15), - String.format("%d", 16), - String.format("%d", 17), - String.format("%d", 18), - String.format("%d", 19), - String.format("%d", 20), - String.format("%d", 21), - String.format("%d", 22), - String.format("%d", 23), - String.format("%d", 24), - String.format("%d", 25), - String.format("%d", 26), - String.format("%d", 27), - String.format("%d", 28), - String.format("%d", 29), - String.format("%d", 30)}, new DialogInterface.OnClickListener() { + final NumberPicker numberPicker = new NumberPicker(getParentActivity()); + numberPicker.setMinValue(12); + numberPicker.setMaxValue(30); + numberPicker.setValue(MessagesController.getInstance().fontSize); + builder.setView(numberPicker); + builder.setNegativeButton(LocaleController.getString("Done", R.string.Done), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); - editor.putInt("fons_size", 12 + which); - MessagesController.getInstance().fontSize = 12 + which; + editor.putInt("fons_size", numberPicker.getValue()); + MessagesController.getInstance().fontSize = numberPicker.getValue(); editor.commit(); if (listView != null) { listView.invalidateViews(); } } }); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showAlertDialog(builder); } else if (i == enableAnimationsRow) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java index 31bf865da..84a121a0e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java @@ -31,7 +31,6 @@ import org.telegram.messenger.TLRPC; import org.telegram.android.ContactsController; import org.telegram.messenger.FileLog; import org.telegram.android.MessagesController; -import org.telegram.android.MessagesStorage; import org.telegram.android.NotificationCenter; import org.telegram.messenger.R; import org.telegram.android.MessageObject; @@ -256,47 +255,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen if (getParentActivity() == null) { return; } - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("MessageLifetime", R.string.MessageLifetime)); - builder.setItems(new CharSequence[]{ - LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever), - LocaleController.getString("ShortMessageLifetime2s", R.string.ShortMessageLifetime2s), - LocaleController.getString("ShortMessageLifetime5s", R.string.ShortMessageLifetime5s), - LocaleController.getString("ShortMessageLifetime1m", R.string.ShortMessageLifetime1m), - LocaleController.getString("ShortMessageLifetime1h", R.string.ShortMessageLifetime1h), - LocaleController.getString("ShortMessageLifetime1d", R.string.ShortMessageLifetime1d), - LocaleController.getString("ShortMessageLifetime1w", R.string.ShortMessageLifetime1w) - - }, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - int oldValue = currentEncryptedChat.ttl; - if (which == 0) { - currentEncryptedChat.ttl = 0; - } else if (which == 1) { - currentEncryptedChat.ttl = 2; - } else if (which == 2) { - currentEncryptedChat.ttl = 5; - } else if (which == 3) { - currentEncryptedChat.ttl = 60; - } else if (which == 4) { - currentEncryptedChat.ttl = 60 * 60; - } else if (which == 5) { - currentEncryptedChat.ttl = 60 * 60 * 24; - } else if (which == 6) { - currentEncryptedChat.ttl = 60 * 60 * 24 * 7; - } - if (oldValue != currentEncryptedChat.ttl) { - if (listView != null) { - listView.invalidateViews(); - } - SendMessagesHelper.getInstance().sendTTLMessage(currentEncryptedChat); - MessagesStorage.getInstance().updateEncryptedChat(currentEncryptedChat); - } - } - }); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showAlertDialog(builder); + showAlertDialog(AndroidUtilities.buildTTLAlert(getParentActivity(), currentEncryptedChat)); } else if (i == settingsNotificationsRow) { Bundle args = new Bundle(); args.putLong("dialog_id", dialog_id == 0 ? user_id : dialog_id); @@ -454,6 +413,13 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen } } + @Override + protected void onDialogDismiss() { + if (listView != null) { + listView.invalidateViews(); + } + } + @Override public void didSelectDialog(MessagesActivity messageFragment, long dialog_id, boolean param) { if (dialog_id != 0) { @@ -670,20 +636,8 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen divider.setVisibility(View.VISIBLE); if (encryptedChat.ttl == 0) { detailTextView.setText(LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever)); - } else if (encryptedChat.ttl == 2) { - detailTextView.setText(LocaleController.getString("ShortMessageLifetime2s", R.string.ShortMessageLifetime2s)); - } else if (encryptedChat.ttl == 5) { - detailTextView.setText(LocaleController.getString("ShortMessageLifetime5s", R.string.ShortMessageLifetime5s)); - } else if (encryptedChat.ttl == 60) { - detailTextView.setText(LocaleController.getString("ShortMessageLifetime1m", R.string.ShortMessageLifetime1m)); - } else if (encryptedChat.ttl == 60 * 60) { - detailTextView.setText(LocaleController.getString("ShortMessageLifetime1h", R.string.ShortMessageLifetime1h)); - } else if (encryptedChat.ttl == 60 * 60 * 24) { - detailTextView.setText(LocaleController.getString("ShortMessageLifetime1d", R.string.ShortMessageLifetime1d)); - } else if (encryptedChat.ttl == 60 * 60 * 24 * 7) { - detailTextView.setText(LocaleController.getString("ShortMessageLifetime1w", R.string.ShortMessageLifetime1w)); } else { - detailTextView.setText(String.format("%d", encryptedChat.ttl)); + detailTextView.setText(AndroidUtilities.formatTTLString(encryptedChat.ttl)); } } } else if (type == 4) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/HorizontalListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/HorizontalListView.java index 277ea468f..59fef1a82 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/HorizontalListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/HorizontalListView.java @@ -23,8 +23,6 @@ import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.Scroller; -import org.telegram.messenger.R; - public class HorizontalListView extends AdapterView { public boolean mAlwaysOverrideTouch = true; @@ -209,7 +207,7 @@ public class HorizontalListView extends AdapterView { v = list.poll(); } View child = mAdapter.getView(mRightViewIndex, v, this); - child.setTag(R.string.CacheTag, type); + child.setTag(1, type); addAndMeasureChild(child, -1); rightEdge += child.getMeasuredWidth(); @@ -236,7 +234,7 @@ public class HorizontalListView extends AdapterView { v = list.poll(); } View child = mAdapter.getView(mLeftViewIndex, v, this); - child.setTag(R.string.CacheTag, type); + child.setTag(1, type); addAndMeasureChild(child, 0); leftEdge -= child.getMeasuredWidth(); @@ -250,7 +248,7 @@ public class HorizontalListView extends AdapterView { while (child != null && child.getRight() + dx <= 0) { mDisplayOffset += child.getMeasuredWidth(); - int type = (Integer) child.getTag(R.string.CacheTag); + int type = (Integer) child.getTag(1); LinkedList list = mRemovedViewQueue.get(type); if (list == null) { list = new LinkedList(); @@ -265,7 +263,7 @@ public class HorizontalListView extends AdapterView { child = getChildAt(getChildCount() - 1); while (child != null && child.getLeft() + dx >= getWidth()) { - int type = (Integer) child.getTag(R.string.CacheTag); + int type = (Integer) child.getTag(1); LinkedList list = mRemovedViewQueue.get(type); if (list == null) { list = new LinkedList(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java new file mode 100644 index 000000000..3c94f8f9f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/NumberPicker.java @@ -0,0 +1,1058 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * 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.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; +import android.widget.LinearLayout; +import android.widget.Scroller; +import android.widget.TextView; + +import org.telegram.messenger.R; + +import java.util.Locale; + +public class NumberPicker extends LinearLayout { + + private static final int SELECTOR_WHEEL_ITEM_COUNT = 3; + private static final long DEFAULT_LONG_PRESS_UPDATE_INTERVAL = 300; + private static final int SELECTOR_MIDDLE_ITEM_INDEX = SELECTOR_WHEEL_ITEM_COUNT / 2; + private static final int SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT = 8; + private static final int SELECTOR_ADJUSTMENT_DURATION_MILLIS = 800; + private static final int SNAP_SCROLL_DURATION = 300; + private static final float TOP_AND_BOTTOM_FADING_EDGE_STRENGTH = 0.9f; + private static final int UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT = 2; + private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48; + private static final int DEFAULT_LAYOUT_RESOURCE_ID = 0; + private static final int SIZE_UNSPECIFIED = -1; + + private final TextView mInputText; + private final int mSelectionDividersDistance; + private final int mMinHeight; + private final int mMaxHeight; + private final int mMinWidth; + private int mMaxWidth; + private final boolean mComputeMaxWidth; + private final int mTextSize; + private int mSelectorTextGapHeight; + private String[] mDisplayedValues; + private int mMinValue; + private int mMaxValue; + private int mValue; + private OnValueChangeListener mOnValueChangeListener; + private OnScrollListener mOnScrollListener; + private Formatter mFormatter; + private long mLongPressUpdateInterval = DEFAULT_LONG_PRESS_UPDATE_INTERVAL; + private final SparseArray mSelectorIndexToStringCache = new SparseArray(); + private final int[] mSelectorIndices = new int[SELECTOR_WHEEL_ITEM_COUNT]; + private final Paint mSelectorWheelPaint; + private final Drawable mVirtualButtonPressedDrawable; + private int mSelectorElementHeight; + private int mInitialScrollOffset = Integer.MIN_VALUE; + private int mCurrentScrollOffset; + private final Scroller mFlingScroller; + private final Scroller mAdjustScroller; + private int mPreviousScrollerY; + private ChangeCurrentByOneFromLongPressCommand mChangeCurrentByOneFromLongPressCommand; + private float mLastDownEventY; + private long mLastDownEventTime; + private float mLastDownOrMoveEventY; + private VelocityTracker mVelocityTracker; + private int mTouchSlop; + private int mMinimumFlingVelocity; + private int mMaximumFlingVelocity; + private boolean mWrapSelectorWheel; + private final int mSolidColor; + private final Drawable mSelectionDivider; + private final int mSelectionDividerHeight; + private int mScrollState = OnScrollListener.SCROLL_STATE_IDLE; + private boolean mIngonreMoveEvents; + private int mTopSelectionDividerTop; + private int mBottomSelectionDividerBottom; + private int mLastHoveredChildVirtualViewId; + private boolean mIncrementVirtualButtonPressed; + private boolean mDecrementVirtualButtonPressed; + private final PressedStateHelper mPressedStateHelper; + private int mLastHandledDownDpadKeyCode = -1; + + public interface OnValueChangeListener { + void onValueChange(NumberPicker picker, int oldVal, int newVal); + } + + public interface OnScrollListener { + public static int SCROLL_STATE_IDLE = 0; + public static int SCROLL_STATE_TOUCH_SCROLL = 1; + public static int SCROLL_STATE_FLING = 2; + + public void onScrollStateChange(NumberPicker view, int scrollState); + } + + public interface Formatter { + public String format(int value); + } + + public NumberPicker(Context context) { + this(context, null); + } + + public NumberPicker(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NumberPicker(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + mSolidColor = 0; + mSelectionDivider = getResources().getDrawable(R.drawable.numberpicker_selection_divider); + + mSelectionDividerHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT, getResources().getDisplayMetrics()); + mSelectionDividersDistance = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE, getResources().getDisplayMetrics()); + + mMinHeight = SIZE_UNSPECIFIED; + + mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 180, getResources().getDisplayMetrics()); + if (mMinHeight != SIZE_UNSPECIFIED && mMaxHeight != SIZE_UNSPECIFIED && mMinHeight > mMaxHeight) { + throw new IllegalArgumentException("minHeight > maxHeight"); + } + + mMinWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 64, getResources().getDisplayMetrics()); + + mMaxWidth = SIZE_UNSPECIFIED; + if (mMinWidth != SIZE_UNSPECIFIED && mMaxWidth != SIZE_UNSPECIFIED && mMinWidth > mMaxWidth) { + throw new IllegalArgumentException("minWidth > maxWidth"); + } + + mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED); + + mVirtualButtonPressedDrawable = getResources().getDrawable(R.drawable.item_background_holo_light); + + mPressedStateHelper = new PressedStateHelper(); + + setWillNotDraw(false); + + mInputText = new TextView(getContext()); + addView(mInputText); + mInputText.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + mInputText.setGravity(Gravity.CENTER); + mInputText.setSingleLine(true); + mInputText.setBackground(null); + mInputText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); + + ViewConfiguration configuration = ViewConfiguration.get(context); + mTouchSlop = configuration.getScaledTouchSlop(); + mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); + mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity() / SELECTOR_MAX_FLING_VELOCITY_ADJUSTMENT; + mTextSize = (int) mInputText.getTextSize(); + + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setTextAlign(Align.CENTER); + paint.setTextSize(mTextSize); + paint.setTypeface(mInputText.getTypeface()); + ColorStateList colors = mInputText.getTextColors(); + int color = colors.getColorForState(ENABLED_STATE_SET, Color.WHITE); + paint.setColor(color); + mSelectorWheelPaint = paint; + + mFlingScroller = new Scroller(getContext(), null, true); + mAdjustScroller = new Scroller(getContext(), new DecelerateInterpolator(2.5f)); + + updateInputTextView(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + final int msrdWdth = getMeasuredWidth(); + final int msrdHght = getMeasuredHeight(); + + final int inptTxtMsrdWdth = mInputText.getMeasuredWidth(); + final int inptTxtMsrdHght = mInputText.getMeasuredHeight(); + final int inptTxtLeft = (msrdWdth - inptTxtMsrdWdth) / 2; + final int inptTxtTop = (msrdHght - inptTxtMsrdHght) / 2; + final int inptTxtRight = inptTxtLeft + inptTxtMsrdWdth; + final int inptTxtBottom = inptTxtTop + inptTxtMsrdHght; + mInputText.layout(inptTxtLeft, inptTxtTop, inptTxtRight, inptTxtBottom); + + if (changed) { + initializeSelectorWheel(); + initializeFadingEdges(); + mTopSelectionDividerTop = (getHeight() - mSelectionDividersDistance) / 2 - mSelectionDividerHeight; + mBottomSelectionDividerBottom = mTopSelectionDividerTop + 2 * mSelectionDividerHeight + mSelectionDividersDistance; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int newWidthMeasureSpec = makeMeasureSpec(widthMeasureSpec, mMaxWidth); + final int newHeightMeasureSpec = makeMeasureSpec(heightMeasureSpec, mMaxHeight); + super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec); + final int widthSize = resolveSizeAndStateRespectingMinSize(mMinWidth, getMeasuredWidth(), widthMeasureSpec); + final int heightSize = resolveSizeAndStateRespectingMinSize(mMinHeight, getMeasuredHeight(), heightMeasureSpec); + setMeasuredDimension(widthSize, heightSize); + } + + private boolean moveToFinalScrollerPosition(Scroller scroller) { + scroller.forceFinished(true); + int amountToScroll = scroller.getFinalY() - scroller.getCurrY(); + int futureScrollOffset = (mCurrentScrollOffset + amountToScroll) % mSelectorElementHeight; + int overshootAdjustment = mInitialScrollOffset - futureScrollOffset; + if (overshootAdjustment != 0) { + if (Math.abs(overshootAdjustment) > mSelectorElementHeight / 2) { + if (overshootAdjustment > 0) { + overshootAdjustment -= mSelectorElementHeight; + } else { + overshootAdjustment += mSelectorElementHeight; + } + } + amountToScroll += overshootAdjustment; + scrollBy(0, amountToScroll); + return true; + } + return false; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + if (!isEnabled()) { + return false; + } + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_DOWN: { + removeAllCallbacks(); + mInputText.setVisibility(View.INVISIBLE); + mLastDownOrMoveEventY = mLastDownEventY = event.getY(); + mLastDownEventTime = event.getEventTime(); + mIngonreMoveEvents = false; + if (mLastDownEventY < mTopSelectionDividerTop) { + if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) { + mPressedStateHelper.buttonPressDelayed(PressedStateHelper.BUTTON_DECREMENT); + } + } else if (mLastDownEventY > mBottomSelectionDividerBottom) { + if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) { + mPressedStateHelper.buttonPressDelayed(PressedStateHelper.BUTTON_INCREMENT); + } + } + getParent().requestDisallowInterceptTouchEvent(true); + if (!mFlingScroller.isFinished()) { + mFlingScroller.forceFinished(true); + mAdjustScroller.forceFinished(true); + onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + } else if (!mAdjustScroller.isFinished()) { + mFlingScroller.forceFinished(true); + mAdjustScroller.forceFinished(true); + } else if (mLastDownEventY < mTopSelectionDividerTop) { + postChangeCurrentByOneFromLongPress(false, ViewConfiguration.getLongPressTimeout()); + } else if (mLastDownEventY > mBottomSelectionDividerBottom) { + postChangeCurrentByOneFromLongPress(true, ViewConfiguration.getLongPressTimeout()); + } + return true; + } + } + return false; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!isEnabled()) { + return false; + } + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(event); + int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_MOVE: { + if (mIngonreMoveEvents) { + break; + } + float currentMoveY = event.getY(); + if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + int deltaDownY = (int) Math.abs(currentMoveY - mLastDownEventY); + if (deltaDownY > mTouchSlop) { + removeAllCallbacks(); + onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); + } + } else { + int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY)); + scrollBy(0, deltaMoveY); + invalidate(); + } + mLastDownOrMoveEventY = currentMoveY; + } + break; + case MotionEvent.ACTION_UP: { + removeChangeCurrentByOneFromLongPress(); + mPressedStateHelper.cancel(); + VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); + int initialVelocity = (int) velocityTracker.getYVelocity(); + if (Math.abs(initialVelocity) > mMinimumFlingVelocity) { + fling(initialVelocity); + onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); + } else { + int eventY = (int) event.getY(); + int deltaMoveY = (int) Math.abs(eventY - mLastDownEventY); + long deltaTime = event.getEventTime() - mLastDownEventTime; + if (deltaMoveY <= mTouchSlop && deltaTime < ViewConfiguration.getTapTimeout()) { + int selectorIndexOffset = (eventY / mSelectorElementHeight) - SELECTOR_MIDDLE_ITEM_INDEX; + if (selectorIndexOffset > 0) { + changeValueByOne(true); + mPressedStateHelper.buttonTapped( + PressedStateHelper.BUTTON_INCREMENT); + } else if (selectorIndexOffset < 0) { + changeValueByOne(false); + mPressedStateHelper.buttonTapped( + PressedStateHelper.BUTTON_DECREMENT); + } + } else { + ensureScrollWheelAdjusted(); + } + onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + } + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + break; + } + return true; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + removeAllCallbacks(); + break; + } + return super.dispatchTouchEvent(event); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + final int keyCode = event.getKeyCode(); + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_CENTER: + case KeyEvent.KEYCODE_ENTER: + removeAllCallbacks(); + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + case KeyEvent.KEYCODE_DPAD_UP: + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + if (mWrapSelectorWheel || (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) + ? getValue() < getMaxValue() : getValue() > getMinValue()) { + requestFocus(); + mLastHandledDownDpadKeyCode = keyCode; + removeAllCallbacks(); + if (mFlingScroller.isFinished()) { + changeValueByOne(keyCode == KeyEvent.KEYCODE_DPAD_DOWN); + } + return true; + } + break; + case KeyEvent.ACTION_UP: + if (mLastHandledDownDpadKeyCode == keyCode) { + mLastHandledDownDpadKeyCode = -1; + return true; + } + break; + } + } + return super.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent event) { + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + removeAllCallbacks(); + break; + } + return super.dispatchTrackballEvent(event); + } + + @Override + public void computeScroll() { + Scroller scroller = mFlingScroller; + if (scroller.isFinished()) { + scroller = mAdjustScroller; + if (scroller.isFinished()) { + return; + } + } + scroller.computeScrollOffset(); + int currentScrollerY = scroller.getCurrY(); + if (mPreviousScrollerY == 0) { + mPreviousScrollerY = scroller.getStartY(); + } + scrollBy(0, currentScrollerY - mPreviousScrollerY); + mPreviousScrollerY = currentScrollerY; + if (scroller.isFinished()) { + onScrollerFinished(scroller); + } else { + invalidate(); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + mInputText.setEnabled(enabled); + } + + @Override + public void scrollBy(int x, int y) { + int[] selectorIndices = mSelectorIndices; + if (!mWrapSelectorWheel && y > 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) { + mCurrentScrollOffset = mInitialScrollOffset; + return; + } + if (!mWrapSelectorWheel && y < 0 && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) { + mCurrentScrollOffset = mInitialScrollOffset; + return; + } + mCurrentScrollOffset += y; + while (mCurrentScrollOffset - mInitialScrollOffset > mSelectorTextGapHeight) { + mCurrentScrollOffset -= mSelectorElementHeight; + decrementSelectorIndices(selectorIndices); + setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true); + if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] <= mMinValue) { + mCurrentScrollOffset = mInitialScrollOffset; + } + } + while (mCurrentScrollOffset - mInitialScrollOffset < -mSelectorTextGapHeight) { + mCurrentScrollOffset += mSelectorElementHeight; + incrementSelectorIndices(selectorIndices); + setValueInternal(selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX], true); + if (!mWrapSelectorWheel && selectorIndices[SELECTOR_MIDDLE_ITEM_INDEX] >= mMaxValue) { + mCurrentScrollOffset = mInitialScrollOffset; + } + } + } + + @Override + protected int computeVerticalScrollOffset() { + return mCurrentScrollOffset; + } + + @Override + protected int computeVerticalScrollRange() { + return (mMaxValue - mMinValue + 1) * mSelectorElementHeight; + } + + @Override + protected int computeVerticalScrollExtent() { + return getHeight(); + } + + @Override + public int getSolidColor() { + return mSolidColor; + } + + public void setOnValueChangedListener(OnValueChangeListener onValueChangedListener) { + mOnValueChangeListener = onValueChangedListener; + } + + public void setOnScrollListener(OnScrollListener onScrollListener) { + mOnScrollListener = onScrollListener; + } + + public void setFormatter(Formatter formatter) { + if (formatter == mFormatter) { + return; + } + mFormatter = formatter; + initializeSelectorWheelIndices(); + updateInputTextView(); + } + + public void setValue(int value) { + setValueInternal(value, false); + } + + private void tryComputeMaxWidth() { + if (!mComputeMaxWidth) { + return; + } + int maxTextWidth = 0; + if (mDisplayedValues == null) { + float maxDigitWidth = 0; + for (int i = 0; i <= 9; i++) { + final float digitWidth = mSelectorWheelPaint.measureText(formatNumberWithLocale(i)); + if (digitWidth > maxDigitWidth) { + maxDigitWidth = digitWidth; + } + } + int numberOfDigits = 0; + int current = mMaxValue; + while (current > 0) { + numberOfDigits++; + current = current / 10; + } + maxTextWidth = (int) (numberOfDigits * maxDigitWidth); + } else { + final int valueCount = mDisplayedValues.length; + for (String mDisplayedValue : mDisplayedValues) { + final float textWidth = mSelectorWheelPaint.measureText(mDisplayedValue); + if (textWidth > maxTextWidth) { + maxTextWidth = (int) textWidth; + } + } + } + maxTextWidth += mInputText.getPaddingLeft() + mInputText.getPaddingRight(); + if (mMaxWidth != maxTextWidth) { + if (maxTextWidth > mMinWidth) { + mMaxWidth = maxTextWidth; + } else { + mMaxWidth = mMinWidth; + } + invalidate(); + } + } + + public boolean getWrapSelectorWheel() { + return mWrapSelectorWheel; + } + + public void setWrapSelectorWheel(boolean wrapSelectorWheel) { + final boolean wrappingAllowed = (mMaxValue - mMinValue) >= mSelectorIndices.length; + if ((!wrapSelectorWheel || wrappingAllowed) && wrapSelectorWheel != mWrapSelectorWheel) { + mWrapSelectorWheel = wrapSelectorWheel; + } + } + + public void setOnLongPressUpdateInterval(long intervalMillis) { + mLongPressUpdateInterval = intervalMillis; + } + + public int getValue() { + return mValue; + } + + public int getMinValue() { + return mMinValue; + } + + public void setMinValue(int minValue) { + if (mMinValue == minValue) { + return; + } + if (minValue < 0) { + throw new IllegalArgumentException("minValue must be >= 0"); + } + mMinValue = minValue; + if (mMinValue > mValue) { + mValue = mMinValue; + } + boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length; + setWrapSelectorWheel(wrapSelectorWheel); + initializeSelectorWheelIndices(); + updateInputTextView(); + tryComputeMaxWidth(); + invalidate(); + } + + public int getMaxValue() { + return mMaxValue; + } + + public void setMaxValue(int maxValue) { + if (mMaxValue == maxValue) { + return; + } + if (maxValue < 0) { + throw new IllegalArgumentException("maxValue must be >= 0"); + } + mMaxValue = maxValue; + if (mMaxValue < mValue) { + mValue = mMaxValue; + } + boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length; + setWrapSelectorWheel(wrapSelectorWheel); + initializeSelectorWheelIndices(); + updateInputTextView(); + tryComputeMaxWidth(); + invalidate(); + } + + public String[] getDisplayedValues() { + return mDisplayedValues; + } + + public void setDisplayedValues(String[] displayedValues) { + if (mDisplayedValues == displayedValues) { + return; + } + mDisplayedValues = displayedValues; + updateInputTextView(); + initializeSelectorWheelIndices(); + tryComputeMaxWidth(); + } + + @Override + protected float getTopFadingEdgeStrength() { + return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH; + } + + @Override + protected float getBottomFadingEdgeStrength() { + return TOP_AND_BOTTOM_FADING_EDGE_STRENGTH; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + removeAllCallbacks(); + } + + @Override + protected void onDraw(Canvas canvas) { + float x = (getRight() - getLeft()) / 2; + float y = mCurrentScrollOffset; + + if (mVirtualButtonPressedDrawable != null && mScrollState == OnScrollListener.SCROLL_STATE_IDLE) { + if (mDecrementVirtualButtonPressed) { + mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET); + mVirtualButtonPressedDrawable.setBounds(0, 0, getRight(), mTopSelectionDividerTop); + mVirtualButtonPressedDrawable.draw(canvas); + } + if (mIncrementVirtualButtonPressed) { + mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET); + mVirtualButtonPressedDrawable.setBounds(0, mBottomSelectionDividerBottom, getRight(), getBottom()); + mVirtualButtonPressedDrawable.draw(canvas); + } + } + + // draw the selector wheel + int[] selectorIndices = mSelectorIndices; + for (int i = 0; i < selectorIndices.length; i++) { + int selectorIndex = selectorIndices[i]; + String scrollSelectorValue = mSelectorIndexToStringCache.get(selectorIndex); + // Do not draw the middle item if input is visible since the input + // is shown only if the wheel is static and it covers the middle + // item. Otherwise, if the user starts editing the text via the + // IME he may see a dimmed version of the old value intermixed + // with the new one. + if (i != SELECTOR_MIDDLE_ITEM_INDEX || mInputText.getVisibility() != VISIBLE) { + canvas.drawText(scrollSelectorValue, x, y, mSelectorWheelPaint); + } + y += mSelectorElementHeight; + } + + // draw the selection dividers + if (mSelectionDivider != null) { + // draw the top divider + int topOfTopDivider = mTopSelectionDividerTop; + int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight; + mSelectionDivider.setBounds(0, topOfTopDivider, getRight(), bottomOfTopDivider); + mSelectionDivider.draw(canvas); + + // draw the bottom divider + int bottomOfBottomDivider = mBottomSelectionDividerBottom; + int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight; + mSelectionDivider.setBounds(0, topOfBottomDivider, getRight(), bottomOfBottomDivider); + mSelectionDivider.draw(canvas); + } + } + + private int makeMeasureSpec(int measureSpec, int maxSize) { + if (maxSize == SIZE_UNSPECIFIED) { + return measureSpec; + } + final int size = MeasureSpec.getSize(measureSpec); + final int mode = MeasureSpec.getMode(measureSpec); + switch (mode) { + case MeasureSpec.EXACTLY: + return measureSpec; + case MeasureSpec.AT_MOST: + return MeasureSpec.makeMeasureSpec(Math.min(size, maxSize), MeasureSpec.EXACTLY); + case MeasureSpec.UNSPECIFIED: + return MeasureSpec.makeMeasureSpec(maxSize, MeasureSpec.EXACTLY); + default: + throw new IllegalArgumentException("Unknown measure mode: " + mode); + } + } + + private int resolveSizeAndStateRespectingMinSize( + int minSize, int measuredSize, int measureSpec) { + if (minSize != SIZE_UNSPECIFIED) { + final int desiredWidth = Math.max(minSize, measuredSize); + return resolveSizeAndState(desiredWidth, measureSpec, 0); + } else { + return measuredSize; + } + } + + private void initializeSelectorWheelIndices() { + mSelectorIndexToStringCache.clear(); + int[] selectorIndices = mSelectorIndices; + int current = getValue(); + for (int i = 0; i < mSelectorIndices.length; i++) { + int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX); + if (mWrapSelectorWheel) { + selectorIndex = getWrappedSelectorIndex(selectorIndex); + } + selectorIndices[i] = selectorIndex; + ensureCachedScrollSelectorValue(selectorIndices[i]); + } + } + + private void setValueInternal(int current, boolean notifyChange) { + if (mValue == current) { + return; + } + if (mWrapSelectorWheel) { + current = getWrappedSelectorIndex(current); + } else { + current = Math.max(current, mMinValue); + current = Math.min(current, mMaxValue); + } + int previous = mValue; + mValue = current; + updateInputTextView(); + if (notifyChange) { + notifyChange(previous, current); + } + initializeSelectorWheelIndices(); + invalidate(); + } + + private void changeValueByOne(boolean increment) { + mInputText.setVisibility(View.INVISIBLE); + if (!moveToFinalScrollerPosition(mFlingScroller)) { + moveToFinalScrollerPosition(mAdjustScroller); + } + mPreviousScrollerY = 0; + if (increment) { + mFlingScroller.startScroll(0, 0, 0, -mSelectorElementHeight, SNAP_SCROLL_DURATION); + } else { + mFlingScroller.startScroll(0, 0, 0, mSelectorElementHeight, SNAP_SCROLL_DURATION); + } + invalidate(); + } + + private void initializeSelectorWheel() { + initializeSelectorWheelIndices(); + int[] selectorIndices = mSelectorIndices; + int totalTextHeight = selectorIndices.length * mTextSize; + float totalTextGapHeight = (getBottom() - getTop()) - totalTextHeight; + float textGapCount = selectorIndices.length; + mSelectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f); + mSelectorElementHeight = mTextSize + mSelectorTextGapHeight; + int editTextTextPosition = mInputText.getBaseline() + mInputText.getTop(); + mInitialScrollOffset = editTextTextPosition - (mSelectorElementHeight * SELECTOR_MIDDLE_ITEM_INDEX); + mCurrentScrollOffset = mInitialScrollOffset; + updateInputTextView(); + } + + private void initializeFadingEdges() { + setVerticalFadingEdgeEnabled(true); + setFadingEdgeLength((getBottom() - getTop() - mTextSize) / 2); + } + + private void onScrollerFinished(Scroller scroller) { + if (scroller == mFlingScroller) { + if (!ensureScrollWheelAdjusted()) { + updateInputTextView(); + } + onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + } else { + if (mScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) { + updateInputTextView(); + } + } + } + + private void onScrollStateChange(int scrollState) { + if (mScrollState == scrollState) { + return; + } + mScrollState = scrollState; + if (mOnScrollListener != null) { + mOnScrollListener.onScrollStateChange(this, scrollState); + } + } + + private void fling(int velocityY) { + mPreviousScrollerY = 0; + + if (velocityY > 0) { + mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); + } else { + mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); + } + + invalidate(); + } + + private int getWrappedSelectorIndex(int selectorIndex) { + if (selectorIndex > mMaxValue) { + return mMinValue + (selectorIndex - mMaxValue) % (mMaxValue - mMinValue) - 1; + } else if (selectorIndex < mMinValue) { + return mMaxValue - (mMinValue - selectorIndex) % (mMaxValue - mMinValue) + 1; + } + return selectorIndex; + } + + private void incrementSelectorIndices(int[] selectorIndices) { + System.arraycopy(selectorIndices, 1, selectorIndices, 0, selectorIndices.length - 1); + int nextScrollSelectorIndex = selectorIndices[selectorIndices.length - 2] + 1; + if (mWrapSelectorWheel && nextScrollSelectorIndex > mMaxValue) { + nextScrollSelectorIndex = mMinValue; + } + selectorIndices[selectorIndices.length - 1] = nextScrollSelectorIndex; + ensureCachedScrollSelectorValue(nextScrollSelectorIndex); + } + + private void decrementSelectorIndices(int[] selectorIndices) { + System.arraycopy(selectorIndices, 0, selectorIndices, 1, selectorIndices.length - 1); + int nextScrollSelectorIndex = selectorIndices[1] - 1; + if (mWrapSelectorWheel && nextScrollSelectorIndex < mMinValue) { + nextScrollSelectorIndex = mMaxValue; + } + selectorIndices[0] = nextScrollSelectorIndex; + ensureCachedScrollSelectorValue(nextScrollSelectorIndex); + } + + private void ensureCachedScrollSelectorValue(int selectorIndex) { + SparseArray cache = mSelectorIndexToStringCache; + String scrollSelectorValue = cache.get(selectorIndex); + if (scrollSelectorValue != null) { + return; + } + if (selectorIndex < mMinValue || selectorIndex > mMaxValue) { + scrollSelectorValue = ""; + } else { + if (mDisplayedValues != null) { + int displayedValueIndex = selectorIndex - mMinValue; + scrollSelectorValue = mDisplayedValues[displayedValueIndex]; + } else { + scrollSelectorValue = formatNumber(selectorIndex); + } + } + cache.put(selectorIndex, scrollSelectorValue); + } + + private String formatNumber(int value) { + return (mFormatter != null) ? mFormatter.format(value) : formatNumberWithLocale(value); + } + + private boolean updateInputTextView() { + String text = (mDisplayedValues == null) ? formatNumber(mValue) : mDisplayedValues[mValue - mMinValue]; + if (!TextUtils.isEmpty(text) && !text.equals(mInputText.getText().toString())) { + mInputText.setText(text); + return true; + } + return false; + } + + private void notifyChange(int previous, int current) { + if (mOnValueChangeListener != null) { + mOnValueChangeListener.onValueChange(this, previous, mValue); + } + } + + private void postChangeCurrentByOneFromLongPress(boolean increment, long delayMillis) { + if (mChangeCurrentByOneFromLongPressCommand == null) { + mChangeCurrentByOneFromLongPressCommand = new ChangeCurrentByOneFromLongPressCommand(); + } else { + removeCallbacks(mChangeCurrentByOneFromLongPressCommand); + } + mChangeCurrentByOneFromLongPressCommand.setStep(increment); + postDelayed(mChangeCurrentByOneFromLongPressCommand, delayMillis); + } + + private void removeChangeCurrentByOneFromLongPress() { + if (mChangeCurrentByOneFromLongPressCommand != null) { + removeCallbacks(mChangeCurrentByOneFromLongPressCommand); + } + } + + private void removeAllCallbacks() { + if (mChangeCurrentByOneFromLongPressCommand != null) { + removeCallbacks(mChangeCurrentByOneFromLongPressCommand); + } + mPressedStateHelper.cancel(); + } + + private int getSelectedPos(String value) { + if (mDisplayedValues == null) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + // Ignore as if it's not a number we don't care + } + } else { + for (int i = 0; i < mDisplayedValues.length; i++) { + // Don't force the user to type in jan when ja will do + value = value.toLowerCase(); + if (mDisplayedValues[i].toLowerCase().startsWith(value)) { + return mMinValue + i; + } + } + + /* + * The user might have typed in a number into the month field i.e. + * 10 instead of OCT so support that too. + */ + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + // Ignore as if it's not a number we don't care + } + } + return mMinValue; + } + + private boolean ensureScrollWheelAdjusted() { + // adjust to the closest value + int deltaY = mInitialScrollOffset - mCurrentScrollOffset; + if (deltaY != 0) { + mPreviousScrollerY = 0; + if (Math.abs(deltaY) > mSelectorElementHeight / 2) { + deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight; + } + mAdjustScroller.startScroll(0, 0, 0, deltaY, SELECTOR_ADJUSTMENT_DURATION_MILLIS); + invalidate(); + return true; + } + return false; + } + + class PressedStateHelper implements Runnable { + public static final int BUTTON_INCREMENT = 1; + public static final int BUTTON_DECREMENT = 2; + + private final int MODE_PRESS = 1; + private final int MODE_TAPPED = 2; + + private int mManagedButton; + private int mMode; + + public void cancel() { + mMode = 0; + mManagedButton = 0; + NumberPicker.this.removeCallbacks(this); + if (mIncrementVirtualButtonPressed) { + mIncrementVirtualButtonPressed = false; + invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom()); + } + mDecrementVirtualButtonPressed = false; + if (mDecrementVirtualButtonPressed) { + invalidate(0, 0, getRight(), mTopSelectionDividerTop); + } + } + + public void buttonPressDelayed(int button) { + cancel(); + mMode = MODE_PRESS; + mManagedButton = button; + NumberPicker.this.postDelayed(this, ViewConfiguration.getTapTimeout()); + } + + public void buttonTapped(int button) { + cancel(); + mMode = MODE_TAPPED; + mManagedButton = button; + NumberPicker.this.post(this); + } + + @Override + public void run() { + switch (mMode) { + case MODE_PRESS: { + switch (mManagedButton) { + case BUTTON_INCREMENT: { + mIncrementVirtualButtonPressed = true; + invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom()); + } + break; + case BUTTON_DECREMENT: { + mDecrementVirtualButtonPressed = true; + invalidate(0, 0, getRight(), mTopSelectionDividerTop); + } + } + } + break; + case MODE_TAPPED: { + switch (mManagedButton) { + case BUTTON_INCREMENT: { + if (!mIncrementVirtualButtonPressed) { + NumberPicker.this.postDelayed(this, + ViewConfiguration.getPressedStateDuration()); + } + mIncrementVirtualButtonPressed ^= true; + invalidate(0, mBottomSelectionDividerBottom, getRight(), getBottom()); + } + break; + case BUTTON_DECREMENT: { + if (!mDecrementVirtualButtonPressed) { + NumberPicker.this.postDelayed(this, + ViewConfiguration.getPressedStateDuration()); + } + mDecrementVirtualButtonPressed ^= true; + invalidate(0, 0, getRight(), mTopSelectionDividerTop); + } + } + } + break; + } + } + } + + class ChangeCurrentByOneFromLongPressCommand implements Runnable { + private boolean mIncrement; + + private void setStep(boolean increment) { + mIncrement = increment; + } + + @Override + public void run() { + changeValueByOne(mIncrement); + postDelayed(this, mLongPressUpdateInterval); + } + } + + static private String formatNumberWithLocale(int value) { + return String.format(Locale.getDefault(), "%d", value); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/TimerButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/TimerButton.java index 25257a58d..9c6c43048 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/TimerButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/TimerButton.java @@ -65,20 +65,33 @@ public class TimerButton extends View { time = value; String timeString = null; - if (time == 2) { - timeString = "2s"; - } else if (time == 5) { - timeString = "5s"; - } else if (time == 60) { - timeString = "1m"; - } else if (time == 60 * 60) { - timeString = "1h"; - } else if (time == 60 * 60 * 24) { - timeString = "1d"; - } else if (time == 60 * 60 * 24 * 7) { - timeString = "1w"; + if (time >= 1 && time < 60) { + timeString = "" + value; + if (timeString.length() < 2) { + timeString += "s"; + } + } else if (time >= 60 && time < 60 * 60) { + timeString = "" + value / 60; + if (timeString.length() < 2) { + timeString += "m"; + } + } else if (time >= 60 * 60 && time < 60 * 60 * 24) { + timeString = "" + value / 60 / 60; + if (timeString.length() < 2) { + timeString += "h"; + } + } else if (time >= 60 * 60 * 24 && time < 60 * 60 * 24 * 7) { + timeString = "" + value / 60 / 60 / 24; + if (timeString.length() < 2) { + timeString += "d"; + } } else { - timeString = "c"; + timeString = "" + value / 60 / 60 / 24 / 7; + if (timeString.length() < 2) { + timeString += "w"; + } else if (timeString.length() > 2) { + timeString = "c"; + } } timeWidth = timePaint.measureText(timeString); @@ -110,7 +123,7 @@ public class TimerButton extends View { drawable.draw(canvas); if (time != 0 && timeLayout != null) { - canvas.translate((width - timeWidth) / 2, (height - timeHeight) / 2 + AndroidUtilities.dp(1)); + canvas.translate((int)(width / 2 - Math.ceil(timeWidth / 2)), (height - timeHeight) / 2 + AndroidUtilities.dpf2(1.5f)); timeLayout.draw(canvas); } } diff --git a/TMessagesProj/src/main/res/drawable-hdpi/list_focused_holo.9.png b/TMessagesProj/src/main/res/drawable-hdpi/list_focused_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..555270842a739eb45d404ab473ad38a4c730240c GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhEX7WqAsj$Z!;#Vf2?p zbb>IW`N`93fr5#iE{-7*Q>RY2$k|}PUG3I8+hq?M&UySt({#?YOY9F7 zV)|4jusAQAw~U49z^wIm)#n)N-o9e&+IDtd30r~4TeZ_)uKQd*6*KFj^h@OhGw-CF i@bOgmX?1Mk5ox7hDXx8`mUDoXF?hQAxvX2?p zbb>IW`N`93fr1X6E{-7*Q%#Yu{jSZY6_r0*XaPU~Kw7H>RPz#So x*q`|ew2FMyJPjWoux@6QYW~leSYKby#;`O{WM{qLW{?XQJYD@<);T3K0RTWvFIxZr literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/list_pressed_holo_light.9.png b/TMessagesProj/src/main/res/drawable-hdpi/list_pressed_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..2054530ed2870bfa7dbc60a2d142ba3776e63d33 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhEX7WqAsj$Z!;#Vf2?p zbb>IW`N`93fr5^nE{-7*QCu0d(23bbo?Vjc_au2PNc{2VI6ud7G9!a*tc2mrrkFoK!x%hW{an^LB{Ts56WK1# literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/list_selector_disabled_holo_light.9.png b/TMessagesProj/src/main/res/drawable-hdpi/list_selector_disabled_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..ca8e9a2778f58deb3be0cba0d7af89400525896a GIT binary patch literal 189 zcmeAS@N?(olHy`uVBq!ia0vp^;y~=k!3HF){@Qy1DVAa<&kznEsNqQI0P;BtJR*x3 z7`Qt@n9=;?>9s(?cuyC{kcif|*Eb3_81S$-+V)J@yPBVWyvqG&GeYz~KYxJdw0eeyIoHJnOEfn# R{sUUY;OXk;vd$@?2>|r{LWckV literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/numberpicker_selection_divider.9.png b/TMessagesProj/src/main/res/drawable-hdpi/numberpicker_selection_divider.9.png new file mode 100644 index 0000000000000000000000000000000000000000..c9c72ba61947b714ec303af2d0a87eab4ba9fdeb GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=mSQK*5Dp-y;YjHK@;M7UB8wRq zxI00Z(fs7;wLn3APZ!4!j+wbV8+jQN1X?T}yPbO{(f6@9Ve#F`7Xli%9)zc=6~48y hnPicXp?vo<6WbTDsrETbX9Kk|c)I$ztaD0e0s!FaC|Lji literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/list_focused_holo.9.png b/TMessagesProj/src/main/res/drawable-mdpi/list_focused_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..00f05d8c97e7963f23dc48a79ec0b09b88d52e20 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEX7WqAsj$Z!;#Vf4nJ zaCd?*qxs3xYk`8ko-U3d5>tD79C;fY1elknGfSSZkgss!Fq+WV_wb)ql1{DGd2)`S!@o4yre42YU?3urKd Mr>mdKI;Vst081n_F8}}l literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/list_longpressed_holo_light.9.png b/TMessagesProj/src/main/res/drawable-mdpi/list_longpressed_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..3226ab760aaa0c15b1fcab6d993bce639f0676dd GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEX7WqAsj$Z!;#Vf4nJ zaCd?*qxs3xYk`8co-U3d5>uD#j+S3j3^P64nJ zaCd?*qxs3xYk`6eo-U3d5>u0BSQe`>3;p0f$^!z%>E}`sT8w%mH#B_t^2ONahROs+ w;Y|~FhzOW8PE-<>Sa9!8KkxZDmXjG7KBtQ4PiOt712l`l)78&qol`;+06UH>DgXcg literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/list_selector_disabled_holo_light.9.png b/TMessagesProj/src/main/res/drawable-mdpi/list_selector_disabled_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..42cb6463e4c28c6aeffa315c4fc869867dbb6b7c GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^{6MVD!3HFkzrK_Oq*#ibJVQ8upoSx*1IXtr@Q5sC zVBqcqVMg@oJyIHz9Y|%SiynA|kd)58s-{92FmHki*;@KxND!%;H%=F4ZU(n;yg$GVRgBd(s L{an^LB{Ts54)-;@ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/numberpicker_selection_divider.9.png b/TMessagesProj/src/main/res/drawable-mdpi/numberpicker_selection_divider.9.png new file mode 100644 index 0000000000000000000000000000000000000000..076fc16642176d49cc1b162d4838de91ce6e27f6 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^EFjFm1|(O0oL2|p6gzo_Z~#FKM@k2f&spFRSa9~641CF;R9N4Um`Tq}MWC=06dTk%OL+*#) ar-D=*+w0EX1GeE8VNeS)2#7n8DN4&t;ucLK6V`fk$Nk literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/list_longpressed_holo_light.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/list_longpressed_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..5532e88c2c65f7fc9f37bf5a90c5868864b47c9d GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xamSQK*5Dp-y;YjHK@;M7UB8wRq zxI00Z(fs7;wLn1^PZ!4!jfu%0zJE_Y!1Ql%qR4~y@6R9LNd1v?xtBZV&W=K1VNIzC z2M=T%`CxN4cgcP?w_UeA;(K{6xb)3i@iD2szMhRiY@WpK7>BTvK=T+pUHx3vIVCg! E0Ltw((f|Me literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/list_pressed_holo_light.9.png b/TMessagesProj/src/main/res/drawable-xhdpi/list_pressed_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..f4af9265719d65e1c500c2a9de627aff162a18cc GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xamSQK*5Dp-y;YjHK@;M7UB8wRq zxI00Z(fs7;wLn2vPZ!4!jfu%KEQ{5c#XiXME^e4`Bzf#c$z!IMFJGq5@VMB( z!{(;k_B=CB`LC(brMqswvQiq=2qh%>1r1WJE1Ls*cJ{|veUO|(2k}?c6(wzK1e5>OQxsgorF)iU^(Q8S`6? g%TvbW5Bsj4%+EhcOubus5M&^Or>mdKI;Vst093XqtN;K2 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/list_focused_holo.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/list_focused_holo.9.png new file mode 100644 index 0000000000000000000000000000000000000000..76cad1739505238d03aa4c0b25887e5813a14df8 GIT binary patch literal 1079 zcmbVL%WKp?9F7)?6e}ot@W~KG5#3}a`^bZ??dGv|!L4Px;^INfCbPS-O(rH&ce@9v zf@r~libpHn6a=pd9uySOL;nGL_M!)Go`gEtcB_ZdgMnlo`F-E_doD~)oEsiGIK*+> zaB1AEuytSZ?i^&_#f2loY&k%!8l55y+O{Li6ZUlOl{EdXM$MkL6JQq;AlM3QveFhW9PUMBuBLPSl?K+vId7gQ?7J?s^%NxW&yq+kQGO=Ta zA`+nJ`$=3q>zG#XUpF?jj;nJa7ArU=vysE<(Kyxzv)pYhlo&E^v)CtvIKJqLW+ShrmRcFqH1NOjMC@W44X?EhMtv_qG=$6EU24M zRjjlM(>WC?y4uHmrX;EoGSlyX$rhm`ZjlfeQ&~{9 z9WO}ENqN?yHF4zKz-}=jKIr9F^ES~}CEbt;mRdk-aTf|Y()FC2HLYwx$;kXV*8QKH ziOd->X^wxIrGLdbFxhUjK09m-4-VMGL~JzPobkT1v1*pgLbda?{(E(GbU=9j`SZPt zdtcn$^Ze+8TPIeYF1ydRD?d_8pFWOZcny4;-|;we|Ml6WJI2k^yRVZcgL~~$-OKLb z#eI)HJa~9~pm5;Gg)6^~E}#1Fek@#AeEH(!{`tzTvH5M>${}EkqaTfAluOox`AWZf F`ww_FQj-7x literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/list_longpressed_holo_light.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/list_longpressed_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..230d649bf730871f6f538cd7cf957d0aa894f899 GIT binary patch literal 1051 zcmbVL&ui0A9FL3*8Jl|0!vyh>T@-BcUYevySe$Lsx`LghD_A@&P2SqLB`+p#HX8`y zK^!Q0(1R#=@Svdh4|q^;7yknlFa8B02=k^dZ8Js24u&M}z3=;eKHuM4jm7iRlgB4H zj+?G6SWUJbO}~Q^?00nUv?JjO&+ugYO0nc&VSkG&< zX}f;GaELG1sg98NAwzTA+p#{3WFh>tPx9yoJpOFKsTi zoXu57clr6VU@kEjfsd&T5`Qg-jl|>!yawB+Vvz>}6}oEjL#NvH1}KvV14YQ^9EcE5 zRRPJ0s-iPMf=Ch}d(|A04MjDi0vJ6!vlh84M$@W}Y_XHc_b3ewQSA5oLO(B%s4F5} z*HaEj$}x>xyb(}4$p!Ij#$e&tiM)_{BmgO+-68ALQKDGN#KaCo zBtX&k)3^rOF>T^MZtSTYw>Cm7HgQbWBZt*vWi|t|-0g2DRb<{67bA}q#a^?Bv+m=7 z)-01}H-hWA21FXmVnK!l391@GS{cb!p(ItSdPR|BE#ue=TZBpx$`ynZ$wH`-*Gp2V zsK}OlwBd#MBNPoZ0o5-4Pbc}WGQ>bBzr zso->4pkcfgj=XEwtwzKLgA5zq9v;BLKaoQ%U(V}!3CXG|L4_Y--T%p&$ea<=_V}w^ zG7;;+bi3R9?65mNIAAjqvEg|2aI| ObX;qdMeB8G`Q~rOt3*x! literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/list_pressed_holo_light.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/list_pressed_holo_light.9.png new file mode 100644 index 0000000000000000000000000000000000000000..1352a1702a7bd044d5e9eb2424f2501b3360abe8 GIT binary patch literal 1051 zcmbVLzi-n(6gH};P(^_Oh9V(Oj)XuWw$FAP+o~zC9k-E4iPA_!EUjZ-8`Iin>??6Q zp$>=&D!MQrP!|T6SP^1iXcztkHa0f?1K^y-g`w(zW&3`;?|bik_uh7Ees*%=@&v;$ zlZ`pMO~;G*cV?Xacebxg(qW3!yJP_`lYyHcrsm@w0*%mJL2cyv8xOytX@(i=2c0hI zHt*;j4mmfs;j%EMXoi`d$zs=AMFjNFN)Q?B&(B|25cmdruhJBnv4#4<+-8CnHs?Fu z=BlUp?944No#~VyM8pMIxE7^)X0StEozC-Zo&`e_vTCr0L3NugVBrLT3MZ940YacC z9F!}H0&fCQfFdu@S1ChTuPC}$1*32}LF2GdJcT zC-7mI=QXrWNgMqQ<5=sovk@b{jZ(axc=SG&O9hx}ce110kVd25O#*r;?wXCg^$RgomqQ5~q(9SG}^saQ2r zu2)UfavVpk6xc?T5;yWt;WwauN7(v_SlvpHOK{S`c&(^_Rv#0b_HhiX1qEDhx?T|F z8#nR`I?UHbN$?2yPJ%-)R9Fv=i9k`RHOZ6}OBiJ?sFLbvR1jINX_mx}u>Sw3#?xT< zygdFXmtu?7U_L!6e!4hn9~9A^NoaFCTl_Rj8>`WBO_I_P`t+vjW zIOBP1Y-j2{^Z1;)e{dTK55T3f2QOu3KUSxn@^4=}`7XfSQ}=tn-;IyII(=b@k+(|e S$9VBa-mZ=My#2vk+WG^{9zwkU literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/numberpicker_selection_divider.9.png b/TMessagesProj/src/main/res/drawable-xxhdpi/numberpicker_selection_divider.9.png new file mode 100644 index 0000000000000000000000000000000000000000..b7a99402eb58c62b821e388e56bc1f24f1e468c6 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEX7WqAsj$Z!;#Vf4nJ za0`JjKhs88ydXVVch{#VC3oI7$PzCZ2v}H1_gl@xyNqj-bwU5 zl(`WVlccR{vOp|>HN@({jOwKf8&nHFJ0HmY%;5b~Xyr*R;}1aH44$rjF6*2UngIQ1 BKpy}A literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable/item_background_holo_light.xml b/TMessagesProj/src/main/res/drawable/item_background_holo_light.xml new file mode 100644 index 000000000..652dc8a4c --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/item_background_holo_light.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/TMessagesProj/src/main/res/drawable/list_selector_background_transition_holo_light.xml b/TMessagesProj/src/main/res/drawable/list_selector_background_transition_holo_light.xml new file mode 100644 index 000000000..41cae09a3 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/list_selector_background_transition_holo_light.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index 961caa6dc..31cc29bef 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -118,12 +118,6 @@ لقد قمت بتعيين التدمير الذاتي إلى %1$s %1$s قام بإيقاف عداد التدمير الذاتي لقد قمت بتعطيل عداد التدمير الذاتي - ثانيتان - ٥ ثوانٍ - دقيقة - ساعة - يوم - أسبوع لديك رسالة جديدة %1$s: %2$s %1$s قام بإرسال رسالة لك @@ -202,12 +196,6 @@ مفتاح التشفير عداد التدمير الذاتي إيقاف - ثانيتان - ٥ ثوانٍ - دقيقة - ساعة - يوم - أسبوع هذه الصورة هي تصور لمفتاح التشفير لهذه المحادثة السرية مع ]]>%1$s]]>.
]]>إذا كانت مطابقة للصورة التي في جهاز ]]>%2$s]]>, فمحادثتكم آمنة ٢٠٠٪.
]]>للمزيد نرجو الذهاب إلى telegram.org
تم تعيين كافة الإشعارات افتراضيا @@ -434,6 +422,36 @@ من %1$d جهات اتصال من %1$d جهة اتصال من %1$d جهة اتصال + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks dd MMM dd.MM.yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s الساعة %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index c6f6c6ccc..1f5e3ecec 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -118,12 +118,6 @@ Du hast den Selbstzerstörungs-Timer auf %1$s gesetzt %1$s hat den Selbstzerstörungs-Timer deaktivert Du hast den Selbstzerstörungs-Timer deaktivert - 2 Sekunden - 5 Sekunden - 1 Minute - 1 Stunde - 1 Tag - 1 Woche Du hast eine neue Nachricht %1$s: %2$s %1$s hat dir eine Nachricht gesendet @@ -202,12 +196,6 @@ Geheimer Schlüssel Selbstzerstörungs-Timer Aus - 2 Sek. - 5 Sek. - 1 Min. - 1 Std. - 1 Tag - 1 Woche Das ist eine Darstellung des Schlüssels für den Geheimen Chat mit ]]>%1$s]]>.
]]>Wenn dieses Bild auf ]]>%2$s\s]]>s Telefon genau so aussieht, ist euer Chat zu 200%% sicher.
]]>Erfahre mehr auf telegram.org
Alle Einstellungen für Mitteilungen zurücksetzen @@ -434,6 +422,36 @@ von %1$d Kontakten von %1$d Kontakten von %1$d Kontakten + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks dd MMM dd.MM.yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s um %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 70f76decd..a305a69f1 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -118,12 +118,6 @@ Activaste la autodestrucción en %1$s %1$s desactivó la autodestrucción Desactivaste la autodestrucción - 2 segundos - 5 segundos - 1 minuto - 1 hora - 1 día - 1 semana Tienes un nuevo mensaje %1$s: %2$s %1$s te envió un mensaje @@ -175,7 +169,7 @@ PON EL NOMBRE DEL GRUPO Fotos y vídeos - Información + Información FOTOS Y VÍDEOS AJUSTES Añadir miembro @@ -202,12 +196,6 @@ Clave de cifrado Autodestrucción Apagada - 2s - 5s - 1m - 1h - 1d - 1S Esta imagen es una visualización de la clave de cifrado para el chat secreto con ]]>%1$s]]>.
]]>Si esta imagen se ve igual en el teléfono de ]]>%2$s]]>, tu chat es seguro en un 200%%.
]]>Aprende más en telegram.org
Restablecer las notificaciones @@ -267,7 +255,7 @@ Desactivadas Con pantalla encendida Con pantalla apagada - Mostrar siempre + Mostrar siempre Globo en el ícono Corto Largo @@ -434,6 +422,36 @@ de %1$d contactos de %1$d contactos de %1$d contactos + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks dd \'de\' MMM dd.MM.yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s a las %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 23a75066d..ab97be9d1 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -118,12 +118,6 @@ Hai impostato il timer di autodistruzione a %1$s %1$s ha disabilitato il timer di autodistruzione Hai disabilitato il timer di autodistruzione - 2 secondi - 5 secondi - 1 minuto - 1 ora - 1 giorno - 1 settimana Hai un nuovo messaggio %1$s: %2$s %1$s ti ha inviato un messaggio @@ -202,12 +196,6 @@ Chiave di cifratura Timer di autodistruzione Spento - 2s - 5s - 1m - 1h - 1g - 1sett Questa immagine è una visualizzazione della chiave di cifratura per questa chat segreta con ]]>%1$s]]>.
]]>Se questa immagine è uguale sul telefono di ]]>%2$s]]>, la chat è sicura al 200%%.
]]>Per saperne di più, visita Telegram.org
Ripristina tutte le impostazioni di notifica predefinite @@ -434,6 +422,36 @@ da %1$d contatti da %1$d contatti da %1$d contatti + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks dd MMM dd.MM.yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s alle %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ko/strings.xml b/TMessagesProj/src/main/res/values-ko/strings.xml index 754bc2100..b17838d1f 100644 --- a/TMessagesProj/src/main/res/values-ko/strings.xml +++ b/TMessagesProj/src/main/res/values-ko/strings.xml @@ -118,12 +118,6 @@ 자동삭제를 %1$s 후로 설정했습니다 %1$s님이 자동삭제를 해제했습니다 자동삭제를 해제했습니다 - 2초 - 5초 - 1분 - 1시간 - 하루 - 일주일 새 메시지가 있습니다 %1$s: %2$s %1$s님이 메시지를 보냈습니다 @@ -202,12 +196,6 @@ 암호화 키 자동삭제 타이머 해제 - 2초 - 5초 - 1분 - 1시간 - 하루 - 일주일 이 이미지는 ]]>%1$s]]>님과의 비밀대화에 사용 중인 암호화 키의 모습입니다.
]]>이 이미지가 ]]>%2$s]]>님의 암호화 키와 똑같다면 대화는 200%% 안전합니다.
]]>더 자세한 사항은 telegram.org 를 참고해 주세요.
모든 알림 설정이 초기화되었습니다 @@ -434,6 +422,36 @@ 채팅방 %1$d개에서 채팅방 %1$d개에서 채팅방 %1$d개에서 + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks M\'월\' d\'일\' yyyy.MM.dd. @@ -444,6 +462,4 @@ HH:mm a h:mm %1$s %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index 6a790bbf3..a14614570 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -118,12 +118,6 @@ Je hebt de zelfvernietigingstimer ingesteld op %1$s %1$s heeft de zelfvernietigingstimer uitgeschakeld Je hebt de zelfvernietigingstimer uitgeschakeld - 2 seconden - 5 seconden - 1 minuut - 1 uur - 1 dag - 1 week Je hebt een nieuw bericht %1$s: %2$s %1$s heeft je een bericht gestuurd @@ -202,12 +196,6 @@ Encryptiesleutel Zelfvernietigingstimer Uit - 2s - 5s - 1m - 1u - 1d - 1w Dit is een weergave van de encryptiesleutel voor deze geheime chat met ]]>%1$s]]>.
]]>Als deze afbeelding er bij ]]>%2$s]]> hetzelfde uitziet, is jullie gesprek 200%% beveiligd.
]]>Lees meer op telegram.org.
Alle meldingsinstellingen herstellen @@ -434,6 +422,36 @@ van %1$d contactpersonen van %1$d contactpersonen van %1$d contactpersonen + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks dd MMM dd-MM-yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s om %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index 8af461b73..ae5634e1a 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -114,16 +114,10 @@ Conversa secreta solicitada Conversa secreta iniciada - %1$s estabeleceu o tempo de autodestruição para %2$s + %1$s estabeleceu o tempo de autodestruição para %2$s Você estabeleceu o tempo de autodestruição para %1$s %1$s desativou o temporizador de autodestruição Você desativou o temporizador de autodestruição - 2 segundos - 5 segundos - 1 minuto - 1 hora - 1 dia - 1 semana Você tem uma nova mensagem %1$s: %2$s %1$s te enviou uma mensagem @@ -202,12 +196,6 @@ Chave criptográfica Tempo de autodestruição Desativado - 2s - 5s - 1m - 1h - 1d - 1 sem. Esta imagem é uma visualização da chave criptográfica para esta conversa secreta com ]]>%1$s]]>.
]]>Se esta imagem aparecer da mesma forma no telefone de ]]>%2$s\'s]]>, sua conversa é 200%% segura.
]]>Saiba mais em telegram.org
Restaurar todas as configurações de notificação @@ -434,6 +422,36 @@ de %1$d contatos de %1$d contatos de %1$d contatos + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks dd MMM dd.MM.yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s às %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml index 1d0892462..90d884f2b 100644 --- a/TMessagesProj/src/main/res/values-pt-rPT/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rPT/strings.xml @@ -114,16 +114,10 @@ Conversa secreta solicitada Conversa secreta iniciada - %1$s estabeleceu o tempo de autodestruição para %2$s + %1$s estabeleceu o tempo de autodestruição para %2$s Você estabeleceu o tempo de autodestruição para %1$s %1$s desativou o temporizador de autodestruição Você desativou o temporizador de autodestruição - 2 segundos - 5 segundos - 1 minuto - 1 hora - 1 dia - 1 semana Você tem uma nova mensagem %1$s: %2$s %1$s te enviou uma mensagem @@ -202,12 +196,6 @@ Chave criptográfica Tempo de autodestruição Desativado - 2s - 5s - 1m - 1h - 1d - 1 sem. Esta imagem é uma visualização da chave criptográfica para esta conversa secreta com ]]>%1$s]]>.
]]>Se esta imagem aparecer da mesma forma no telefone de ]]>%2$s\'s]]>, sua conversa é 200%% segura.
]]>Saiba mais em telegram.org
Restaurar todas as configurações de notificação @@ -434,6 +422,36 @@ de %1$d contatos de %1$d contatos de %1$d contatos + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks dd MMM dd.MM.yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s às %2$s - - CACHE_TAG \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index b6cd51c4c..3dce45bef 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -118,12 +118,6 @@ You set the self-destruct timer to %1$s %1$s disabled the self-destruct timer You disabled the self-destruct timer - 2 seconds - 5 seconds - 1 minute - 1 hour - 1 day - 1 week You have a new message %1$s: %2$s %1$s sent you a message @@ -202,12 +196,6 @@ Encryption Key Self-Destruct Timer Off - 2s - 5s - 1m - 1h - 1d - 1w This image is a visualization of the encryption key for this secret chat with ]]>%1$s]]>.
]]>If this image looks the same on ]]>%2$s\'s]]> phone, your chat is 200%% secure.
]]>Learn more at telegram.org
Reset all notification settings to default @@ -434,6 +422,36 @@ from %1$d contacts from %1$d contacts from %1$d contacts + %1$d seconds + %1$d second + %1$d seconds + %1$d seconds + %1$d seconds + %1$d seconds + %1$d minutes + %1$d minute + %1$d minutes + %1$d minutes + %1$d minutes + %1$d minutes + %1$d hours + %1$d hour + %1$d hours + %1$d hours + %1$d hours + %1$d hours + %1$d days + %1$d day + %1$d days + %1$d days + %1$d days + %1$d days + %1$d weeks + %1$d week + %1$d weeks + %1$d weeks + %1$d weeks + %1$d weeks MMM dd dd.MM.yy @@ -444,6 +462,4 @@ HH:mm h:mm a %1$s at %2$s - - CACHE_TAG \ No newline at end of file