mirror of
https://github.com/DrKLO/Telegram.git
synced 2024-12-22 06:25:14 +01:00
Update to 3.1.1
This commit is contained in:
parent
a93d299484
commit
82f9be238d
422 changed files with 10937 additions and 1587 deletions
|
@ -73,7 +73,7 @@ android {
|
|||
defaultConfig {
|
||||
minSdkVersion 8
|
||||
targetSdkVersion 22
|
||||
versionCode 572
|
||||
versionName "3.0.1"
|
||||
versionCode 580
|
||||
versionName "3.1.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,8 +161,20 @@
|
|||
|
||||
<service android:name="org.telegram.android.NotificationsService" android:enabled="true"/>
|
||||
<service android:name="org.telegram.android.NotificationRepeat" android:exported="false"/>
|
||||
<service android:name="org.telegram.android.NotificationDelay" android:exported="false"/>
|
||||
<service android:name="org.telegram.android.VideoEncodingService" android:enabled="true"/>
|
||||
<service android:name="org.telegram.android.MusicPlayerService" android:exported="true" android:enabled="true"/>
|
||||
|
||||
<receiver android:name="org.telegram.android.MusicPlayerReceiver" >
|
||||
<intent-filter>
|
||||
<action android:name="org.telegram.android.musicplayer.close" />
|
||||
<action android:name="org.telegram.android.musicplayer.pause" />
|
||||
<action android:name="org.telegram.android.musicplayer.next" />
|
||||
<action android:name="org.telegram.android.musicplayer.play" />
|
||||
<action android:name="org.telegram.android.musicplayer.previous" />
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="org.telegram.android.AppStartReceiver" android:enabled="true">
|
||||
<intent-filter>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
package org.telegram.SQLite;
|
||||
|
||||
import org.telegram.messenger.BuildVars;
|
||||
import org.telegram.messenger.FileLog;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -30,7 +29,7 @@ public class SQLitePreparedStatement {
|
|||
public SQLitePreparedStatement(SQLiteDatabase db, String sql, boolean finalize) throws SQLiteException {
|
||||
finalizeAfterQuery = finalize;
|
||||
sqliteStatementHandle = prepare(db.getSQLiteHandle(), sql);
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
/*if (BuildVars.DEBUG_VERSION) {
|
||||
if (hashMap == null) {
|
||||
hashMap = new HashMap<>();
|
||||
}
|
||||
|
@ -38,7 +37,7 @@ public class SQLitePreparedStatement {
|
|||
for (HashMap.Entry<SQLitePreparedStatement, String> entry : hashMap.entrySet()) {
|
||||
FileLog.d("tmessages", "exist entry = " + entry.getValue());
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
|
@ -101,9 +100,9 @@ public class SQLitePreparedStatement {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
/*if (BuildVars.DEBUG_VERSION) {
|
||||
hashMap.remove(this);
|
||||
}
|
||||
}*/
|
||||
isFinalized = true;
|
||||
finalize(sqliteStatementHandle);
|
||||
} catch (SQLiteException e) {
|
||||
|
|
|
@ -542,16 +542,6 @@ public class AndroidUtilities {
|
|||
return 0;
|
||||
}
|
||||
|
||||
public static int getCurrentActionBarHeight() {
|
||||
if (isTablet()) {
|
||||
return dp(64);
|
||||
} else if (ApplicationLoader.applicationContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
return dp(48);
|
||||
} else {
|
||||
return dp(56);
|
||||
}
|
||||
}
|
||||
|
||||
public static Point getRealScreenSize() {
|
||||
Point size = new Point();
|
||||
try {
|
||||
|
|
|
@ -66,13 +66,13 @@ public class AnimatorSetProxy {
|
|||
|
||||
public void playTogether(ArrayList<Object> items) {
|
||||
if (View10.NEED_PROXY) {
|
||||
ArrayList<Animator10> animators = new ArrayList<Animator10>();
|
||||
ArrayList<Animator10> animators = new ArrayList<>();
|
||||
for (Object obj : items) {
|
||||
animators.add((Animator10)obj);
|
||||
}
|
||||
((AnimatorSet10) animatorSet).playTogether(animators);
|
||||
} else {
|
||||
ArrayList<Animator> animators = new ArrayList<Animator>();
|
||||
ArrayList<Animator> animators = new ArrayList<>();
|
||||
for (Object obj : items) {
|
||||
animators.add((Animator)obj);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.database.Cursor;
|
|||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
import android.provider.ContactsContract;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import org.telegram.PhoneFormat.PhoneFormat;
|
||||
|
@ -330,7 +331,7 @@ public class ContactsController {
|
|||
ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver();
|
||||
|
||||
HashMap<String, Contact> shortContacts = new HashMap<>();
|
||||
StringBuilder ids = new StringBuilder();
|
||||
ArrayList<Integer> idsArr = new ArrayList<>();
|
||||
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectionPhones, null, null, null);
|
||||
if (pCur != null) {
|
||||
if (pCur.getCount() > 0) {
|
||||
|
@ -355,10 +356,9 @@ public class ContactsController {
|
|||
}
|
||||
|
||||
Integer id = pCur.getInt(0);
|
||||
if (ids.length() != 0) {
|
||||
ids.append(",");
|
||||
if (!idsArr.contains(id)) {
|
||||
idsArr.add(id);
|
||||
}
|
||||
ids.append(id);
|
||||
|
||||
int type = pCur.getInt(2);
|
||||
Contact contact = contactsMap.get(id);
|
||||
|
@ -392,8 +392,9 @@ public class ContactsController {
|
|||
}
|
||||
pCur.close();
|
||||
}
|
||||
String ids = TextUtils.join(",", idsArr);
|
||||
|
||||
pCur = cr.query(ContactsContract.Data.CONTENT_URI, projectionNames, ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " IN (" + ids.toString() + ") AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'", null, null);
|
||||
pCur = cr.query(ContactsContract.Data.CONTENT_URI, projectionNames, ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " IN (" + ids + ") AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'", null, null);
|
||||
if (pCur != null && pCur.getCount() > 0) {
|
||||
while (pCur.moveToNext()) {
|
||||
int id = pCur.getInt(0);
|
||||
|
@ -474,6 +475,23 @@ public class ContactsController {
|
|||
FileLog.e("tmessages", e);
|
||||
contactsMap.clear();
|
||||
}
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
for (HashMap.Entry<Integer, Contact> entry : contactsMap.entrySet()) {
|
||||
Contact contact = entry.getValue();
|
||||
FileLog.e("tmessages", "contact = " + contact.first_name + " " + contact.last_name);
|
||||
if (contact.first_name.length() == 0 && contact.last_name.length() == 0 && contact.phones.size() > 0) {
|
||||
FileLog.e("tmessages", "warning, empty name for contact = " + contact.id);
|
||||
}
|
||||
FileLog.e("tmessages", "phones:");
|
||||
for (String s : contact.phones) {
|
||||
FileLog.e("tmessages", "phone = " + s);
|
||||
}
|
||||
FileLog.e("tmessages", "short phones:");
|
||||
for (String s : contact.shortPhones) {
|
||||
FileLog.e("tmessages", "short phone = " + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
return contactsMap;
|
||||
}
|
||||
|
||||
|
@ -569,7 +587,7 @@ public class ContactsController {
|
|||
}
|
||||
}
|
||||
|
||||
boolean nameChanged = existing != null && (!existing.first_name.equals(value.first_name) || !existing.last_name.equals(value.last_name));
|
||||
boolean nameChanged = existing != null && (value.first_name.length() != 0 && !existing.first_name.equals(value.first_name) || value.last_name != null && !existing.last_name.equals(value.last_name));
|
||||
if (existing == null || nameChanged) {
|
||||
for (int a = 0; a < value.phones.size(); a++) {
|
||||
String sphone = value.shortPhones.get(a);
|
||||
|
@ -607,8 +625,12 @@ public class ContactsController {
|
|||
int index = existing.shortPhones.indexOf(sphone);
|
||||
if (index == -1) {
|
||||
if (request) {
|
||||
if (contactsByPhone.containsKey(sphone)) {
|
||||
continue;
|
||||
TLRPC.TL_contact contact = contactsByPhone.get(sphone);
|
||||
if (contact != null) {
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(contact.user_id);
|
||||
if (user == null || user.first_name != null && user.first_name.length() != 0 || user.last_name != null && user.last_name.length() != 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
||||
|
@ -702,8 +724,12 @@ public class ContactsController {
|
|||
int id = pair.getKey();
|
||||
for (int a = 0; a < value.phones.size(); a++) {
|
||||
String phone = value.shortPhones.get(a);
|
||||
if (contactsByPhone.containsKey(phone)) {
|
||||
continue;
|
||||
TLRPC.TL_contact contact = contactsByPhone.get(phone);
|
||||
if (contact != null) {
|
||||
TLRPC.User user = MessagesController.getInstance().getUser(contact.user_id);
|
||||
if (user == null || user.first_name != null && user.first_name.length() != 0 || user.last_name != null && user.last_name.length() != 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
||||
imp.client_id = id;
|
||||
|
@ -721,9 +747,9 @@ public class ContactsController {
|
|||
if (!toImport.isEmpty()) {
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
FileLog.e("tmessages", "start import contacts");
|
||||
// for (TLRPC.TL_inputPhoneContact contact : toImport) {
|
||||
// FileLog.e("tmessages", "add contact " + contact.first_name + " " + contact.last_name + " " + contact.phone);
|
||||
// }
|
||||
for (TLRPC.TL_inputPhoneContact contact : toImport) {
|
||||
FileLog.e("tmessages", "add contact " + contact.first_name + " " + contact.last_name + " " + contact.phone);
|
||||
}
|
||||
}
|
||||
final int count = (int)Math.ceil(toImport.size() / 500.0f);
|
||||
for (int a = 0; a < count; a++) {
|
||||
|
@ -743,9 +769,9 @@ public class ContactsController {
|
|||
}
|
||||
TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
// for (TLRPC.User user : res.users) {
|
||||
// FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
// }
|
||||
for (TLRPC.User user : res.users) {
|
||||
FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
}
|
||||
}
|
||||
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
||||
ArrayList<TLRPC.TL_contact> cArr = new ArrayList<>();
|
||||
|
@ -903,9 +929,9 @@ public class ContactsController {
|
|||
if (user != null) {
|
||||
usersDict.put(user.id, user);
|
||||
|
||||
// if (BuildVars.DEBUG_VERSION) {
|
||||
// FileLog.e("tmessages", "loaded user contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
// }
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
FileLog.e("tmessages", "loaded user contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1524,9 +1550,9 @@ public class ContactsController {
|
|||
contactsParams.add(c);
|
||||
req.contacts = contactsParams;
|
||||
req.replace = false;
|
||||
// if (BuildVars.DEBUG_VERSION) {
|
||||
// FileLog.e("tmessages", "add contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
// }
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
FileLog.e("tmessages", "add contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
}
|
||||
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
|
||||
@Override
|
||||
public void run(TLObject response, TLRPC.TL_error error) {
|
||||
|
@ -1536,11 +1562,11 @@ public class ContactsController {
|
|||
final TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
|
||||
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
||||
|
||||
// if (BuildVars.DEBUG_VERSION) {
|
||||
// for (TLRPC.User user : res.users) {
|
||||
// FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
// }
|
||||
// }
|
||||
if (BuildVars.DEBUG_VERSION) {
|
||||
for (TLRPC.User user : res.users) {
|
||||
FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||
}
|
||||
}
|
||||
|
||||
for (final TLRPC.User u : res.users) {
|
||||
Utilities.phoneBookQueue.postRunnable(new Runnable() {
|
||||
|
|
|
@ -33,13 +33,13 @@ import org.telegram.messenger.Utilities;
|
|||
import org.telegram.messenger.ApplicationLoader;
|
||||
|
||||
public class Emoji {
|
||||
private static HashMap<Long, DrawableInfo> rects = new HashMap<>();
|
||||
private static int drawImgSize;
|
||||
private static HashMap<Long, DrawableInfo> rects = new HashMap<>();
|
||||
private static int drawImgSize;
|
||||
private static int bigImgSize;
|
||||
private static boolean inited = false;
|
||||
private static Paint placeholderPaint;
|
||||
private static Bitmap emojiBmp[] = new Bitmap[5];
|
||||
private static boolean loadingEmoji[] = new boolean[5];
|
||||
private static boolean inited = false;
|
||||
private static Paint placeholderPaint;
|
||||
private static Bitmap emojiBmp[] = new Bitmap[5];
|
||||
private static boolean loadingEmoji[] = new boolean[5];
|
||||
|
||||
private static final int[] cols = {
|
||||
13, 10, 15, 10, 14
|
||||
|
@ -60,139 +60,144 @@ public class Emoji {
|
|||
0x2B1C, 0x2B50, 0x2B55, 0x3030, 0x303D, 0x3297, 0x3299
|
||||
};
|
||||
|
||||
public static long[][] data = {
|
||||
public static long[][] data = {
|
||||
new long[]
|
||||
{},
|
||||
new long[]//189
|
||||
{0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL,
|
||||
0x00000000D83DDE18L, 0x00000000D83DDE1AL, 0x00000000D83DDE17L, 0x00000000D83DDE19L, 0x00000000D83DDE1CL, 0x00000000D83DDE1DL, 0x00000000D83DDE1BL,
|
||||
0x00000000D83DDE33L, 0x00000000D83DDE01L, 0x00000000D83DDE14L, 0x00000000D83DDE0CL, 0x00000000D83DDE12L, 0x00000000D83DDE1EL, 0x00000000D83DDE23L,
|
||||
0x00000000D83DDE22L, 0x00000000D83DDE02L, 0x00000000D83DDE2DL, 0x00000000D83DDE2AL, 0x00000000D83DDE25L, 0x00000000D83DDE30L, 0x00000000D83DDE05L,
|
||||
0x00000000D83DDE13L, 0x00000000D83DDE29L, 0x00000000D83DDE2BL, 0x00000000D83DDE28L, 0x00000000D83DDE31L, 0x00000000D83DDE20L, 0x00000000D83DDE21L,
|
||||
0x00000000D83DDE24L, 0x00000000D83DDE16L, 0x00000000D83DDE06L, 0x00000000D83DDE0BL, 0x00000000D83DDE37L, 0x00000000D83DDE0EL, 0x00000000D83DDE34L,
|
||||
0x00000000D83DDE35L, 0x00000000D83DDE32L, 0x00000000D83DDE1FL, 0x00000000D83DDE26L, 0x00000000D83DDE27L, 0x00000000D83DDE08L, 0x00000000D83DDC7FL,
|
||||
0x00000000D83DDE2EL, 0x00000000D83DDE2CL, 0x00000000D83DDE10L, 0x00000000D83DDE15L, 0x00000000D83DDE2FL, 0x00000000D83DDE36L, 0x00000000D83DDE07L,
|
||||
0x00000000D83DDE0FL, 0x00000000D83DDE11L, 0x00000000D83DDC72L, 0x00000000D83DDC73L, 0x00000000D83DDC6EL, 0x00000000D83DDC77L, 0x00000000D83DDC82L,
|
||||
0x00000000D83DDC76L, 0x00000000D83DDC66L, 0x00000000D83DDC67L, 0x00000000D83DDC68L, 0x00000000D83DDC69L, 0x00000000D83DDC74L, 0x00000000D83DDC75L,
|
||||
0x00000000D83DDC71L, 0x00000000D83DDC7CL, 0x00000000D83DDC78L, 0x00000000D83DDE3AL, 0x00000000D83DDE38L, 0x00000000D83DDE3BL, 0x00000000D83DDE3DL,
|
||||
0x00000000D83DDE3CL, 0x00000000D83DDE40L, 0x00000000D83DDE3FL, 0x00000000D83DDE39L, 0x00000000D83DDE3EL, 0x00000000D83DDC79L, 0x00000000D83DDC7AL,
|
||||
0x00000000D83DDE48L, 0x00000000D83DDE49L, 0x00000000D83DDE4AL, 0x00000000D83DDC80L, 0x00000000D83DDC7DL, 0x00000000D83DDCA9L, 0x00000000D83DDD25L,
|
||||
0x0000000000002728L, 0x00000000D83CDF1FL, 0x00000000D83DDCABL, 0x00000000D83DDCA5L, 0x00000000D83DDCA2L, 0x00000000D83DDCA6L, 0x00000000D83DDCA7L,
|
||||
0x00000000D83DDCA4L, 0x00000000D83DDCA8L, 0x00000000D83DDC42L, 0x00000000D83DDC40L, 0x00000000D83DDC43L, 0x00000000D83DDC45L, 0x00000000D83DDC44L,
|
||||
0x00000000D83DDC4DL, 0x00000000D83DDC4EL, 0x00000000D83DDC4CL, 0x00000000D83DDC4AL, 0x000000000000270AL, 0x000000000000270CL, 0x00000000D83DDC4BL,
|
||||
0x000000000000270BL, 0x00000000D83DDC50L, 0x00000000D83DDC46L, 0x00000000D83DDC47L, 0x00000000D83DDC49L, 0x00000000D83DDC48L, 0x00000000D83DDE4CL,
|
||||
0x00000000D83DDE4FL, 0x000000000000261DL, 0x00000000D83DDC4FL, 0x00000000D83DDCAAL, 0x00000000D83DDEB6L, 0x00000000D83CDFC3L, 0x00000000D83DDC83L,
|
||||
0x00000000D83DDC6BL, 0x00000000D83DDC6AL, 0x00000000D83DDC6CL, 0x00000000D83DDC6DL, 0x00000000D83DDC8FL, 0x00000000D83DDC91L, 0x00000000D83DDC6FL,
|
||||
0x00000000D83DDE46L, 0x00000000D83DDE45L, 0x00000000D83DDC81L, 0x00000000D83DDE4BL, 0x00000000D83DDC86L, 0x00000000D83DDC87L, 0x00000000D83DDC85L,
|
||||
0x00000000D83DDC70L, 0x00000000D83DDE4EL, 0x00000000D83DDE4DL, 0x00000000D83DDE47L, 0x00000000D83CDFA9L, 0x00000000D83DDC51L, 0x00000000D83DDC52L,
|
||||
0x00000000D83DDC5FL, 0x00000000D83DDC5EL, 0x00000000D83DDC61L, 0x00000000D83DDC60L, 0x00000000D83DDC62L, 0x00000000D83DDC55L, 0x00000000D83DDC54L,
|
||||
0x00000000D83DDC5AL, 0x00000000D83DDC57L, 0x00000000D83CDFBDL, 0x00000000D83DDC56L, 0x00000000D83DDC58L, 0x00000000D83DDC59L, 0x00000000D83DDCBCL,
|
||||
0x00000000D83DDC5CL, 0x00000000D83DDC5DL, 0x00000000D83DDC5BL, 0x00000000D83DDC53L, 0x00000000D83CDF80L, 0x00000000D83CDF02L, 0x00000000D83DDC84L,
|
||||
0x00000000D83DDC9BL, 0x00000000D83DDC99L, 0x00000000D83DDC9CL, 0x00000000D83DDC9AL, 0x0000000000002764L, 0x00000000D83DDC94L, 0x00000000D83DDC97L,
|
||||
0x00000000D83DDC93L, 0x00000000D83DDC95L, 0x00000000D83DDC96L, 0x00000000D83DDC9EL, 0x00000000D83DDC98L, 0x00000000D83DDC8CL, 0x00000000D83DDC8BL,
|
||||
0x00000000D83DDC8DL, 0x00000000D83DDC8EL, 0x00000000D83DDC64L, 0x00000000D83DDC65L, 0x00000000D83DDCACL, 0x00000000D83DDC63L, 0x00000000D83DDCADL},
|
||||
{
|
||||
0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL,
|
||||
0x00000000D83DDE18L, 0x00000000D83DDE1AL, 0x00000000D83DDE17L, 0x00000000D83DDE19L, 0x00000000D83DDE1CL, 0x00000000D83DDE1DL, 0x00000000D83DDE1BL,
|
||||
0x00000000D83DDE33L, 0x00000000D83DDE01L, 0x00000000D83DDE14L, 0x00000000D83DDE0CL, 0x00000000D83DDE12L, 0x00000000D83DDE1EL, 0x00000000D83DDE23L,
|
||||
0x00000000D83DDE22L, 0x00000000D83DDE02L, 0x00000000D83DDE2DL, 0x00000000D83DDE2AL, 0x00000000D83DDE25L, 0x00000000D83DDE30L, 0x00000000D83DDE05L,
|
||||
0x00000000D83DDE13L, 0x00000000D83DDE29L, 0x00000000D83DDE2BL, 0x00000000D83DDE28L, 0x00000000D83DDE31L, 0x00000000D83DDE20L, 0x00000000D83DDE21L,
|
||||
0x00000000D83DDE24L, 0x00000000D83DDE16L, 0x00000000D83DDE06L, 0x00000000D83DDE0BL, 0x00000000D83DDE37L, 0x00000000D83DDE0EL, 0x00000000D83DDE34L,
|
||||
0x00000000D83DDE35L, 0x00000000D83DDE32L, 0x00000000D83DDE1FL, 0x00000000D83DDE26L, 0x00000000D83DDE27L, 0x00000000D83DDE08L, 0x00000000D83DDC7FL,
|
||||
0x00000000D83DDE2EL, 0x00000000D83DDE2CL, 0x00000000D83DDE10L, 0x00000000D83DDE15L, 0x00000000D83DDE2FL, 0x00000000D83DDE36L, 0x00000000D83DDE07L,
|
||||
0x00000000D83DDE0FL, 0x00000000D83DDE11L, 0x00000000D83DDC72L, 0x00000000D83DDC73L, 0x00000000D83DDC6EL, 0x00000000D83DDC77L, 0x00000000D83DDC82L,
|
||||
0x00000000D83DDC76L, 0x00000000D83DDC66L, 0x00000000D83DDC67L, 0x00000000D83DDC68L, 0x00000000D83DDC69L, 0x00000000D83DDC74L, 0x00000000D83DDC75L,
|
||||
0x00000000D83DDC71L, 0x00000000D83DDC7CL, 0x00000000D83DDC78L, 0x00000000D83DDE3AL, 0x00000000D83DDE38L, 0x00000000D83DDE3BL, 0x00000000D83DDE3DL,
|
||||
0x00000000D83DDE3CL, 0x00000000D83DDE40L, 0x00000000D83DDE3FL, 0x00000000D83DDE39L, 0x00000000D83DDE3EL, 0x00000000D83DDC79L, 0x00000000D83DDC7AL,
|
||||
0x00000000D83DDE48L, 0x00000000D83DDE49L, 0x00000000D83DDE4AL, 0x00000000D83DDC80L, 0x00000000D83DDC7DL, 0x00000000D83DDCA9L, 0x00000000D83DDD25L,
|
||||
0x0000000000002728L, 0x00000000D83CDF1FL, 0x00000000D83DDCABL, 0x00000000D83DDCA5L, 0x00000000D83DDCA2L, 0x00000000D83DDCA6L, 0x00000000D83DDCA7L,
|
||||
0x00000000D83DDCA4L, 0x00000000D83DDCA8L, 0x00000000D83DDC42L, 0x00000000D83DDC40L, 0x00000000D83DDC43L, 0x00000000D83DDC45L, 0x00000000D83DDC44L,
|
||||
0x00000000D83DDC4DL, 0x00000000D83DDC4EL, 0x00000000D83DDC4CL, 0x00000000D83DDC4AL, 0x000000000000270AL, 0x000000000000270CL, 0x00000000D83DDC4BL,
|
||||
0x000000000000270BL, 0x00000000D83DDC50L, 0x00000000D83DDC46L, 0x00000000D83DDC47L, 0x00000000D83DDC49L, 0x00000000D83DDC48L, 0x00000000D83DDE4CL,
|
||||
0x00000000D83DDE4FL, 0x000000000000261DL, 0x00000000D83DDC4FL, 0x00000000D83DDCAAL, 0x00000000D83DDEB6L, 0x00000000D83CDFC3L, 0x00000000D83DDC83L,
|
||||
0x00000000D83DDC6BL, 0x00000000D83DDC6AL, 0x00000000D83DDC6CL, 0x00000000D83DDC6DL, 0x00000000D83DDC8FL, 0x00000000D83DDC91L, 0x00000000D83DDC6FL,
|
||||
0x00000000D83DDE46L, 0x00000000D83DDE45L, 0x00000000D83DDC81L, 0x00000000D83DDE4BL, 0x00000000D83DDC86L, 0x00000000D83DDC87L, 0x00000000D83DDC85L,
|
||||
0x00000000D83DDC70L, 0x00000000D83DDE4EL, 0x00000000D83DDE4DL, 0x00000000D83DDE47L, 0x00000000D83CDFA9L, 0x00000000D83DDC51L, 0x00000000D83DDC52L,
|
||||
0x00000000D83DDC5FL, 0x00000000D83DDC5EL, 0x00000000D83DDC61L, 0x00000000D83DDC60L, 0x00000000D83DDC62L, 0x00000000D83DDC55L, 0x00000000D83DDC54L,
|
||||
0x00000000D83DDC5AL, 0x00000000D83DDC57L, 0x00000000D83CDFBDL, 0x00000000D83DDC56L, 0x00000000D83DDC58L, 0x00000000D83DDC59L, 0x00000000D83DDCBCL,
|
||||
0x00000000D83DDC5CL, 0x00000000D83DDC5DL, 0x00000000D83DDC5BL, 0x00000000D83DDC53L, 0x00000000D83CDF80L, 0x00000000D83CDF02L, 0x00000000D83DDC84L,
|
||||
0x00000000D83DDC9BL, 0x00000000D83DDC99L, 0x00000000D83DDC9CL, 0x00000000D83DDC9AL, 0x0000000000002764L, 0x00000000D83DDC94L, 0x00000000D83DDC97L,
|
||||
0x00000000D83DDC93L, 0x00000000D83DDC95L, 0x00000000D83DDC96L, 0x00000000D83DDC9EL, 0x00000000D83DDC98L, 0x00000000D83DDC8CL, 0x00000000D83DDC8BL,
|
||||
0x00000000D83DDC8DL, 0x00000000D83DDC8EL, 0x00000000D83DDC64L, 0x00000000D83DDC65L, 0x00000000D83DDCACL, 0x00000000D83DDC63L, 0x00000000D83DDCADL},
|
||||
new long[]//116
|
||||
{0x00000000D83DDC36L, 0x00000000D83DDC3AL, 0x00000000D83DDC31L, 0x00000000D83DDC2DL, 0x00000000D83DDC39L, 0x00000000D83DDC30L, 0x00000000D83DDC38L, 0x00000000D83DDC2FL,
|
||||
0x00000000D83DDC28L, 0x00000000D83DDC3BL, 0x00000000D83DDC37L, 0x00000000D83DDC3DL, 0x00000000D83DDC2EL, 0x00000000D83DDC17L, 0x00000000D83DDC35L,
|
||||
0x00000000D83DDC12L, 0x00000000D83DDC34L, 0x00000000D83DDC11L, 0x00000000D83DDC18L, 0x00000000D83DDC3CL, 0x00000000D83DDC27L, 0x00000000D83DDC26L,
|
||||
0x00000000D83DDC24L, 0x00000000D83DDC25L, 0x00000000D83DDC23L, 0x00000000D83DDC14L, 0x00000000D83DDC0DL, 0x00000000D83DDC22L, 0x00000000D83DDC1BL,
|
||||
0x00000000D83DDC1DL, 0x00000000D83DDC1CL, 0x00000000D83DDC1EL, 0x00000000D83DDC0CL, 0x00000000D83DDC19L, 0x00000000D83DDC1AL, 0x00000000D83DDC20L,
|
||||
0x00000000D83DDC1FL, 0x00000000D83DDC2CL, 0x00000000D83DDC33L, 0x00000000D83DDC0BL, 0x00000000D83DDC04L, 0x00000000D83DDC0FL, 0x00000000D83DDC00L,
|
||||
0x00000000D83DDC03L, 0x00000000D83DDC05L, 0x00000000D83DDC07L, 0x00000000D83DDC09L, 0x00000000D83DDC0EL, 0x00000000D83DDC10L, 0x00000000D83DDC13L,
|
||||
0x00000000D83DDC15L, 0x00000000D83DDC16L, 0x00000000D83DDC01L, 0x00000000D83DDC02L, 0x00000000D83DDC32L, 0x00000000D83DDC21L, 0x00000000D83DDC0AL,
|
||||
0x00000000D83DDC2BL, 0x00000000D83DDC2AL, 0x00000000D83DDC06L, 0x00000000D83DDC08L, 0x00000000D83DDC29L, 0x00000000D83DDC3EL, 0x00000000D83DDC90L,
|
||||
0x00000000D83CDF38L, 0x00000000D83CDF37L, 0x00000000D83CDF40L, 0x00000000D83CDF39L, 0x00000000D83CDF3BL, 0x00000000D83CDF3AL, 0x00000000D83CDF41L,
|
||||
0x00000000D83CDF43L, 0x00000000D83CDF42L, 0x00000000D83CDF3FL, 0x00000000D83CDF3EL, 0x00000000D83CDF44L, 0x00000000D83CDF35L, 0x00000000D83CDF34L,
|
||||
0x00000000D83CDF32L, 0x00000000D83CDF33L, 0x00000000D83CDF30L, 0x00000000D83CDF31L, 0x00000000D83CDF3CL, 0x00000000D83CDF10L, 0x00000000D83CDF1EL,
|
||||
0x00000000D83CDF1DL, 0x00000000D83CDF1AL, 0x00000000D83CDF11L, 0x00000000D83CDF12L, 0x00000000D83CDF13L, 0x00000000D83CDF14L, 0x00000000D83CDF15L,
|
||||
0x00000000D83CDF16L, 0x00000000D83CDF17L, 0x00000000D83CDF18L, 0x00000000D83CDF1CL, 0x00000000D83CDF1BL, 0x00000000D83CDF19L, 0x00000000D83CDF0DL,
|
||||
0x00000000D83CDF0EL, 0x00000000D83CDF0FL, 0x00000000D83CDF0BL, 0x00000000D83CDF0CL, 0x00000000D83CDF20L, 0x0000000000002B50L, 0x0000000000002600L,
|
||||
0x00000000000026C5L, 0x0000000000002601L, 0x00000000000026A1L, 0x0000000000002614L, 0x0000000000002744L, 0x00000000000026C4L, 0x00000000D83CDF00L,
|
||||
0x00000000D83CDF01L, 0x00000000D83CDF08L, 0x00000000D83CDF0AL},
|
||||
{
|
||||
0x00000000D83DDC36L, 0x00000000D83DDC3AL, 0x00000000D83DDC31L, 0x00000000D83DDC2DL, 0x00000000D83DDC39L, 0x00000000D83DDC30L, 0x00000000D83DDC38L, 0x00000000D83DDC2FL,
|
||||
0x00000000D83DDC28L, 0x00000000D83DDC3BL, 0x00000000D83DDC37L, 0x00000000D83DDC3DL, 0x00000000D83DDC2EL, 0x00000000D83DDC17L, 0x00000000D83DDC35L,
|
||||
0x00000000D83DDC12L, 0x00000000D83DDC34L, 0x00000000D83DDC11L, 0x00000000D83DDC18L, 0x00000000D83DDC3CL, 0x00000000D83DDC27L, 0x00000000D83DDC26L,
|
||||
0x00000000D83DDC24L, 0x00000000D83DDC25L, 0x00000000D83DDC23L, 0x00000000D83DDC14L, 0x00000000D83DDC0DL, 0x00000000D83DDC22L, 0x00000000D83DDC1BL,
|
||||
0x00000000D83DDC1DL, 0x00000000D83DDC1CL, 0x00000000D83DDC1EL, 0x00000000D83DDC0CL, 0x00000000D83DDC19L, 0x00000000D83DDC1AL, 0x00000000D83DDC20L,
|
||||
0x00000000D83DDC1FL, 0x00000000D83DDC2CL, 0x00000000D83DDC33L, 0x00000000D83DDC0BL, 0x00000000D83DDC04L, 0x00000000D83DDC0FL, 0x00000000D83DDC00L,
|
||||
0x00000000D83DDC03L, 0x00000000D83DDC05L, 0x00000000D83DDC07L, 0x00000000D83DDC09L, 0x00000000D83DDC0EL, 0x00000000D83DDC10L, 0x00000000D83DDC13L,
|
||||
0x00000000D83DDC15L, 0x00000000D83DDC16L, 0x00000000D83DDC01L, 0x00000000D83DDC02L, 0x00000000D83DDC32L, 0x00000000D83DDC21L, 0x00000000D83DDC0AL,
|
||||
0x00000000D83DDC2BL, 0x00000000D83DDC2AL, 0x00000000D83DDC06L, 0x00000000D83DDC08L, 0x00000000D83DDC29L, 0x00000000D83DDC3EL, 0x00000000D83DDC90L,
|
||||
0x00000000D83CDF38L, 0x00000000D83CDF37L, 0x00000000D83CDF40L, 0x00000000D83CDF39L, 0x00000000D83CDF3BL, 0x00000000D83CDF3AL, 0x00000000D83CDF41L,
|
||||
0x00000000D83CDF43L, 0x00000000D83CDF42L, 0x00000000D83CDF3FL, 0x00000000D83CDF3EL, 0x00000000D83CDF44L, 0x00000000D83CDF35L, 0x00000000D83CDF34L,
|
||||
0x00000000D83CDF32L, 0x00000000D83CDF33L, 0x00000000D83CDF30L, 0x00000000D83CDF31L, 0x00000000D83CDF3CL, 0x00000000D83CDF10L, 0x00000000D83CDF1EL,
|
||||
0x00000000D83CDF1DL, 0x00000000D83CDF1AL, 0x00000000D83CDF11L, 0x00000000D83CDF12L, 0x00000000D83CDF13L, 0x00000000D83CDF14L, 0x00000000D83CDF15L,
|
||||
0x00000000D83CDF16L, 0x00000000D83CDF17L, 0x00000000D83CDF18L, 0x00000000D83CDF1CL, 0x00000000D83CDF1BL, 0x00000000D83CDF19L, 0x00000000D83CDF0DL,
|
||||
0x00000000D83CDF0EL, 0x00000000D83CDF0FL, 0x00000000D83CDF0BL, 0x00000000D83CDF0CL, 0x00000000D83CDF20L, 0x0000000000002B50L, 0x0000000000002600L,
|
||||
0x00000000000026C5L, 0x0000000000002601L, 0x00000000000026A1L, 0x0000000000002614L, 0x0000000000002744L, 0x00000000000026C4L, 0x00000000D83CDF00L,
|
||||
0x00000000D83CDF01L, 0x00000000D83CDF08L, 0x00000000D83CDF0AL},
|
||||
new long[]//230
|
||||
{0x00000000D83CDF8DL, 0x00000000D83DDC9DL, 0x00000000D83CDF8EL, 0x00000000D83CDF92L, 0x00000000D83CDF93L, 0x00000000D83CDF8FL, 0x00000000D83CDF86L, 0x00000000D83CDF87L,
|
||||
0x00000000D83CDF90L, 0x00000000D83CDF91L, 0x00000000D83CDF83L, 0x00000000D83DDC7BL, 0x00000000D83CDF85L, 0x00000000D83CDF84L, 0x00000000D83CDF81L,
|
||||
0x00000000D83CDF8BL, 0x00000000D83CDF89L, 0x00000000D83CDF8AL, 0x00000000D83CDF88L, 0x00000000D83CDF8CL, 0x00000000D83DDD2EL, 0x00000000D83CDFA5L,
|
||||
0x00000000D83DDCF7L, 0x00000000D83DDCF9L, 0x00000000D83DDCFCL, 0x00000000D83DDCBFL, 0x00000000D83DDCC0L, 0x00000000D83DDCBDL, 0x00000000D83DDCBEL,
|
||||
0x00000000D83DDCBBL, 0x00000000D83DDCF1L, 0x000000000000260EL, 0x00000000D83DDCDEL, 0x00000000D83DDCDFL, 0x00000000D83DDCE0L, 0x00000000D83DDCE1L,
|
||||
0x00000000D83DDCFAL, 0x00000000D83DDCFBL, 0x00000000D83DDD0AL, 0x00000000D83DDD09L, 0x00000000D83DDD08L, 0x00000000D83DDD07L, 0x00000000D83DDD14L,
|
||||
0x00000000D83DDD15L, 0x00000000D83DDCE2L, 0x00000000D83DDCE3L, 0x00000000000023F3L, 0x000000000000231BL, 0x00000000000023F0L, 0x000000000000231AL,
|
||||
0x00000000D83DDD13L, 0x00000000D83DDD12L, 0x00000000D83DDD0FL, 0x00000000D83DDD10L, 0x00000000D83DDD11L, 0x00000000D83DDD0EL, 0x00000000D83DDCA1L,
|
||||
0x00000000D83DDD26L, 0x00000000D83DDD06L, 0x00000000D83DDD05L, 0x00000000D83DDD0CL, 0x00000000D83DDD0BL, 0x00000000D83DDD0DL, 0x00000000D83DDEC1L, 0x00000000D83DDEC0L,
|
||||
0x00000000D83DDEBFL, 0x00000000D83DDEBDL, 0x00000000D83DDD27L, 0x00000000D83DDD29L, 0x00000000D83DDD28L, 0x00000000D83DDEAAL, 0x00000000D83DDEACL,
|
||||
0x00000000D83DDCA3L, 0x00000000D83DDD2BL, 0x00000000D83DDD2AL, 0x00000000D83DDC8AL, 0x00000000D83DDC89L, 0x00000000D83DDCB0L, 0x00000000D83DDCB4L,
|
||||
0x00000000D83DDCB5L, 0x00000000D83DDCB7L, 0x00000000D83DDCB6L, 0x00000000D83DDCB3L, 0x00000000D83DDCB8L, 0x00000000D83DDCF2L, 0x00000000D83DDCE7L,
|
||||
0x00000000D83DDCE5L, 0x00000000D83DDCE4L, 0x0000000000002709L, 0x00000000D83DDCE9L, 0x00000000D83DDCE8L, 0x00000000D83DDCEFL, 0x00000000D83DDCEBL,
|
||||
0x00000000D83DDCEAL, 0x00000000D83DDCECL, 0x00000000D83DDCEDL, 0x00000000D83DDCEEL, 0x00000000D83DDCE6L, 0x00000000D83DDCDDL, 0x00000000D83DDCC4L,
|
||||
0x00000000D83DDCC3L, 0x00000000D83DDCD1L, 0x00000000D83DDCCAL, 0x00000000D83DDCC8L, 0x00000000D83DDCC9L, 0x00000000D83DDCDCL, 0x00000000D83DDCCBL,
|
||||
0x00000000D83DDCC5L, 0x00000000D83DDCC6L, 0x00000000D83DDCC7L, 0x00000000D83DDCC1L, 0x00000000D83DDCC2L, 0x0000000000002702L, 0x00000000D83DDCCCL,
|
||||
0x00000000D83DDCCEL, 0x0000000000002712L, 0x000000000000270FL, 0x00000000D83DDCCFL, 0x00000000D83DDCD0L, 0x00000000D83DDCD5L, 0x00000000D83DDCD7L,
|
||||
0x00000000D83DDCD8L, 0x00000000D83DDCD9L, 0x00000000D83DDCD3L, 0x00000000D83DDCD4L, 0x00000000D83DDCD2L, 0x00000000D83DDCDAL, 0x00000000D83DDCD6L,
|
||||
0x00000000D83DDD16L, 0x00000000D83DDCDBL, 0x00000000D83DDD2CL, 0x00000000D83DDD2DL, 0x00000000D83DDCF0L, 0x00000000D83CDFA8L, 0x00000000D83CDFACL,
|
||||
0x00000000D83CDFA4L, 0x00000000D83CDFA7L, 0x00000000D83CDFBCL, 0x00000000D83CDFB5L, 0x00000000D83CDFB6L, 0x00000000D83CDFB9L, 0x00000000D83CDFBBL,
|
||||
0x00000000D83CDFBAL, 0x00000000D83CDFB7L, 0x00000000D83CDFB8L, 0x00000000D83DDC7EL, 0x00000000D83CDFAEL, 0x00000000D83CDCCFL, 0x00000000D83CDFB4L,
|
||||
0x00000000D83CDC04L, 0x00000000D83CDFB2L, 0x00000000D83CDFAFL, 0x00000000D83CDFC8L, 0x00000000D83CDFC0L, 0x00000000000026BDL, 0x00000000000026BEL,
|
||||
0x00000000D83CDFBEL, 0x00000000D83CDFB1L, 0x00000000D83CDFC9L, 0x00000000D83CDFB3L, 0x00000000000026F3L, 0x00000000D83DDEB5L, 0x00000000D83DDEB4L,
|
||||
0x00000000D83CDFC1L, 0x00000000D83CDFC7L, 0x00000000D83CDFC6L, 0x00000000D83CDFBFL, 0x00000000D83CDFC2L, 0x00000000D83CDFCAL, 0x00000000D83CDFC4L,
|
||||
0x00000000D83CDFA3L, 0x0000000000002615L, 0x00000000D83CDF75L, 0x00000000D83CDF76L, 0x00000000D83CDF7CL, 0x00000000D83CDF7AL, 0x00000000D83CDF7BL,
|
||||
0x00000000D83CDF78L, 0x00000000D83CDF79L, 0x00000000D83CDF77L, 0x00000000D83CDF74L, 0x00000000D83CDF55L, 0x00000000D83CDF54L, 0x00000000D83CDF5FL,
|
||||
0x00000000D83CDF57L, 0x00000000D83CDF56L, 0x00000000D83CDF5DL, 0x00000000D83CDF5BL, 0x00000000D83CDF64L, 0x00000000D83CDF71L, 0x00000000D83CDF63L,
|
||||
0x00000000D83CDF65L, 0x00000000D83CDF59L, 0x00000000D83CDF58L, 0x00000000D83CDF5AL, 0x00000000D83CDF5CL, 0x00000000D83CDF72L, 0x00000000D83CDF62L,
|
||||
0x00000000D83CDF61L, 0x00000000D83CDF73L, 0x00000000D83CDF5EL, 0x00000000D83CDF69L, 0x00000000D83CDF6EL, 0x00000000D83CDF66L, 0x00000000D83CDF68L,
|
||||
0x00000000D83CDF67L, 0x00000000D83CDF82L, 0x00000000D83CDF70L, 0x00000000D83CDF6AL, 0x00000000D83CDF6BL, 0x00000000D83CDF6CL, 0x00000000D83CDF6DL,
|
||||
0x00000000D83CDF6FL, 0x00000000D83CDF4EL, 0x00000000D83CDF4FL, 0x00000000D83CDF4AL, 0x00000000D83CDF4BL, 0x00000000D83CDF52L, 0x00000000D83CDF47L,
|
||||
0x00000000D83CDF49L, 0x00000000D83CDF53L, 0x00000000D83CDF51L, 0x00000000D83CDF48L, 0x00000000D83CDF4CL, 0x00000000D83CDF50L, 0x00000000D83CDF4DL,
|
||||
0x00000000D83CDF60L, 0x00000000D83CDF46L, 0x00000000D83CDF45L, 0x00000000D83CDF3DL},
|
||||
{
|
||||
0x00000000D83CDF8DL, 0x00000000D83DDC9DL, 0x00000000D83CDF8EL, 0x00000000D83CDF92L, 0x00000000D83CDF93L, 0x00000000D83CDF8FL, 0x00000000D83CDF86L, 0x00000000D83CDF87L,
|
||||
0x00000000D83CDF90L, 0x00000000D83CDF91L, 0x00000000D83CDF83L, 0x00000000D83DDC7BL, 0x00000000D83CDF85L, 0x00000000D83CDF84L, 0x00000000D83CDF81L,
|
||||
0x00000000D83CDF8BL, 0x00000000D83CDF89L, 0x00000000D83CDF8AL, 0x00000000D83CDF88L, 0x00000000D83CDF8CL, 0x00000000D83DDD2EL, 0x00000000D83CDFA5L,
|
||||
0x00000000D83DDCF7L, 0x00000000D83DDCF9L, 0x00000000D83DDCFCL, 0x00000000D83DDCBFL, 0x00000000D83DDCC0L, 0x00000000D83DDCBDL, 0x00000000D83DDCBEL,
|
||||
0x00000000D83DDCBBL, 0x00000000D83DDCF1L, 0x000000000000260EL, 0x00000000D83DDCDEL, 0x00000000D83DDCDFL, 0x00000000D83DDCE0L, 0x00000000D83DDCE1L,
|
||||
0x00000000D83DDCFAL, 0x00000000D83DDCFBL, 0x00000000D83DDD0AL, 0x00000000D83DDD09L, 0x00000000D83DDD08L, 0x00000000D83DDD07L, 0x00000000D83DDD14L,
|
||||
0x00000000D83DDD15L, 0x00000000D83DDCE2L, 0x00000000D83DDCE3L, 0x00000000000023F3L, 0x000000000000231BL, 0x00000000000023F0L, 0x000000000000231AL,
|
||||
0x00000000D83DDD13L, 0x00000000D83DDD12L, 0x00000000D83DDD0FL, 0x00000000D83DDD10L, 0x00000000D83DDD11L, 0x00000000D83DDD0EL, 0x00000000D83DDCA1L,
|
||||
0x00000000D83DDD26L, 0x00000000D83DDD06L, 0x00000000D83DDD05L, 0x00000000D83DDD0CL, 0x00000000D83DDD0BL, 0x00000000D83DDD0DL, 0x00000000D83DDEC1L, 0x00000000D83DDEC0L,
|
||||
0x00000000D83DDEBFL, 0x00000000D83DDEBDL, 0x00000000D83DDD27L, 0x00000000D83DDD29L, 0x00000000D83DDD28L, 0x00000000D83DDEAAL, 0x00000000D83DDEACL,
|
||||
0x00000000D83DDCA3L, 0x00000000D83DDD2BL, 0x00000000D83DDD2AL, 0x00000000D83DDC8AL, 0x00000000D83DDC89L, 0x00000000D83DDCB0L, 0x00000000D83DDCB4L,
|
||||
0x00000000D83DDCB5L, 0x00000000D83DDCB7L, 0x00000000D83DDCB6L, 0x00000000D83DDCB3L, 0x00000000D83DDCB8L, 0x00000000D83DDCF2L, 0x00000000D83DDCE7L,
|
||||
0x00000000D83DDCE5L, 0x00000000D83DDCE4L, 0x0000000000002709L, 0x00000000D83DDCE9L, 0x00000000D83DDCE8L, 0x00000000D83DDCEFL, 0x00000000D83DDCEBL,
|
||||
0x00000000D83DDCEAL, 0x00000000D83DDCECL, 0x00000000D83DDCEDL, 0x00000000D83DDCEEL, 0x00000000D83DDCE6L, 0x00000000D83DDCDDL, 0x00000000D83DDCC4L,
|
||||
0x00000000D83DDCC3L, 0x00000000D83DDCD1L, 0x00000000D83DDCCAL, 0x00000000D83DDCC8L, 0x00000000D83DDCC9L, 0x00000000D83DDCDCL, 0x00000000D83DDCCBL,
|
||||
0x00000000D83DDCC5L, 0x00000000D83DDCC6L, 0x00000000D83DDCC7L, 0x00000000D83DDCC1L, 0x00000000D83DDCC2L, 0x0000000000002702L, 0x00000000D83DDCCCL,
|
||||
0x00000000D83DDCCEL, 0x0000000000002712L, 0x000000000000270FL, 0x00000000D83DDCCFL, 0x00000000D83DDCD0L, 0x00000000D83DDCD5L, 0x00000000D83DDCD7L,
|
||||
0x00000000D83DDCD8L, 0x00000000D83DDCD9L, 0x00000000D83DDCD3L, 0x00000000D83DDCD4L, 0x00000000D83DDCD2L, 0x00000000D83DDCDAL, 0x00000000D83DDCD6L,
|
||||
0x00000000D83DDD16L, 0x00000000D83DDCDBL, 0x00000000D83DDD2CL, 0x00000000D83DDD2DL, 0x00000000D83DDCF0L, 0x00000000D83CDFA8L, 0x00000000D83CDFACL,
|
||||
0x00000000D83CDFA4L, 0x00000000D83CDFA7L, 0x00000000D83CDFBCL, 0x00000000D83CDFB5L, 0x00000000D83CDFB6L, 0x00000000D83CDFB9L, 0x00000000D83CDFBBL,
|
||||
0x00000000D83CDFBAL, 0x00000000D83CDFB7L, 0x00000000D83CDFB8L, 0x00000000D83DDC7EL, 0x00000000D83CDFAEL, 0x00000000D83CDCCFL, 0x00000000D83CDFB4L,
|
||||
0x00000000D83CDC04L, 0x00000000D83CDFB2L, 0x00000000D83CDFAFL, 0x00000000D83CDFC8L, 0x00000000D83CDFC0L, 0x00000000000026BDL, 0x00000000000026BEL,
|
||||
0x00000000D83CDFBEL, 0x00000000D83CDFB1L, 0x00000000D83CDFC9L, 0x00000000D83CDFB3L, 0x00000000000026F3L, 0x00000000D83DDEB5L, 0x00000000D83DDEB4L,
|
||||
0x00000000D83CDFC1L, 0x00000000D83CDFC7L, 0x00000000D83CDFC6L, 0x00000000D83CDFBFL, 0x00000000D83CDFC2L, 0x00000000D83CDFCAL, 0x00000000D83CDFC4L,
|
||||
0x00000000D83CDFA3L, 0x0000000000002615L, 0x00000000D83CDF75L, 0x00000000D83CDF76L, 0x00000000D83CDF7CL, 0x00000000D83CDF7AL, 0x00000000D83CDF7BL,
|
||||
0x00000000D83CDF78L, 0x00000000D83CDF79L, 0x00000000D83CDF77L, 0x00000000D83CDF74L, 0x00000000D83CDF55L, 0x00000000D83CDF54L, 0x00000000D83CDF5FL,
|
||||
0x00000000D83CDF57L, 0x00000000D83CDF56L, 0x00000000D83CDF5DL, 0x00000000D83CDF5BL, 0x00000000D83CDF64L, 0x00000000D83CDF71L, 0x00000000D83CDF63L,
|
||||
0x00000000D83CDF65L, 0x00000000D83CDF59L, 0x00000000D83CDF58L, 0x00000000D83CDF5AL, 0x00000000D83CDF5CL, 0x00000000D83CDF72L, 0x00000000D83CDF62L,
|
||||
0x00000000D83CDF61L, 0x00000000D83CDF73L, 0x00000000D83CDF5EL, 0x00000000D83CDF69L, 0x00000000D83CDF6EL, 0x00000000D83CDF66L, 0x00000000D83CDF68L,
|
||||
0x00000000D83CDF67L, 0x00000000D83CDF82L, 0x00000000D83CDF70L, 0x00000000D83CDF6AL, 0x00000000D83CDF6BL, 0x00000000D83CDF6CL, 0x00000000D83CDF6DL,
|
||||
0x00000000D83CDF6FL, 0x00000000D83CDF4EL, 0x00000000D83CDF4FL, 0x00000000D83CDF4AL, 0x00000000D83CDF4BL, 0x00000000D83CDF52L, 0x00000000D83CDF47L,
|
||||
0x00000000D83CDF49L, 0x00000000D83CDF53L, 0x00000000D83CDF51L, 0x00000000D83CDF48L, 0x00000000D83CDF4CL, 0x00000000D83CDF50L, 0x00000000D83CDF4DL,
|
||||
0x00000000D83CDF60L, 0x00000000D83CDF46L, 0x00000000D83CDF45L, 0x00000000D83CDF3DL},
|
||||
new long[]//101
|
||||
{0x00000000D83CDFE0L, 0x00000000D83CDFE1L, 0x00000000D83CDFEBL, 0x00000000D83CDFE2L, 0x00000000D83CDFE3L, 0x00000000D83CDFE5L, 0x00000000D83CDFE6L, 0x00000000D83CDFEAL,
|
||||
0x00000000D83CDFE9L, 0x00000000D83CDFE8L, 0x00000000D83DDC92L, 0x00000000000026EAL, 0x00000000D83CDFECL, 0x00000000D83CDFE4L, 0x00000000D83CDF07L,
|
||||
0x00000000D83CDF06L, 0x00000000D83CDFEFL, 0x00000000D83CDFF0L, 0x00000000000026FAL, 0x00000000D83CDFEDL, 0x00000000D83DDDFCL, 0x00000000D83DDDFEL,
|
||||
0x00000000D83DDDFBL, 0x00000000D83CDF04L, 0x00000000D83CDF05L, 0x00000000D83CDF03L, 0x00000000D83DDDFDL, 0x00000000D83CDF09L, 0x00000000D83CDFA0L,
|
||||
0x00000000D83CDFA1L, 0x00000000000026F2L, 0x00000000D83CDFA2L, 0x00000000D83DDEA2L, 0x00000000000026F5L, 0x00000000D83DDEA4L, 0x00000000D83DDEA3L,
|
||||
0x0000000000002693L, 0x00000000D83DDE80L, 0x0000000000002708L, 0x00000000D83DDCBAL, 0x00000000D83DDE81L, 0x00000000D83DDE82L, 0x00000000D83DDE8AL,
|
||||
0x00000000D83DDE89L, 0x00000000D83DDE9EL, 0x00000000D83DDE86L, 0x00000000D83DDE84L, 0x00000000D83DDE85L, 0x00000000D83DDE88L, 0x00000000D83DDE87L,
|
||||
0x00000000D83DDE9DL, 0x00000000D83DDE8BL, 0x00000000D83DDE83L, 0x00000000D83DDE8EL, 0x00000000D83DDE8CL, 0x00000000D83DDE8DL, 0x00000000D83DDE99L,
|
||||
0x00000000D83DDE98L, 0x00000000D83DDE97L, 0x00000000D83DDE95L, 0x00000000D83DDE96L, 0x00000000D83DDE9BL, 0x00000000D83DDE9AL, 0x00000000D83DDEA8L,
|
||||
0x00000000D83DDE93L, 0x00000000D83DDE94L, 0x00000000D83DDE92L, 0x00000000D83DDE91L, 0x00000000D83DDE90L, 0x00000000D83DDEB2L, 0x00000000D83DDEA1L,
|
||||
0x00000000D83DDE9FL, 0x00000000D83DDEA0L, 0x00000000D83DDE9CL, 0x00000000D83DDC88L, 0x00000000D83DDE8FL, 0x00000000D83CDFABL, 0x00000000D83DDEA6L,
|
||||
0x00000000D83DDEA5L, 0x00000000000026A0L, 0x00000000D83DDEA7L, 0x00000000D83DDD30L, 0x00000000000026FDL, 0x00000000D83CDFEEL, 0x00000000D83CDFB0L,
|
||||
0x0000000000002668L, 0x00000000D83DDDFFL, 0x00000000D83CDFAAL, 0x00000000D83CDFADL, 0x00000000D83DDCCDL, 0x00000000D83DDEA9L, 0xD83CDDEFD83CDDF5L,
|
||||
0xD83CDDF0D83CDDF7L, 0xD83CDDE9D83CDDEAL, 0xD83CDDE8D83CDDF3L, 0xD83CDDFAD83CDDF8L, 0xD83CDDEBD83CDDF7L, 0xD83CDDEAD83CDDF8L, 0xD83CDDEED83CDDF9L,
|
||||
0xD83CDDF7D83CDDFAL, 0xD83CDDECD83CDDE7L},
|
||||
{
|
||||
0x00000000D83CDFE0L, 0x00000000D83CDFE1L, 0x00000000D83CDFEBL, 0x00000000D83CDFE2L, 0x00000000D83CDFE3L, 0x00000000D83CDFE5L, 0x00000000D83CDFE6L, 0x00000000D83CDFEAL,
|
||||
0x00000000D83CDFE9L, 0x00000000D83CDFE8L, 0x00000000D83DDC92L, 0x00000000000026EAL, 0x00000000D83CDFECL, 0x00000000D83CDFE4L, 0x00000000D83CDF07L,
|
||||
0x00000000D83CDF06L, 0x00000000D83CDFEFL, 0x00000000D83CDFF0L, 0x00000000000026FAL, 0x00000000D83CDFEDL, 0x00000000D83DDDFCL, 0x00000000D83DDDFEL,
|
||||
0x00000000D83DDDFBL, 0x00000000D83CDF04L, 0x00000000D83CDF05L, 0x00000000D83CDF03L, 0x00000000D83DDDFDL, 0x00000000D83CDF09L, 0x00000000D83CDFA0L,
|
||||
0x00000000D83CDFA1L, 0x00000000000026F2L, 0x00000000D83CDFA2L, 0x00000000D83DDEA2L, 0x00000000000026F5L, 0x00000000D83DDEA4L, 0x00000000D83DDEA3L,
|
||||
0x0000000000002693L, 0x00000000D83DDE80L, 0x0000000000002708L, 0x00000000D83DDCBAL, 0x00000000D83DDE81L, 0x00000000D83DDE82L, 0x00000000D83DDE8AL,
|
||||
0x00000000D83DDE89L, 0x00000000D83DDE9EL, 0x00000000D83DDE86L, 0x00000000D83DDE84L, 0x00000000D83DDE85L, 0x00000000D83DDE88L, 0x00000000D83DDE87L,
|
||||
0x00000000D83DDE9DL, 0x00000000D83DDE8BL, 0x00000000D83DDE83L, 0x00000000D83DDE8EL, 0x00000000D83DDE8CL, 0x00000000D83DDE8DL, 0x00000000D83DDE99L,
|
||||
0x00000000D83DDE98L, 0x00000000D83DDE97L, 0x00000000D83DDE95L, 0x00000000D83DDE96L, 0x00000000D83DDE9BL, 0x00000000D83DDE9AL, 0x00000000D83DDEA8L,
|
||||
0x00000000D83DDE93L, 0x00000000D83DDE94L, 0x00000000D83DDE92L, 0x00000000D83DDE91L, 0x00000000D83DDE90L, 0x00000000D83DDEB2L, 0x00000000D83DDEA1L,
|
||||
0x00000000D83DDE9FL, 0x00000000D83DDEA0L, 0x00000000D83DDE9CL, 0x00000000D83DDC88L, 0x00000000D83DDE8FL, 0x00000000D83CDFABL, 0x00000000D83DDEA6L,
|
||||
0x00000000D83DDEA5L, 0x00000000000026A0L, 0x00000000D83DDEA7L, 0x00000000D83DDD30L, 0x00000000000026FDL, 0x00000000D83CDFEEL, 0x00000000D83CDFB0L,
|
||||
0x0000000000002668L, 0x00000000D83DDDFFL, 0x00000000D83CDFAAL, 0x00000000D83CDFADL, 0x00000000D83DDCCDL, 0x00000000D83DDEA9L, 0xD83CDDEFD83CDDF5L,
|
||||
0xD83CDDF0D83CDDF7L, 0xD83CDDE9D83CDDEAL, 0xD83CDDE8D83CDDF3L, 0xD83CDDFAD83CDDF8L, 0xD83CDDEBD83CDDF7L, 0xD83CDDEAD83CDDF8L, 0xD83CDDEED83CDDF9L,
|
||||
0xD83CDDF7D83CDDFAL, 0xD83CDDECD83CDDE7L},
|
||||
new long[]//209
|
||||
{0x00000000003120E3L, 0x00000000003220E3L, 0x00000000003320E3L, 0x00000000003420E3L, 0x00000000003520E3L, 0x00000000003620E3L, 0x00000000003720E3L,
|
||||
0x00000000003820E3L, 0x00000000003920E3L, 0x00000000003020E3L, 0x00000000D83DDD1FL, 0x00000000D83DDD22L, 0x00000000002320E3L, 0x00000000D83DDD23L,
|
||||
0x0000000000002B06L, 0x0000000000002B07L, 0x0000000000002B05L, 0x00000000000027A1L, 0x00000000D83DDD20L, 0x00000000D83DDD21L, 0x00000000D83DDD24L,
|
||||
0x0000000000002197L, 0x0000000000002196L, 0x0000000000002198L, 0x0000000000002199L, 0x0000000000002194L, 0x0000000000002195L, 0x00000000D83DDD04L,
|
||||
0x00000000000025C0L, 0x00000000000025B6L, 0x00000000D83DDD3CL, 0x00000000D83DDD3DL, 0x00000000000021A9L, 0x00000000000021AAL, 0x0000000000002139L,
|
||||
0x00000000000023EAL, 0x00000000000023E9L, 0x00000000000023EBL, 0x00000000000023ECL, 0x0000000000002935L, 0x0000000000002934L, 0x00000000D83CDD97L,
|
||||
0x00000000D83DDD00L, 0x00000000D83DDD01L, 0x00000000D83DDD02L, 0x00000000D83CDD95L, 0x00000000D83CDD99L, 0x00000000D83CDD92L, 0x00000000D83CDD93L,
|
||||
0x00000000D83CDD96L, 0x00000000D83DDCF6L, 0x00000000D83CDFA6L, 0x00000000D83CDE01L, 0x00000000D83CDE2FL, 0x00000000D83CDE33L, 0x00000000D83CDE35L,
|
||||
0x00000000D83CDE32L, 0x00000000D83CDE34L, 0x00000000D83CDE50L, 0x00000000D83CDE39L, 0x00000000D83CDE3AL, 0x00000000D83CDE36L, 0x00000000D83CDE1AL,
|
||||
0x00000000D83DDEBBL, 0x00000000D83DDEB9L, 0x00000000D83DDEBAL, 0x00000000D83DDEBCL, 0x00000000D83DDEBEL, 0x00000000D83DDEB0L, 0x00000000D83DDEAEL,
|
||||
0x00000000D83CDD7FL, 0x000000000000267FL, 0x00000000D83DDEADL, 0x00000000D83CDE37L, 0x00000000D83CDE38L, 0x00000000D83CDE02L, 0x00000000000024C2L,
|
||||
0x00000000D83DDEC2L, 0x00000000D83DDEC4L, 0x00000000D83DDEC5L, 0x00000000D83DDEC3L, 0x00000000D83CDE51L, 0x0000000000003299L, 0x0000000000003297L,
|
||||
0x00000000D83CDD91L, 0x00000000D83CDD98L, 0x00000000D83CDD94L, 0x00000000D83DDEABL,
|
||||
0x00000000D83DDD1EL, 0x00000000D83DDCF5L, 0x00000000D83DDEAFL, 0x00000000D83DDEB1L, 0x00000000D83DDEB3L, 0x00000000D83DDEB7L, 0x00000000D83DDEB8L,
|
||||
0x00000000000026D4L, 0x0000000000002733L, 0x0000000000002747L, 0x000000000000274EL, 0x0000000000002705L, 0x0000000000002734L, 0x00000000D83DDC9FL,
|
||||
0x00000000D83CDD9AL, 0x00000000D83DDCF3L, 0x00000000D83DDCF4L, 0x00000000D83CDD70L, 0x00000000D83CDD71L, 0x00000000D83CDD8EL, 0x00000000D83CDD7EL,
|
||||
0x00000000D83DDCA0L, 0x00000000000027BFL, 0x000000000000267BL, 0x0000000000002648L, 0x0000000000002649L, 0x000000000000264AL, 0x000000000000264BL,
|
||||
0x000000000000264CL, 0x000000000000264DL, 0x000000000000264EL, 0x000000000000264FL, 0x0000000000002650L, 0x0000000000002651L, 0x0000000000002652L,
|
||||
0x0000000000002653L, 0x00000000000026CEL, 0x00000000D83DDD2FL, 0x00000000D83CDFE7L, 0x00000000D83DDCB9L, 0x00000000D83DDCB2L, 0x00000000D83DDCB1L,
|
||||
0x00000000000000A9L, 0x00000000000000AEL, 0x0000000000002122L, 0x000000000000303DL, 0x0000000000003030L, 0x00000000D83DDD1DL, 0x00000000D83DDD1AL,
|
||||
0x00000000D83DDD19L, 0x00000000D83DDD1BL, 0x00000000D83DDD1CL, 0x000000000000274CL, 0x0000000000002B55L, 0x0000000000002757L, 0x000000000000203CL,
|
||||
0x0000000000002049L, 0x0000000000002753L,
|
||||
0x0000000000002755L, 0x0000000000002754L, 0x00000000D83DDD03L, 0x00000000D83DDD5BL, 0x00000000D83DDD67L, 0x00000000D83DDD50L, 0x00000000D83DDD5CL,
|
||||
0x00000000D83DDD51L, 0x00000000D83DDD5DL, 0x00000000D83DDD52L, 0x00000000D83DDD5EL, 0x00000000D83DDD53L, 0x00000000D83DDD5FL, 0x00000000D83DDD54L,
|
||||
0x00000000D83DDD60L, 0x00000000D83DDD55L, 0x00000000D83DDD56L, 0x00000000D83DDD57L, 0x00000000D83DDD58L, 0x00000000D83DDD59L, 0x00000000D83DDD5AL,
|
||||
0x00000000D83DDD61L, 0x00000000D83DDD62L, 0x00000000D83DDD63L, 0x00000000D83DDD64L, 0x00000000D83DDD65L, 0x00000000D83DDD66L, 0x0000000000002716L,
|
||||
0x0000000000002795L, 0x0000000000002796L, 0x0000000000002797L, 0x0000000000002660L, 0x0000000000002665L, 0x0000000000002663L, 0x0000000000002666L,
|
||||
0x00000000D83DDCAEL, 0x00000000D83DDCAFL, 0x0000000000002714L, 0x0000000000002611L, 0x00000000D83DDD18L, 0x00000000D83DDD17L, 0x00000000000027B0L,
|
||||
0x00000000D83DDD31L, 0x00000000D83DDD32L, 0x00000000D83DDD33L, 0x00000000000025FCL, 0x00000000000025FBL, 0x00000000000025FEL, 0x00000000000025FDL,
|
||||
0x00000000000025AAL, 0x00000000000025ABL, 0x00000000D83DDD3AL, 0x0000000000002B1CL, 0x0000000000002B1BL, 0x00000000000026ABL, 0x00000000000026AAL,
|
||||
0x00000000D83DDD34L, 0x00000000D83DDD35L, 0x00000000D83DDD3BL, 0x00000000D83DDD36L, 0x00000000D83DDD37L, 0x00000000D83DDD38L, 0x00000000D83DDD39L}};
|
||||
|
||||
static {
|
||||
{
|
||||
0x00000000003120E3L, 0x00000000003220E3L, 0x00000000003320E3L, 0x00000000003420E3L, 0x00000000003520E3L, 0x00000000003620E3L, 0x00000000003720E3L,
|
||||
0x00000000003820E3L, 0x00000000003920E3L, 0x00000000003020E3L, 0x00000000D83DDD1FL, 0x00000000D83DDD22L, 0x00000000002320E3L, 0x00000000D83DDD23L,
|
||||
0x0000000000002B06L, 0x0000000000002B07L, 0x0000000000002B05L, 0x00000000000027A1L, 0x00000000D83DDD20L, 0x00000000D83DDD21L, 0x00000000D83DDD24L,
|
||||
0x0000000000002197L, 0x0000000000002196L, 0x0000000000002198L, 0x0000000000002199L, 0x0000000000002194L, 0x0000000000002195L, 0x00000000D83DDD04L,
|
||||
0x00000000000025C0L, 0x00000000000025B6L, 0x00000000D83DDD3CL, 0x00000000D83DDD3DL, 0x00000000000021A9L, 0x00000000000021AAL, 0x0000000000002139L,
|
||||
0x00000000000023EAL, 0x00000000000023E9L, 0x00000000000023EBL, 0x00000000000023ECL, 0x0000000000002935L, 0x0000000000002934L, 0x00000000D83CDD97L,
|
||||
0x00000000D83DDD00L, 0x00000000D83DDD01L, 0x00000000D83DDD02L, 0x00000000D83CDD95L, 0x00000000D83CDD99L, 0x00000000D83CDD92L, 0x00000000D83CDD93L,
|
||||
0x00000000D83CDD96L, 0x00000000D83DDCF6L, 0x00000000D83CDFA6L, 0x00000000D83CDE01L, 0x00000000D83CDE2FL, 0x00000000D83CDE33L, 0x00000000D83CDE35L,
|
||||
0x00000000D83CDE32L, 0x00000000D83CDE34L, 0x00000000D83CDE50L, 0x00000000D83CDE39L, 0x00000000D83CDE3AL, 0x00000000D83CDE36L, 0x00000000D83CDE1AL,
|
||||
0x00000000D83DDEBBL, 0x00000000D83DDEB9L, 0x00000000D83DDEBAL, 0x00000000D83DDEBCL, 0x00000000D83DDEBEL, 0x00000000D83DDEB0L, 0x00000000D83DDEAEL,
|
||||
0x00000000D83CDD7FL, 0x000000000000267FL, 0x00000000D83DDEADL, 0x00000000D83CDE37L, 0x00000000D83CDE38L, 0x00000000D83CDE02L, 0x00000000000024C2L,
|
||||
0x00000000D83DDEC2L, 0x00000000D83DDEC4L, 0x00000000D83DDEC5L, 0x00000000D83DDEC3L, 0x00000000D83CDE51L, 0x0000000000003299L, 0x0000000000003297L,
|
||||
0x00000000D83CDD91L, 0x00000000D83CDD98L, 0x00000000D83CDD94L, 0x00000000D83DDEABL,
|
||||
0x00000000D83DDD1EL, 0x00000000D83DDCF5L, 0x00000000D83DDEAFL, 0x00000000D83DDEB1L, 0x00000000D83DDEB3L, 0x00000000D83DDEB7L, 0x00000000D83DDEB8L,
|
||||
0x00000000000026D4L, 0x0000000000002733L, 0x0000000000002747L, 0x000000000000274EL, 0x0000000000002705L, 0x0000000000002734L, 0x00000000D83DDC9FL,
|
||||
0x00000000D83CDD9AL, 0x00000000D83DDCF3L, 0x00000000D83DDCF4L, 0x00000000D83CDD70L, 0x00000000D83CDD71L, 0x00000000D83CDD8EL, 0x00000000D83CDD7EL,
|
||||
0x00000000D83DDCA0L, 0x00000000000027BFL, 0x000000000000267BL, 0x0000000000002648L, 0x0000000000002649L, 0x000000000000264AL, 0x000000000000264BL,
|
||||
0x000000000000264CL, 0x000000000000264DL, 0x000000000000264EL, 0x000000000000264FL, 0x0000000000002650L, 0x0000000000002651L, 0x0000000000002652L,
|
||||
0x0000000000002653L, 0x00000000000026CEL, 0x00000000D83DDD2FL, 0x00000000D83CDFE7L, 0x00000000D83DDCB9L, 0x00000000D83DDCB2L, 0x00000000D83DDCB1L,
|
||||
0x00000000000000A9L, 0x00000000000000AEL, 0x0000000000002122L, 0x000000000000303DL, 0x0000000000003030L, 0x00000000D83DDD1DL, 0x00000000D83DDD1AL,
|
||||
0x00000000D83DDD19L, 0x00000000D83DDD1BL, 0x00000000D83DDD1CL, 0x000000000000274CL, 0x0000000000002B55L, 0x0000000000002757L, 0x000000000000203CL,
|
||||
0x0000000000002049L, 0x0000000000002753L,
|
||||
0x0000000000002755L, 0x0000000000002754L, 0x00000000D83DDD03L, 0x00000000D83DDD5BL, 0x00000000D83DDD67L, 0x00000000D83DDD50L, 0x00000000D83DDD5CL,
|
||||
0x00000000D83DDD51L, 0x00000000D83DDD5DL, 0x00000000D83DDD52L, 0x00000000D83DDD5EL, 0x00000000D83DDD53L, 0x00000000D83DDD5FL, 0x00000000D83DDD54L,
|
||||
0x00000000D83DDD60L, 0x00000000D83DDD55L, 0x00000000D83DDD56L, 0x00000000D83DDD57L, 0x00000000D83DDD58L, 0x00000000D83DDD59L, 0x00000000D83DDD5AL,
|
||||
0x00000000D83DDD61L, 0x00000000D83DDD62L, 0x00000000D83DDD63L, 0x00000000D83DDD64L, 0x00000000D83DDD65L, 0x00000000D83DDD66L, 0x0000000000002716L,
|
||||
0x0000000000002795L, 0x0000000000002796L, 0x0000000000002797L, 0x0000000000002660L, 0x0000000000002665L, 0x0000000000002663L, 0x0000000000002666L,
|
||||
0x00000000D83DDCAEL, 0x00000000D83DDCAFL, 0x0000000000002714L, 0x0000000000002611L, 0x00000000D83DDD18L, 0x00000000D83DDD17L, 0x00000000000027B0L,
|
||||
0x00000000D83DDD31L, 0x00000000D83DDD32L, 0x00000000D83DDD33L, 0x00000000000025FCL, 0x00000000000025FBL, 0x00000000000025FEL, 0x00000000000025FDL,
|
||||
0x00000000000025AAL, 0x00000000000025ABL, 0x00000000D83DDD3AL, 0x0000000000002B1CL, 0x0000000000002B1BL, 0x00000000000026ABL, 0x00000000000026AAL,
|
||||
0x00000000D83DDD34L, 0x00000000D83DDD35L, 0x00000000D83DDD3BL, 0x00000000D83DDD36L, 0x00000000D83DDD37L, 0x00000000D83DDD38L, 0x00000000D83DDD39L}};
|
||||
|
||||
static {
|
||||
int emojiFullSize;
|
||||
if (AndroidUtilities.density <= 1.0f) {
|
||||
emojiFullSize = 32;
|
||||
|
@ -203,25 +208,25 @@ public class Emoji {
|
|||
} else {
|
||||
emojiFullSize = 96;
|
||||
}
|
||||
drawImgSize = AndroidUtilities.dp(20);
|
||||
drawImgSize = AndroidUtilities.dp(20);
|
||||
if (AndroidUtilities.isTablet()) {
|
||||
bigImgSize = AndroidUtilities.dp(40);
|
||||
} else {
|
||||
bigImgSize = AndroidUtilities.dp(32);
|
||||
}
|
||||
|
||||
for (int j = 1; j < data.length; j++) {
|
||||
for (int i = 0; i < data[j].length; i++) {
|
||||
for (int j = 1; j < data.length; j++) {
|
||||
for (int i = 0; i < data[j].length; i++) {
|
||||
Rect rect = new Rect((i % cols[j - 1]) * emojiFullSize, (i / cols[j - 1]) * emojiFullSize, (i % cols[j - 1] + 1) * emojiFullSize, (i / cols[j - 1] + 1) * emojiFullSize);
|
||||
rects.put(data[j][i], new DrawableInfo(rect, (byte)(j - 1)));
|
||||
}
|
||||
}
|
||||
placeholderPaint = new Paint();
|
||||
placeholderPaint.setColor(0x00000000);
|
||||
}
|
||||
rects.put(data[j][i], new DrawableInfo(rect, (byte) (j - 1)));
|
||||
}
|
||||
}
|
||||
placeholderPaint = new Paint();
|
||||
placeholderPaint.setColor(0x00000000);
|
||||
}
|
||||
|
||||
private static void loadEmoji(final int page) {
|
||||
try {
|
||||
private static void loadEmoji(final int page) {
|
||||
try {
|
||||
float scale;
|
||||
int imageResize = 1;
|
||||
if (AndroidUtilities.density <= 1.0f) {
|
||||
|
@ -290,64 +295,64 @@ public class Emoji {
|
|||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.emojiDidLoaded);
|
||||
}
|
||||
});
|
||||
} catch(Throwable x) {
|
||||
} catch (Throwable x) {
|
||||
FileLog.e("tmessages", "Error loading emoji", x);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadEmojiAsync(final int page) {
|
||||
if (loadingEmoji[page]) {
|
||||
}
|
||||
|
||||
private static void loadEmojiAsync(final int page) {
|
||||
if (loadingEmoji[page]) {
|
||||
return;
|
||||
}
|
||||
loadingEmoji[page] = true;
|
||||
new Thread(new Runnable() {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
loadEmoji(page);
|
||||
loadingEmoji[page] = false;
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void invalidateAll(View view) {
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup g = (ViewGroup)view;
|
||||
for (int i = 0; i < g.getChildCount(); i++) {
|
||||
invalidateAll(g.getChildAt(i));
|
||||
}
|
||||
} else if (view instanceof TextView) {
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public static EmojiDrawable getEmojiDrawable(long code) {
|
||||
DrawableInfo info = rects.get(code);
|
||||
if (info == null) {
|
||||
}
|
||||
|
||||
public static void invalidateAll(View view) {
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup g = (ViewGroup) view;
|
||||
for (int i = 0; i < g.getChildCount(); i++) {
|
||||
invalidateAll(g.getChildAt(i));
|
||||
}
|
||||
} else if (view instanceof TextView) {
|
||||
view.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public static EmojiDrawable getEmojiDrawable(long code) {
|
||||
DrawableInfo info = rects.get(code);
|
||||
if (info == null) {
|
||||
FileLog.e("tmessages", "No emoji drawable for code " + String.format("%016X", code));
|
||||
return null;
|
||||
}
|
||||
EmojiDrawable ed = new EmojiDrawable(info);
|
||||
ed.setBounds(0, 0, drawImgSize, drawImgSize);
|
||||
return ed;
|
||||
}
|
||||
|
||||
public static Drawable getEmojiBigDrawable(long code) {
|
||||
EmojiDrawable ed = getEmojiDrawable(code);
|
||||
if (ed == null) {
|
||||
return null;
|
||||
}
|
||||
ed.setBounds(0, 0, bigImgSize, bigImgSize);
|
||||
ed.fullSize = true;
|
||||
return ed;
|
||||
}
|
||||
|
||||
public static class EmojiDrawable extends Drawable {
|
||||
EmojiDrawable ed = new EmojiDrawable(info);
|
||||
ed.setBounds(0, 0, drawImgSize, drawImgSize);
|
||||
return ed;
|
||||
}
|
||||
|
||||
public static Drawable getEmojiBigDrawable(long code) {
|
||||
EmojiDrawable ed = getEmojiDrawable(code);
|
||||
if (ed == null) {
|
||||
return null;
|
||||
}
|
||||
ed.setBounds(0, 0, bigImgSize, bigImgSize);
|
||||
ed.fullSize = true;
|
||||
return ed;
|
||||
}
|
||||
|
||||
public static class EmojiDrawable extends Drawable {
|
||||
private DrawableInfo info;
|
||||
private boolean fullSize = false;
|
||||
private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
public EmojiDrawable(DrawableInfo i) {
|
||||
info = i;
|
||||
}
|
||||
private boolean fullSize = false;
|
||||
private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
public EmojiDrawable(DrawableInfo i) {
|
||||
info = i;
|
||||
}
|
||||
|
||||
public DrawableInfo getDrawableInfo() {
|
||||
return info;
|
||||
|
@ -364,12 +369,12 @@ public class Emoji {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (emojiBmp[info.page] == null) {
|
||||
public void draw(Canvas canvas) {
|
||||
if (emojiBmp[info.page] == null) {
|
||||
loadEmojiAsync(info.page);
|
||||
canvas.drawRect(getBounds(), placeholderPaint);
|
||||
return;
|
||||
}
|
||||
canvas.drawRect(getBounds(), placeholderPaint);
|
||||
return;
|
||||
}
|
||||
Rect b;
|
||||
if (fullSize) {
|
||||
b = getDrawRect();
|
||||
|
@ -380,33 +385,33 @@ public class Emoji {
|
|||
if (!canvas.quickReject(b.left, b.top, b.right, b.bottom, Canvas.EdgeType.AA)) {
|
||||
canvas.drawBitmap(emojiBmp[info.page], info.rect, b, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static class DrawableInfo {
|
||||
}
|
||||
|
||||
private static class DrawableInfo {
|
||||
public Rect rect;
|
||||
public byte page;
|
||||
|
||||
public DrawableInfo(Rect r, byte p) {
|
||||
rect = r;
|
||||
public DrawableInfo(Rect r, byte p) {
|
||||
rect = r;
|
||||
page = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean inArray(char c, char[] a) {
|
||||
for (char cc : a) {
|
||||
|
@ -425,18 +430,22 @@ public class Emoji {
|
|||
return value == 0xd83cdffb || value == 0xd83cdffc || value == 0xd83cdffd || value == 0xd83cdffe || value == 0xd83cdfff;
|
||||
}
|
||||
|
||||
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size) {
|
||||
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew) {
|
||||
if (cs == null || cs.length() == 0) {
|
||||
return cs;
|
||||
}
|
||||
//SpannableStringLight.isFieldsAvailable();
|
||||
//SpannableStringLight s = new SpannableStringLight(cs.toString());
|
||||
Spannable s;
|
||||
if (cs instanceof Spannable) {
|
||||
s = (Spannable)cs;
|
||||
if (!createNew && cs instanceof Spannable) {
|
||||
s = (Spannable) cs;
|
||||
} else {
|
||||
s = Spannable.Factory.getInstance().newSpannable(cs);
|
||||
s = Spannable.Factory.getInstance().newSpannable(cs.toString());
|
||||
}
|
||||
long buf = 0;
|
||||
int emojiCount = 0;
|
||||
//s.setSpansCount(emojiCount);
|
||||
|
||||
try {
|
||||
for (int i = 0; i < cs.length(); i++) {
|
||||
char c = cs.charAt(i);
|
||||
|
@ -450,12 +459,12 @@ public class Emoji {
|
|||
if (d != null) {
|
||||
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
||||
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
||||
emojiCount++;
|
||||
if (c >= 0xDDE6 && c <= 0xDDFA) {
|
||||
s.setSpan(span, i - 3, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
} else {
|
||||
s.setSpan(span, i - 1, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
emojiCount++;
|
||||
if (nextIsSkinTone) {
|
||||
i += 2;
|
||||
}
|
||||
|
@ -472,8 +481,8 @@ public class Emoji {
|
|||
if (d != null) {
|
||||
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
||||
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
||||
emojiCount++;
|
||||
s.setSpan(span, i - 1, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
emojiCount++;
|
||||
if (nextIsSkinTone) {
|
||||
i += 2;
|
||||
}
|
||||
|
@ -486,8 +495,8 @@ public class Emoji {
|
|||
if (d != null) {
|
||||
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
||||
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
||||
emojiCount++;
|
||||
s.setSpan(span, i, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
emojiCount++;
|
||||
if (nextIsSkinTone) {
|
||||
i += 2;
|
||||
}
|
||||
|
|
|
@ -1285,6 +1285,9 @@ public class ImageLoader {
|
|||
}
|
||||
|
||||
public Float getFileProgress(String location) {
|
||||
if (location == null) {
|
||||
return null;
|
||||
}
|
||||
return fileProgresses.get(location);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ import android.os.Vibrator;
|
|||
import android.provider.MediaStore;
|
||||
import android.view.View;
|
||||
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
import org.telegram.android.query.SharedMediaQuery;
|
||||
import org.telegram.android.video.InputSurface;
|
||||
import org.telegram.android.video.MP4Builder;
|
||||
import org.telegram.android.video.Mp4Movie;
|
||||
|
@ -78,22 +80,34 @@ import java.util.concurrent.Semaphore;
|
|||
public class MediaController implements NotificationCenter.NotificationCenterDelegate, SensorEventListener {
|
||||
|
||||
private native int startRecord(String path);
|
||||
|
||||
private native int writeFrame(ByteBuffer frame, int len);
|
||||
|
||||
private native void stopRecord();
|
||||
|
||||
private native int openOpusFile(String path);
|
||||
|
||||
private native int seekOpusFile(float position);
|
||||
|
||||
private native int isOpusFile(String path);
|
||||
|
||||
private native void closeOpusFile();
|
||||
|
||||
private native void readOpusFile(ByteBuffer buffer, int capacity, int[] args);
|
||||
|
||||
private native long getTotalPcmDuration();
|
||||
|
||||
public static int[] readArgs = new int[3];
|
||||
|
||||
public interface FileDownloadProgressListener {
|
||||
void onFailedDownload(String fileName);
|
||||
|
||||
void onSuccessDownload(String fileName);
|
||||
|
||||
void onProgressDownload(String fileName, float progress);
|
||||
|
||||
void onProgressUpload(String fileName, float progress, boolean isEncrypted);
|
||||
|
||||
int getObserverTag();
|
||||
}
|
||||
|
||||
|
@ -127,6 +141,16 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
MediaStore.Video.Media.DATE_TAKEN
|
||||
};
|
||||
|
||||
public static class AudioEntry {
|
||||
public long id;
|
||||
public String author;
|
||||
public String title;
|
||||
public String genre;
|
||||
public int duration;
|
||||
public String path;
|
||||
public MessageObject messageObject;
|
||||
}
|
||||
|
||||
public static class AlbumEntry {
|
||||
public int bucketId;
|
||||
public String bucketName;
|
||||
|
@ -221,7 +245,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
private HashMap<String, DownloadObject> downloadQueueKeys = new HashMap<>();
|
||||
|
||||
private boolean saveToGallery = true;
|
||||
private boolean shuffleMusic;
|
||||
private int repeatMode;
|
||||
|
||||
private Runnable refreshGalleryRunnable;
|
||||
public static AlbumEntry allPhotosAlbumEntry;
|
||||
|
||||
private HashMap<String, ArrayList<WeakReference<FileDownloadProgressListener>>> loadingFileObservers = new HashMap<>();
|
||||
|
@ -249,6 +276,11 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
private final Object progressTimerSync = new Object();
|
||||
private boolean useFrontSpeaker;
|
||||
private int buffersWrited;
|
||||
private ArrayList<MessageObject> playlist = new ArrayList<>();
|
||||
private ArrayList<MessageObject> shuffledPlaylist = new ArrayList<>();
|
||||
private int currentPlaylistNum;
|
||||
private boolean downloadingCurrentMessage;
|
||||
private AudioInfo audioInfo;
|
||||
|
||||
private AudioRecord audioRecorder = null;
|
||||
private TLRPC.TL_audio recordingAudio = null;
|
||||
|
@ -271,6 +303,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
private int recordBufferSize;
|
||||
private boolean sendAfterDone;
|
||||
|
||||
private Runnable recordStartRunnable;
|
||||
private DispatchQueue recordQueue;
|
||||
private DispatchQueue fileEncodingQueue;
|
||||
private Runnable recordRunnable = new Runnable() {
|
||||
|
@ -359,7 +392,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
}
|
||||
|
||||
/*private class GalleryObserverInternal extends ContentObserver {
|
||||
private class GalleryObserverInternal extends ContentObserver {
|
||||
public GalleryObserverInternal() {
|
||||
super(null);
|
||||
}
|
||||
|
@ -367,9 +400,13 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
super.onChange(selfChange);
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
if (refreshGalleryRunnable != null) {
|
||||
AndroidUtilities.cancelRunOnUIThread(refreshGalleryRunnable);
|
||||
}
|
||||
AndroidUtilities.runOnUIThread(refreshGalleryRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshGalleryRunnable = null;
|
||||
loadGalleryPhotosAlbums(0);
|
||||
}
|
||||
}, 2000);
|
||||
|
@ -384,14 +421,18 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
super.onChange(selfChange);
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
if (refreshGalleryRunnable != null) {
|
||||
AndroidUtilities.cancelRunOnUIThread(refreshGalleryRunnable);
|
||||
}
|
||||
AndroidUtilities.runOnUIThread(refreshGalleryRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshGalleryRunnable = null;
|
||||
loadGalleryPhotosAlbums(0);
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private ExternalObserver externalObserver = null;
|
||||
private InternalObserver internalObserver = null;
|
||||
|
@ -402,6 +443,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
private ArrayList<Long> lastSecretChatVisibleMessages = null;
|
||||
private int startObserverToken = 0;
|
||||
private StopMediaObserverRunnable stopMediaObserverRunnable = null;
|
||||
|
||||
private final class StopMediaObserverRunnable implements Runnable {
|
||||
public int currentObserverToken = 0;
|
||||
|
||||
|
@ -427,9 +469,11 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] mediaProjections = null;
|
||||
|
||||
private static volatile MediaController Instance = null;
|
||||
|
||||
public static MediaController getInstance() {
|
||||
MediaController localInstance = Instance;
|
||||
if (localInstance == null) {
|
||||
|
@ -484,6 +528,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
wifiDownloadMask = preferences.getInt("wifiDownloadMask", AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO);
|
||||
roamingDownloadMask = preferences.getInt("roamingDownloadMask", 0);
|
||||
saveToGallery = preferences.getBoolean("save_gallery", false);
|
||||
shuffleMusic = preferences.getBoolean("shuffleMusic", false);
|
||||
repeatMode = preferences.getInt("repeatMode", 0);
|
||||
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidFailedLoad);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidLoaded);
|
||||
|
@ -491,6 +537,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileUploadProgressChanged);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesDeleted);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.removeAllMessagesFromDialog);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.musicDidLoaded);
|
||||
|
||||
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
|
@ -506,7 +553,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 16) {
|
||||
mediaProjections = new String[] {
|
||||
mediaProjections = new String[]{
|
||||
MediaStore.Images.ImageColumns.DATA,
|
||||
MediaStore.Images.ImageColumns.DISPLAY_NAME,
|
||||
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
|
||||
|
@ -516,7 +563,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
MediaStore.Images.ImageColumns.HEIGHT
|
||||
};
|
||||
} else {
|
||||
mediaProjections = new String[] {
|
||||
mediaProjections = new String[]{
|
||||
MediaStore.Images.ImageColumns.DATA,
|
||||
MediaStore.Images.ImageColumns.DISPLAY_NAME,
|
||||
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
|
||||
|
@ -525,7 +572,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
};
|
||||
}
|
||||
|
||||
/*try {
|
||||
try {
|
||||
ApplicationLoader.applicationContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, new GalleryObserverExternal());
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
|
@ -534,7 +581,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
ApplicationLoader.applicationContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, false, new GalleryObserverInternal());
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
private void startProgressTimer() {
|
||||
|
@ -606,12 +653,13 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
|
||||
public void cleanup() {
|
||||
clenupPlayer(false);
|
||||
clenupPlayer(false, true);
|
||||
if (currentGifDrawable != null) {
|
||||
currentGifDrawable.recycle();
|
||||
currentGifDrawable = null;
|
||||
}
|
||||
currentMediaCell = null;
|
||||
audioInfo = null;
|
||||
currentGifMessageObject = null;
|
||||
photoDownloadQueue.clear();
|
||||
audioDownloadQueue.clear();
|
||||
|
@ -619,6 +667,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
videoDownloadQueue.clear();
|
||||
downloadQueueKeys.clear();
|
||||
videoConvertQueue.clear();
|
||||
playlist.clear();
|
||||
shuffledPlaylist.clear();
|
||||
typingTimes.clear();
|
||||
cancelVideoConvert(null);
|
||||
}
|
||||
|
@ -652,7 +702,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
} else {
|
||||
for (DownloadObject downloadObject : photoDownloadQueue) {
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.PhotoSize)downloadObject.object);
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.PhotoSize) downloadObject.object);
|
||||
}
|
||||
photoDownloadQueue.clear();
|
||||
}
|
||||
|
@ -662,7 +712,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
} else {
|
||||
for (DownloadObject downloadObject : audioDownloadQueue) {
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Audio)downloadObject.object);
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Audio) downloadObject.object);
|
||||
}
|
||||
audioDownloadQueue.clear();
|
||||
}
|
||||
|
@ -672,7 +722,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
} else {
|
||||
for (DownloadObject downloadObject : documentDownloadQueue) {
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Document)downloadObject.object);
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Document) downloadObject.object);
|
||||
}
|
||||
documentDownloadQueue.clear();
|
||||
}
|
||||
|
@ -682,7 +732,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
} else {
|
||||
for (DownloadObject downloadObject : videoDownloadQueue) {
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Video)downloadObject.object);
|
||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Video) downloadObject.object);
|
||||
}
|
||||
videoDownloadQueue.clear();
|
||||
}
|
||||
|
@ -713,7 +763,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
private int getCurrentDownloadMask() {
|
||||
if (ConnectionsManager.isConnectedToWiFi()) {
|
||||
return wifiDownloadMask;
|
||||
} else if(ConnectionsManager.isRoaming()) {
|
||||
} else if (ConnectionsManager.isRoaming()) {
|
||||
return roamingDownloadMask;
|
||||
} else {
|
||||
return mobileDataDownloadMask;
|
||||
|
@ -742,13 +792,13 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
|
||||
boolean added = true;
|
||||
if (downloadObject.object instanceof TLRPC.Audio) {
|
||||
FileLoader.getInstance().loadFile((TLRPC.Audio)downloadObject.object, false);
|
||||
FileLoader.getInstance().loadFile((TLRPC.Audio) downloadObject.object, false);
|
||||
} else if (downloadObject.object instanceof TLRPC.PhotoSize) {
|
||||
FileLoader.getInstance().loadFile((TLRPC.PhotoSize)downloadObject.object, null, false);
|
||||
FileLoader.getInstance().loadFile((TLRPC.PhotoSize) downloadObject.object, null, false);
|
||||
} else if (downloadObject.object instanceof TLRPC.Video) {
|
||||
FileLoader.getInstance().loadFile((TLRPC.Video)downloadObject.object, false);
|
||||
FileLoader.getInstance().loadFile((TLRPC.Video) downloadObject.object, false);
|
||||
} else if (downloadObject.object instanceof TLRPC.Document) {
|
||||
FileLoader.getInstance().loadFile((TLRPC.Document)downloadObject.object, false, false);
|
||||
FileLoader.getInstance().loadFile((TLRPC.Document) downloadObject.object, false, false);
|
||||
} else {
|
||||
added = false;
|
||||
}
|
||||
|
@ -986,7 +1036,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
public void didReceivedNotification(int id, Object... args) {
|
||||
if (id == NotificationCenter.FileDidFailedLoad) {
|
||||
listenerInProgress = true;
|
||||
String fileName = (String)args[0];
|
||||
String fileName = (String) args[0];
|
||||
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||
if (arrayList != null) {
|
||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||
|
@ -999,10 +1049,16 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
listenerInProgress = false;
|
||||
processLaterArrays();
|
||||
checkDownloadFinished(fileName, (Integer)args[1]);
|
||||
checkDownloadFinished(fileName, (Integer) args[1]);
|
||||
} else if (id == NotificationCenter.FileDidLoaded) {
|
||||
listenerInProgress = true;
|
||||
String fileName = (String)args[0];
|
||||
String fileName = (String) args[0];
|
||||
if (downloadingCurrentMessage && playingMessageObject != null) {
|
||||
String file = FileLoader.getAttachFileName(playingMessageObject.messageOwner.media.document);
|
||||
if (file.equals(fileName)) {
|
||||
playAudio(playingMessageObject);
|
||||
}
|
||||
}
|
||||
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||
if (arrayList != null) {
|
||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||
|
@ -1018,10 +1074,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
checkDownloadFinished(fileName, 0);
|
||||
} else if (id == NotificationCenter.FileLoadProgressChanged) {
|
||||
listenerInProgress = true;
|
||||
String fileName = (String)args[0];
|
||||
String fileName = (String) args[0];
|
||||
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||
if (arrayList != null) {
|
||||
Float progress = (Float)args[1];
|
||||
Float progress = (Float) args[1];
|
||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||
if (reference.get() != null) {
|
||||
reference.get().onProgressDownload(fileName, progress);
|
||||
|
@ -1032,11 +1088,11 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
processLaterArrays();
|
||||
} else if (id == NotificationCenter.FileUploadProgressChanged) {
|
||||
listenerInProgress = true;
|
||||
String fileName = (String)args[0];
|
||||
String fileName = (String) args[0];
|
||||
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||
if (arrayList != null) {
|
||||
Float progress = (Float)args[1];
|
||||
Boolean enc = (Boolean)args[2];
|
||||
Float progress = (Float) args[1];
|
||||
Boolean enc = (Boolean) args[2];
|
||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||
if (reference.get() != null) {
|
||||
reference.get().onProgressUpload(fileName, progress, enc);
|
||||
|
@ -1070,15 +1126,27 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
} else if (id == NotificationCenter.messagesDeleted) {
|
||||
if (playingMessageObject != null) {
|
||||
ArrayList<Integer> markAsDeletedMessages = (ArrayList<Integer>)args[0];
|
||||
ArrayList<Integer> markAsDeletedMessages = (ArrayList<Integer>) args[0];
|
||||
if (markAsDeletedMessages.contains(playingMessageObject.getId())) {
|
||||
clenupPlayer(false);
|
||||
clenupPlayer(false, true);
|
||||
}
|
||||
}
|
||||
} else if (id == NotificationCenter.removeAllMessagesFromDialog) {
|
||||
long did = (Long)args[0];
|
||||
long did = (Long) args[0];
|
||||
if (playingMessageObject != null && playingMessageObject.getDialogId() == did) {
|
||||
clenupPlayer(false);
|
||||
clenupPlayer(false, true);
|
||||
}
|
||||
} else if (id == NotificationCenter.musicDidLoaded) {
|
||||
long did = (Long) args[0];
|
||||
if (playingMessageObject != null && playingMessageObject.isMusic() && playingMessageObject.getDialogId() == did) {
|
||||
ArrayList<MessageObject> arrayList = (ArrayList<MessageObject>) args[1];
|
||||
playlist.addAll(0, arrayList);
|
||||
if (shuffleMusic) {
|
||||
buildShuffledPlayList();
|
||||
currentPlaylistNum = 0;
|
||||
} else {
|
||||
currentPlaylistNum += arrayList.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1174,7 +1242,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
audioTrackPlayer.setNotificationMarkerPosition(1);
|
||||
}
|
||||
if (finalBuffersWrited == 1) {
|
||||
clenupPlayer(true);
|
||||
clenupPlayer(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1221,7 +1289,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioRouteChanged, useFrontSpeaker);
|
||||
MessageObject currentMessageObject = playingMessageObject;
|
||||
float progress = playingMessageObject.audioProgress;
|
||||
clenupPlayer(false);
|
||||
clenupPlayer(false, true);
|
||||
currentMessageObject.audioProgress = progress;
|
||||
playAudio(currentMessageObject);
|
||||
ignoreProximity = false;
|
||||
|
@ -1266,9 +1334,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
}
|
||||
|
||||
private void clenupPlayer(boolean notify) {
|
||||
public void clenupPlayer(boolean notify, boolean stopService) {
|
||||
stopProximitySensor();
|
||||
if (audioPlayer != null || audioTrackPlayer != null) {
|
||||
if (playingMessageObject != null) {
|
||||
if (audioPlayer != null) {
|
||||
try {
|
||||
audioPlayer.stop();
|
||||
|
@ -1301,12 +1369,20 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
lastProgress = 0;
|
||||
buffersWrited = 0;
|
||||
isPaused = false;
|
||||
if (downloadingCurrentMessage) {
|
||||
FileLoader.getInstance().cancelLoadFile(playingMessageObject.messageOwner.media.document);
|
||||
}
|
||||
MessageObject lastFile = playingMessageObject;
|
||||
playingMessageObject.audioProgress = 0.0f;
|
||||
playingMessageObject.audioProgressSec = 0;
|
||||
playingMessageObject = null;
|
||||
downloadingCurrentMessage = false;
|
||||
if (notify) {
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioDidReset, lastFile.getId());
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioDidReset, lastFile.getId(), stopService);
|
||||
}
|
||||
if (stopService) {
|
||||
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
|
||||
ApplicationLoader.applicationContext.stopService(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1364,6 +1440,151 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
return true;
|
||||
}
|
||||
|
||||
public MessageObject getPlayingMessageObject() {
|
||||
return playingMessageObject;
|
||||
}
|
||||
|
||||
private void buildShuffledPlayList() {
|
||||
ArrayList<MessageObject> all = new ArrayList<>(playlist);
|
||||
shuffledPlaylist.clear();
|
||||
|
||||
MessageObject messageObject = playlist.get(currentPlaylistNum);
|
||||
all.remove(currentPlaylistNum);
|
||||
shuffledPlaylist.add(messageObject);
|
||||
|
||||
int count = all.size();
|
||||
for (int a = 0; a < count; a++) {
|
||||
int index = Utilities.random.nextInt(all.size());
|
||||
shuffledPlaylist.add(all.get(index));
|
||||
all.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setPlaylist(ArrayList<MessageObject> messageObjects, MessageObject current) {
|
||||
if (playingMessageObject == current) {
|
||||
return playAudio(current);
|
||||
}
|
||||
playlist.clear();
|
||||
for (int a = messageObjects.size() - 1; a >= 0; a--) {
|
||||
MessageObject messageObject = messageObjects.get(a);
|
||||
if (messageObject.isMusic()) {
|
||||
playlist.add(messageObject);
|
||||
}
|
||||
}
|
||||
currentPlaylistNum = playlist.indexOf(current);
|
||||
if (currentPlaylistNum == -1) {
|
||||
playlist.clear();
|
||||
shuffledPlaylist.clear();
|
||||
return false;
|
||||
}
|
||||
if (shuffleMusic) {
|
||||
buildShuffledPlayList();
|
||||
currentPlaylistNum = 0;
|
||||
}
|
||||
SharedMediaQuery.loadMusic(current.getDialogId(), playlist.get(0).getId());
|
||||
return playAudio(current);
|
||||
}
|
||||
|
||||
public void playNextMessage() {
|
||||
playNextMessage(false);
|
||||
}
|
||||
|
||||
private void playNextMessage(boolean byStop) {
|
||||
ArrayList<MessageObject> currentPlayList = shuffleMusic ? shuffledPlaylist : playlist;
|
||||
|
||||
if (byStop && repeatMode == 2) {
|
||||
clenupPlayer(false, false);
|
||||
playAudio(currentPlayList.get(currentPlaylistNum));
|
||||
return;
|
||||
}
|
||||
currentPlaylistNum++;
|
||||
if (currentPlaylistNum >= currentPlayList.size()) {
|
||||
currentPlaylistNum = 0;
|
||||
if (byStop && repeatMode == 0) {
|
||||
stopProximitySensor();
|
||||
if (audioPlayer != null || audioTrackPlayer != null) {
|
||||
if (audioPlayer != null) {
|
||||
try {
|
||||
audioPlayer.stop();
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
try {
|
||||
audioPlayer.release();
|
||||
audioPlayer = null;
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
} else if (audioTrackPlayer != null) {
|
||||
synchronized (playerObjectSync) {
|
||||
try {
|
||||
audioTrackPlayer.pause();
|
||||
audioTrackPlayer.flush();
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
try {
|
||||
audioTrackPlayer.release();
|
||||
audioTrackPlayer = null;
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
stopProgressTimer();
|
||||
lastProgress = 0;
|
||||
buffersWrited = 0;
|
||||
isPaused = true;
|
||||
playingMessageObject.audioProgress = 0.0f;
|
||||
playingMessageObject.audioProgressSec = 0;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (currentPlaylistNum < 0 || currentPlaylistNum >= currentPlayList.size()) {
|
||||
return;
|
||||
}
|
||||
playAudio(currentPlayList.get(currentPlaylistNum));
|
||||
}
|
||||
|
||||
public void playPreviousMessage() {
|
||||
ArrayList<MessageObject> currentPlayList = shuffleMusic ? shuffledPlaylist : playlist;
|
||||
|
||||
currentPlaylistNum--;
|
||||
if (currentPlaylistNum < 0) {
|
||||
currentPlaylistNum = currentPlayList.size() - 1;
|
||||
}
|
||||
if (currentPlaylistNum < 0 || currentPlaylistNum >= currentPlayList.size()) {
|
||||
return;
|
||||
}
|
||||
playAudio(currentPlayList.get(currentPlaylistNum));
|
||||
}
|
||||
|
||||
private void checkIsNextMusicFileDownloaded() {
|
||||
ArrayList<MessageObject> currentPlayList = shuffleMusic ? shuffledPlaylist : playlist;
|
||||
if (currentPlayList == null || currentPlayList.size() < 2) {
|
||||
return;
|
||||
}
|
||||
int nextIndex = currentPlaylistNum + 1;
|
||||
if (nextIndex >= currentPlayList.size()) {
|
||||
nextIndex = 0;
|
||||
}
|
||||
MessageObject nextAudio = currentPlayList.get(nextIndex);
|
||||
File file = null;
|
||||
if (nextAudio.messageOwner.attachPath != null && nextAudio.messageOwner.attachPath.length() > 0) {
|
||||
file = new File(nextAudio.messageOwner.attachPath);
|
||||
if (!file.exists()) {
|
||||
file = null;
|
||||
}
|
||||
}
|
||||
final File cacheFile = file != null ? file : FileLoader.getPathToMessage(nextAudio.messageOwner);
|
||||
boolean exist = cacheFile != null && cacheFile.exists();
|
||||
if (cacheFile != null && cacheFile != file && !cacheFile.exists() && nextAudio.isMusic()) {
|
||||
FileLoader.getInstance().loadFile(nextAudio.messageOwner.media.document, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean playAudio(MessageObject messageObject) {
|
||||
if (messageObject == null) {
|
||||
return false;
|
||||
|
@ -1374,8 +1595,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
return true;
|
||||
}
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioDidStarted, messageObject);
|
||||
clenupPlayer(true);
|
||||
if (audioTrackPlayer != null) {
|
||||
MusicPlayerService.setIgnoreAudioFocus();
|
||||
}
|
||||
clenupPlayer(true, false);
|
||||
File file = null;
|
||||
if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() > 0) {
|
||||
file = new File(messageObject.messageOwner.attachPath);
|
||||
|
@ -1384,8 +1607,33 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
}
|
||||
final File cacheFile = file != null ? file : FileLoader.getPathToMessage(messageObject.messageOwner);
|
||||
if (cacheFile != null && cacheFile != file && !cacheFile.exists() && messageObject.isMusic()) {
|
||||
FileLoader.getInstance().loadFile(messageObject.messageOwner.media.document, true, false);
|
||||
downloadingCurrentMessage = true;
|
||||
isPaused = false;
|
||||
lastProgress = 0;
|
||||
lastPlayPcm = 0;
|
||||
audioInfo = null;
|
||||
playingMessageObject = messageObject;
|
||||
if (playingMessageObject.messageOwner.media.document != null) {
|
||||
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
|
||||
ApplicationLoader.applicationContext.startService(intent);
|
||||
} else {
|
||||
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
|
||||
ApplicationLoader.applicationContext.stopService(intent);
|
||||
}
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
|
||||
return true;
|
||||
} else {
|
||||
downloadingCurrentMessage = false;
|
||||
}
|
||||
if (messageObject.isMusic()) {
|
||||
checkIsNextMusicFileDownloaded();
|
||||
}
|
||||
|
||||
if (isOpusFile(cacheFile.getAbsolutePath()) == 1) {
|
||||
playlist.clear();
|
||||
shuffledPlaylist.clear();
|
||||
synchronized (playerObjectSync) {
|
||||
try {
|
||||
ignoreFirstProgress = 3;
|
||||
|
@ -1410,7 +1658,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
audioTrackPlayer.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener() {
|
||||
@Override
|
||||
public void onMarkerReached(AudioTrack audioTrack) {
|
||||
clenupPlayer(true);
|
||||
clenupPlayer(true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1420,7 +1668,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
});
|
||||
audioTrackPlayer.play();
|
||||
startProgressTimer();
|
||||
startProximitySensor();
|
||||
if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaAudio) {
|
||||
startProximitySensor();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
if (audioTrackPlayer != null) {
|
||||
|
@ -1428,6 +1678,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
audioTrackPlayer = null;
|
||||
isPaused = false;
|
||||
playingMessageObject = null;
|
||||
downloadingCurrentMessage = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1440,13 +1691,28 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
audioPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mediaPlayer) {
|
||||
clenupPlayer(true);
|
||||
if (!playlist.isEmpty() && playlist.size() > 1) {
|
||||
playNextMessage(true);
|
||||
} else {
|
||||
clenupPlayer(true, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
audioPlayer.prepare();
|
||||
audioPlayer.start();
|
||||
startProgressTimer();
|
||||
startProximitySensor();
|
||||
if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaAudio) {
|
||||
audioInfo = null;
|
||||
playlist.clear();
|
||||
shuffledPlaylist.clear();
|
||||
startProximitySensor();
|
||||
} else {
|
||||
try {
|
||||
audioInfo = AudioInfo.getAudioInfo(cacheFile);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
if (audioPlayer != null) {
|
||||
|
@ -1454,6 +1720,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
audioPlayer = null;
|
||||
isPaused = false;
|
||||
playingMessageObject = null;
|
||||
downloadingCurrentMessage = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1463,6 +1730,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
lastProgress = 0;
|
||||
lastPlayPcm = 0;
|
||||
playingMessageObject = messageObject;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioDidStarted, messageObject);
|
||||
|
||||
if (audioPlayer != null) {
|
||||
try {
|
||||
|
@ -1484,7 +1752,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
public void run() {
|
||||
try {
|
||||
if (playingMessageObject != null && playingMessageObject.audioProgress != 0) {
|
||||
lastPlayPcm = (long)(currentTotalPcmDuration * playingMessageObject.audioProgress);
|
||||
lastPlayPcm = (long) (currentTotalPcmDuration * playingMessageObject.audioProgress);
|
||||
seekOpusFile(playingMessageObject.audioProgress);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -1500,6 +1768,14 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
});
|
||||
}
|
||||
|
||||
if (playingMessageObject.messageOwner.media.document != null) {
|
||||
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
|
||||
ApplicationLoader.applicationContext.startService(intent);
|
||||
} else {
|
||||
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
|
||||
ApplicationLoader.applicationContext.stopService(intent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1533,7 +1809,55 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
stopProgressTimer();
|
||||
playingMessageObject = null;
|
||||
downloadingCurrentMessage = false;
|
||||
isPaused = false;
|
||||
|
||||
Intent intent = new Intent(ApplicationLoader.applicationContext, MusicPlayerService.class);
|
||||
ApplicationLoader.applicationContext.stopService(intent);
|
||||
}
|
||||
|
||||
public AudioInfo getAudioInfo() {
|
||||
return audioInfo;
|
||||
}
|
||||
|
||||
public boolean isShuffleMusic() {
|
||||
return shuffleMusic;
|
||||
}
|
||||
|
||||
public int getRepeatMode() {
|
||||
return repeatMode;
|
||||
}
|
||||
|
||||
public void toggleShuffleMusic() {
|
||||
shuffleMusic = !shuffleMusic;
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putBoolean("shuffleMusic", shuffleMusic);
|
||||
editor.commit();
|
||||
if (shuffleMusic) {
|
||||
buildShuffledPlayList();
|
||||
currentPlaylistNum = 0;
|
||||
} else {
|
||||
if (playingMessageObject != null) {
|
||||
currentPlaylistNum = playlist.indexOf(playingMessageObject);
|
||||
if (currentPlaylistNum == -1) {
|
||||
playlist.clear();
|
||||
shuffledPlaylist.clear();
|
||||
clenupPlayer(true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleRepeatMode() {
|
||||
repeatMode++;
|
||||
if (repeatMode > 2) {
|
||||
repeatMode = 0;
|
||||
}
|
||||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putInt("repeatMode", repeatMode);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public boolean pauseAudio(MessageObject messageObject) {
|
||||
|
@ -1549,6 +1873,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
audioTrackPlayer.pause();
|
||||
}
|
||||
isPaused = true;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
isPaused = false;
|
||||
|
@ -1561,7 +1886,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
if (audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.getId() != messageObject.getId()) {
|
||||
return false;
|
||||
}
|
||||
startProximitySensor();
|
||||
if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaAudio) {
|
||||
startProximitySensor();
|
||||
}
|
||||
try {
|
||||
startProgressTimer();
|
||||
if (audioPlayer != null) {
|
||||
|
@ -1571,6 +1898,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
checkPlayerQueue();
|
||||
}
|
||||
isPaused = false;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
return false;
|
||||
|
@ -1579,15 +1907,23 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
|
||||
public boolean isPlayingAudio(MessageObject messageObject) {
|
||||
return !(audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.getId() != messageObject.getId());
|
||||
return !(audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && (playingMessageObject.getId() != messageObject.getId() || downloadingCurrentMessage));
|
||||
}
|
||||
|
||||
public boolean isAudioPaused() {
|
||||
return isPaused;
|
||||
return isPaused || downloadingCurrentMessage;
|
||||
}
|
||||
|
||||
public boolean isDownloadingCurrentMessage() {
|
||||
return downloadingCurrentMessage;
|
||||
}
|
||||
|
||||
public void startRecording(final long dialog_id, final MessageObject reply_to_msg) {
|
||||
clenupPlayer(true);
|
||||
boolean paused = false;
|
||||
if (playingMessageObject != null && isPlayingAudio(playingMessageObject) && !isAudioPaused()) {
|
||||
paused = true;
|
||||
pauseAudio(playingMessageObject);
|
||||
}
|
||||
|
||||
try {
|
||||
Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
|
@ -1596,13 +1932,14 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
FileLog.e("tmessages", e);
|
||||
}
|
||||
|
||||
recordQueue.postRunnable(new Runnable() {
|
||||
recordQueue.postRunnable(recordStartRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (audioRecorder != null) {
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
recordStartRunnable = null;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
||||
}
|
||||
});
|
||||
|
@ -1624,6 +1961,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
recordStartRunnable = null;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
||||
}
|
||||
});
|
||||
|
@ -1653,6 +1991,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
recordStartRunnable = null;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
||||
}
|
||||
});
|
||||
|
@ -1663,11 +2002,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
recordStartRunnable = null;
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStarted);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, paused ? 500 : 0);
|
||||
}
|
||||
|
||||
private void stopRecordingInternal(final boolean send) {
|
||||
|
@ -1709,6 +2049,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
|
||||
public void stopRecording(final boolean send) {
|
||||
if (recordStartRunnable != null) {
|
||||
recordQueue.cancelRunnable(recordStartRunnable);
|
||||
}
|
||||
recordQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -1790,10 +2133,15 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
destFile = AndroidUtilities.generateVideoPath();
|
||||
} else if (type == 2) {
|
||||
File f = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
f.mkdir();
|
||||
destFile = new File(f, name);
|
||||
} else if (type == 3) {
|
||||
File f = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
|
||||
f.mkdirs();
|
||||
destFile = new File(f, name);
|
||||
}
|
||||
|
||||
if(!destFile.exists()) {
|
||||
if (!destFile.exists()) {
|
||||
destFile.createNewFile();
|
||||
}
|
||||
FileChannel source = null;
|
||||
|
@ -1835,7 +2183,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
}
|
||||
}
|
||||
|
||||
if (result && (type == 0 || type == 1)) {
|
||||
if (result && (type == 0 || type == 1 || type == 3)) {
|
||||
AndroidUtilities.addMediaToGallery(Uri.fromFile(destFile));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -1940,7 +2288,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
|||
String str = new String(header);
|
||||
if (str != null) {
|
||||
str = str.toLowerCase();
|
||||
if (str.startsWith("riff") && str.endsWith("webp")){
|
||||
if (str.startsWith("riff") && str.endsWith("webp")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -313,6 +313,8 @@ public class MessageObject {
|
|||
} else {
|
||||
messageText = LocaleController.getString("AttachSticker", R.string.AttachSticker);
|
||||
}
|
||||
} else if (isMusic()) {
|
||||
messageText = LocaleController.getString("AttachMusic", R.string.AttachMusic);
|
||||
} else {
|
||||
String name = FileLoader.getDocumentFileName(message.media.document);
|
||||
if (name != null && name.length() > 0) {
|
||||
|
@ -327,7 +329,9 @@ public class MessageObject {
|
|||
} else {
|
||||
messageText = message.message;
|
||||
}
|
||||
messageText = Emoji.replaceEmoji(messageText, textPaint.getFontMetricsInt(), AndroidUtilities.dp(20));
|
||||
if (generateLayout) {
|
||||
messageText = Emoji.replaceEmoji(messageText, textPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||
}
|
||||
|
||||
if (message instanceof TLRPC.TL_message || message instanceof TLRPC.TL_messageForwarded_old2) {
|
||||
if (isMediaEmpty()) {
|
||||
|
@ -355,6 +359,9 @@ public class MessageObject {
|
|||
type = 8;
|
||||
} else if (message.media.document.mime_type.equals("image/webp") && isSticker()) {
|
||||
type = 13;
|
||||
} else if (isMusic()) {
|
||||
type = 14;
|
||||
contentType = 8;
|
||||
} else {
|
||||
type = 9;
|
||||
}
|
||||
|
@ -600,7 +607,7 @@ public class MessageObject {
|
|||
return;
|
||||
}
|
||||
if (messageOwner.media != null && messageOwner.media.caption != null && messageOwner.media.caption.length() > 0) {
|
||||
caption = Emoji.replaceEmoji(messageOwner.media.caption, textPaint.getFontMetricsInt(), AndroidUtilities.dp(20));
|
||||
caption = Emoji.replaceEmoji(messageOwner.media.caption, textPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||
if (containsUrls(caption)) {
|
||||
try {
|
||||
Linkify.addLinks((Spannable) caption, Linkify.WEB_URLS);
|
||||
|
@ -950,6 +957,17 @@ public class MessageObject {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean isMusicMessage(TLRPC.Message message) {
|
||||
if (message.media != null && message.media.document != null) {
|
||||
for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) {
|
||||
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static TLRPC.InputStickerSet getInputStickerSet(TLRPC.Message message) {
|
||||
if (message.media != null && message.media.document != null) {
|
||||
for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) {
|
||||
|
@ -986,6 +1004,8 @@ public class MessageObject {
|
|||
return AndroidUtilities.dp(100);
|
||||
} else if (type == 4) {
|
||||
return AndroidUtilities.dp(114);
|
||||
} else if (type == 14) {
|
||||
return AndroidUtilities.dp(78);
|
||||
} else if (type == 13) {
|
||||
float maxHeight = AndroidUtilities.displaySize.y * 0.4f;
|
||||
float maxWidth;
|
||||
|
@ -1061,6 +1081,39 @@ public class MessageObject {
|
|||
return isStickerMessage(messageOwner);
|
||||
}
|
||||
|
||||
public boolean isMusic() {
|
||||
return isMusicMessage(messageOwner);
|
||||
}
|
||||
|
||||
public String getMusicTitle() {
|
||||
for (TLRPC.DocumentAttribute attribute : messageOwner.media.document.attributes) {
|
||||
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
|
||||
String title = attribute.title;
|
||||
if (title == null || title.length() == 0) {
|
||||
title = FileLoader.getDocumentFileName(messageOwner.media.document);
|
||||
if (title == null || title.length() == 0) {
|
||||
title = LocaleController.getString("AudioUnknownTitle", R.string.AudioUnknownTitle);
|
||||
}
|
||||
}
|
||||
return title;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getMusicAuthor() {
|
||||
for (TLRPC.DocumentAttribute attribute : messageOwner.media.document.attributes) {
|
||||
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
|
||||
String performer = attribute.performer;
|
||||
if (performer == null || performer.length() == 0) {
|
||||
performer = LocaleController.getString("AudioUnknownArtist", R.string.AudioUnknownArtist);
|
||||
}
|
||||
return performer;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public TLRPC.InputStickerSet getInputStickerSet() {
|
||||
return getInputStickerSet(messageOwner);
|
||||
}
|
||||
|
|
|
@ -3900,7 +3900,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter
|
|||
}
|
||||
if (!markAsReadMessagesInbox.isEmpty() || !markAsReadMessagesOutbox.isEmpty() || !markAsReadEncrypted.isEmpty()) {
|
||||
if (!markAsReadMessagesInbox.isEmpty() || !markAsReadMessagesOutbox.isEmpty()) {
|
||||
MessagesStorage.getInstance().updateDialogsWithReadedMessages(markAsReadMessagesInbox, true);
|
||||
MessagesStorage.getInstance().updateDialogsWithReadMessages(markAsReadMessagesInbox, true);
|
||||
}
|
||||
MessagesStorage.getInstance().markMessagesAsRead(markAsReadMessagesInbox, markAsReadMessagesOutbox, markAsReadEncrypted, true);
|
||||
}
|
||||
|
|
|
@ -1149,7 +1149,7 @@ public class MessagesStorage {
|
|||
});
|
||||
}
|
||||
|
||||
private void updateDialogsWithReadedMessagesInternal(final ArrayList<Integer> messages, final HashMap<Integer, Integer> inbox) {
|
||||
private void updateDialogsWithReadMessagesInternal(final ArrayList<Integer> messages, final HashMap<Integer, Integer> inbox) {
|
||||
try {
|
||||
HashMap<Long, Integer> dialogsToUpdate = new HashMap<>();
|
||||
StringBuilder dialogsToReload = new StringBuilder();
|
||||
|
@ -1184,14 +1184,13 @@ public class MessagesStorage {
|
|||
SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(mid) FROM messages WHERE uid = %d AND mid <= %d AND read_state IN(0,2) AND out = 0", entry.getKey(), entry.getValue()));
|
||||
if (cursor.next()) {
|
||||
int count = cursor.intValue(0);
|
||||
if (count == 0) {
|
||||
continue;
|
||||
if (count != 0) {
|
||||
dialogsToUpdate.put((long) entry.getKey(), count);
|
||||
if (dialogsToReload.length() != 0) {
|
||||
dialogsToReload.append(",");
|
||||
}
|
||||
dialogsToReload.append(entry.getKey());
|
||||
}
|
||||
dialogsToUpdate.put((long) entry.getKey(), count);
|
||||
if (dialogsToReload.length() != 0) {
|
||||
dialogsToReload.append(",");
|
||||
}
|
||||
dialogsToReload.append(entry.getKey());
|
||||
}
|
||||
cursor.dispose();
|
||||
}
|
||||
|
@ -1231,7 +1230,7 @@ public class MessagesStorage {
|
|||
}
|
||||
}
|
||||
|
||||
public void updateDialogsWithReadedMessages(final HashMap<Integer, Integer> inbox, boolean useQueue) {
|
||||
public void updateDialogsWithReadMessages(final HashMap<Integer, Integer> inbox, boolean useQueue) {
|
||||
if (inbox.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1239,11 +1238,11 @@ public class MessagesStorage {
|
|||
storageQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateDialogsWithReadedMessagesInternal(null, inbox);
|
||||
updateDialogsWithReadMessagesInternal(null, inbox);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateDialogsWithReadedMessagesInternal(null, inbox);
|
||||
updateDialogsWithReadMessagesInternal(null, inbox);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3473,7 +3472,7 @@ public class MessagesStorage {
|
|||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesDeleted, mids);
|
||||
}
|
||||
});
|
||||
MessagesStorage.getInstance().updateDialogsWithReadedMessagesInternal(mids, null);
|
||||
MessagesStorage.getInstance().updateDialogsWithReadMessagesInternal(mids, null);
|
||||
MessagesStorage.getInstance().markMessagesAsDeletedInternal(mids);
|
||||
MessagesStorage.getInstance().updateDialogsWithDeletedMessagesInternal(mids);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 2.x.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-2015.
|
||||
*/
|
||||
|
||||
package org.telegram.android;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
public class MusicPlayerReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON)) {
|
||||
if (intent.getExtras() == null) {
|
||||
return;
|
||||
}
|
||||
KeyEvent keyEvent = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT);
|
||||
if (keyEvent == null) {
|
||||
return;
|
||||
}
|
||||
if (keyEvent.getAction() != KeyEvent.ACTION_DOWN)
|
||||
return;
|
||||
|
||||
switch (keyEvent.getKeyCode()) {
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
|
||||
if (MediaController.getInstance().isAudioPaused()) {
|
||||
MediaController.getInstance().playAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
} else {
|
||||
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
||||
MediaController.getInstance().playAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
||||
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_STOP:
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
||||
MediaController.getInstance().playNextMessage();
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
||||
MediaController.getInstance().playPreviousMessage();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (intent.getAction().equals(MusicPlayerService.NOTIFY_PLAY)) {
|
||||
MediaController.getInstance().playAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_PAUSE) || intent.getAction().equals(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
|
||||
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_NEXT)) {
|
||||
MediaController.getInstance().playNextMessage();
|
||||
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_CLOSE)) {
|
||||
MediaController.getInstance().clenupPlayer(true, true);
|
||||
} else if (intent.getAction().equals(MusicPlayerService.NOTIFY_PREVIOUS)) {
|
||||
MediaController.getInstance().playPreviousMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 2.x.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-2015.
|
||||
*/
|
||||
|
||||
package org.telegram.android;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.media.RemoteControlClient;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.LaunchActivity;
|
||||
|
||||
public class MusicPlayerService extends Service implements AudioManager.OnAudioFocusChangeListener, NotificationCenter.NotificationCenterDelegate {
|
||||
|
||||
public static final String NOTIFY_PREVIOUS = "org.telegram.android.musicplayer.previous";
|
||||
public static final String NOTIFY_CLOSE = "org.telegram.android.musicplayer.close";
|
||||
public static final String NOTIFY_PAUSE = "org.telegram.android.musicplayer.pause";
|
||||
public static final String NOTIFY_PLAY = "org.telegram.android.musicplayer.play";
|
||||
public static final String NOTIFY_NEXT = "org.telegram.android.musicplayer.next";
|
||||
|
||||
private RemoteControlClient remoteControlClient;
|
||||
private AudioManager audioManager;
|
||||
private static boolean ignoreAudioFocus = false;
|
||||
private PhoneStateListener phoneStateListener;
|
||||
|
||||
private static boolean supportBigNotifications = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
|
||||
private static boolean supportLockScreenControls = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioProgressDidChanged);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||
try {
|
||||
phoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onCallStateChanged(int state, String incomingNumber) {
|
||||
if (state == TelephonyManager.CALL_STATE_RINGING) {
|
||||
if (MediaController.getInstance().isPlayingAudio(MediaController.getInstance().getPlayingMessageObject()) && !MediaController.getInstance().isAudioPaused()) {
|
||||
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
}
|
||||
} else if (state == TelephonyManager.CALL_STATE_IDLE) {
|
||||
|
||||
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
|
||||
|
||||
}
|
||||
super.onCallStateChanged(state, incomingNumber);
|
||||
}
|
||||
};
|
||||
TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
|
||||
if (mgr != null) {
|
||||
mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
try {
|
||||
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
|
||||
if (messageObject == null) {
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopSelf();
|
||||
}
|
||||
});
|
||||
return START_STICKY;
|
||||
}
|
||||
if (supportLockScreenControls) {
|
||||
ComponentName remoteComponentName = new ComponentName(getApplicationContext(), MusicPlayerReceiver.class.getName());
|
||||
try {
|
||||
if (remoteControlClient == null) {
|
||||
audioManager.registerMediaButtonEventReceiver(remoteComponentName);
|
||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
mediaButtonIntent.setComponent(remoteComponentName);
|
||||
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
|
||||
remoteControlClient = new RemoteControlClient(mediaPendingIntent);
|
||||
audioManager.registerRemoteControlClient(remoteControlClient);
|
||||
}
|
||||
remoteControlClient.setTransportControlFlags(RemoteControlClient.FLAG_KEY_MEDIA_PLAY | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE |
|
||||
RemoteControlClient.FLAG_KEY_MEDIA_STOP | RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS | RemoteControlClient.FLAG_KEY_MEDIA_NEXT);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
createNotification(messageObject);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private void createNotification(MessageObject messageObject) {
|
||||
String songName = messageObject.getMusicTitle();
|
||||
String authorName = messageObject.getMusicAuthor();
|
||||
AudioInfo audioInfo = MediaController.getInstance().getAudioInfo();
|
||||
|
||||
RemoteViews simpleContentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.player_small_notification);
|
||||
RemoteViews expandedView = null;
|
||||
if (supportBigNotifications) {
|
||||
expandedView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.player_big_notification);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class);
|
||||
intent.setAction("com.tmessages.openplayer");
|
||||
intent.setFlags(32768);
|
||||
PendingIntent contentIntent = PendingIntent.getActivity(ApplicationLoader.applicationContext, 0, intent, 0);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(getApplicationContext())
|
||||
.setSmallIcon(R.drawable.player)
|
||||
.setContentIntent(contentIntent)
|
||||
.setContentTitle(songName).build();
|
||||
|
||||
notification.contentView = simpleContentView;
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView = expandedView;
|
||||
}
|
||||
|
||||
setListeners(simpleContentView);
|
||||
if (supportBigNotifications) {
|
||||
setListeners(expandedView);
|
||||
}
|
||||
|
||||
Bitmap albumArt = audioInfo != null ? audioInfo.getSmallCover() : null;
|
||||
if (albumArt != null) {
|
||||
notification.contentView.setImageViewBitmap(R.id.player_album_art, albumArt);
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView.setImageViewBitmap(R.id.player_album_art, albumArt);
|
||||
}
|
||||
} else {
|
||||
notification.contentView.setImageViewResource(R.id.player_album_art, R.drawable.nocover_small);
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView.setImageViewResource(R.id.player_album_art, R.drawable.nocover_big);
|
||||
}
|
||||
}
|
||||
if (MediaController.getInstance().isDownloadingCurrentMessage()) {
|
||||
notification.contentView.setViewVisibility(R.id.player_pause, View.GONE);
|
||||
notification.contentView.setViewVisibility(R.id.player_play, View.GONE);
|
||||
notification.contentView.setViewVisibility(R.id.player_next, View.GONE);
|
||||
notification.contentView.setViewVisibility(R.id.player_previous, View.GONE);
|
||||
notification.contentView.setViewVisibility(R.id.player_progress_bar, View.VISIBLE);
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView.setViewVisibility(R.id.player_pause, View.GONE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_play, View.GONE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_next, View.GONE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_previous, View.GONE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_progress_bar, View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
notification.contentView.setViewVisibility(R.id.player_progress_bar, View.GONE);
|
||||
notification.contentView.setViewVisibility(R.id.player_next, View.VISIBLE);
|
||||
notification.contentView.setViewVisibility(R.id.player_previous, View.VISIBLE);
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView.setViewVisibility(R.id.player_next, View.VISIBLE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_previous, View.VISIBLE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_progress_bar, View.GONE);
|
||||
}
|
||||
|
||||
if (MediaController.getInstance().isAudioPaused()) {
|
||||
notification.contentView.setViewVisibility(R.id.player_pause, View.GONE);
|
||||
notification.contentView.setViewVisibility(R.id.player_play, View.VISIBLE);
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView.setViewVisibility(R.id.player_pause, View.GONE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_play, View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
notification.contentView.setViewVisibility(R.id.player_pause, View.VISIBLE);
|
||||
notification.contentView.setViewVisibility(R.id.player_play, View.GONE);
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView.setViewVisibility(R.id.player_pause, View.VISIBLE);
|
||||
notification.bigContentView.setViewVisibility(R.id.player_play, View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notification.contentView.setTextViewText(R.id.player_song_name, songName);
|
||||
notification.contentView.setTextViewText(R.id.player_author_name, authorName);
|
||||
if (supportBigNotifications) {
|
||||
notification.bigContentView.setTextViewText(R.id.player_song_name, songName);
|
||||
notification.bigContentView.setTextViewText(R.id.player_author_name, authorName);
|
||||
}
|
||||
notification.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
startForeground(5, notification);
|
||||
|
||||
if (remoteControlClient != null) {
|
||||
RemoteControlClient.MetadataEditor metadataEditor = remoteControlClient.editMetadata(true);
|
||||
metadataEditor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, authorName);
|
||||
metadataEditor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, songName);
|
||||
if (audioInfo != null && audioInfo.getCover() != null) {
|
||||
metadataEditor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, audioInfo.getCover());
|
||||
}
|
||||
metadataEditor.apply();
|
||||
audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
||||
}
|
||||
}
|
||||
|
||||
public void setListeners(RemoteViews view) {
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
view.setOnClickPendingIntent(R.id.player_previous, pendingIntent);
|
||||
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
view.setOnClickPendingIntent(R.id.player_close, pendingIntent);
|
||||
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
view.setOnClickPendingIntent(R.id.player_pause, pendingIntent);
|
||||
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
view.setOnClickPendingIntent(R.id.player_next, pendingIntent);
|
||||
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent(NOTIFY_PLAY), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
view.setOnClickPendingIntent(R.id.player_play, pendingIntent);
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (remoteControlClient != null) {
|
||||
RemoteControlClient.MetadataEditor metadataEditor = remoteControlClient.editMetadata(true);
|
||||
metadataEditor.clear();
|
||||
metadataEditor.apply();
|
||||
audioManager.unregisterRemoteControlClient(remoteControlClient);
|
||||
}
|
||||
try {
|
||||
TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
|
||||
if (mgr != null) {
|
||||
mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioProgressDidChanged);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioFocusChange(int focusChange) {
|
||||
if (ignoreAudioFocus) {
|
||||
ignoreAudioFocus = false;
|
||||
return;
|
||||
}
|
||||
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
|
||||
if (MediaController.getInstance().isPlayingAudio(MediaController.getInstance().getPlayingMessageObject()) && !MediaController.getInstance().isAudioPaused()) {
|
||||
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
}
|
||||
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
|
||||
//MediaController.getInstance().playAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didReceivedNotification(int id, Object... args) {
|
||||
if (id == NotificationCenter.audioPlayStateChanged) {
|
||||
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
|
||||
if (messageObject != null) {
|
||||
createNotification(messageObject);
|
||||
} else {
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setIgnoreAudioFocus() {
|
||||
ignoreAudioFocus = true;
|
||||
}
|
||||
}
|
|
@ -64,6 +64,7 @@ public class NotificationCenter {
|
|||
public static final int botInfoDidLoaded = totalEvents++;
|
||||
public static final int botKeyboardDidLoaded = totalEvents++;
|
||||
public static final int chatSearchResultsAvailable = totalEvents++;
|
||||
public static final int musicDidLoaded = totalEvents++;
|
||||
|
||||
public static final int httpFileDidLoaded = totalEvents++;
|
||||
public static final int httpFileDidFailedLoad = totalEvents++;
|
||||
|
@ -89,6 +90,7 @@ public class NotificationCenter {
|
|||
|
||||
public static final int audioProgressDidChanged = totalEvents++;
|
||||
public static final int audioDidReset = totalEvents++;
|
||||
public static final int audioPlayStateChanged = totalEvents++;
|
||||
public static final int recordProgressChanged = totalEvents++;
|
||||
public static final int recordStarted = totalEvents++;
|
||||
public static final int recordStartError = totalEvents++;
|
||||
|
|
|
@ -31,8 +31,6 @@ import android.support.v4.app.NotificationCompat;
|
|||
import android.support.v4.app.NotificationManagerCompat;
|
||||
import android.support.v4.app.RemoteInput;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.telegram.messenger.ConnectionsManager;
|
||||
import org.telegram.messenger.DispatchQueue;
|
||||
import org.telegram.messenger.FileLog;
|
||||
|
@ -602,11 +600,8 @@ public class NotificationsController {
|
|||
}
|
||||
|
||||
String lastMessage = null;
|
||||
String lastMessageFull = null;
|
||||
if (pushMessages.size() == 1) {
|
||||
String message = lastMessageFull = getStringForMessage(pushMessages.get(0), false);
|
||||
//lastMessage = getStringForMessage(pushMessages.get(0), true);
|
||||
lastMessage = lastMessageFull;
|
||||
String message = lastMessage = getStringForMessage(pushMessages.get(0), false);
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -630,8 +625,7 @@ public class NotificationsController {
|
|||
continue;
|
||||
}
|
||||
if (i == 0) {
|
||||
lastMessageFull = message;
|
||||
lastMessage = lastMessageFull;
|
||||
lastMessage = message;
|
||||
}
|
||||
if (pushDialogs.size() == 1) {
|
||||
if (replace) {
|
||||
|
@ -692,9 +686,6 @@ public class NotificationsController {
|
|||
|
||||
showExtraNotifications(mBuilder, notifyAboutLast);
|
||||
notificationManager.notify(1, mBuilder.build());
|
||||
if (preferences.getBoolean("EnablePebbleNotifications", false)) {
|
||||
sendAlertToPebble(lastMessageFull);
|
||||
}
|
||||
|
||||
scheduleNotificationRepeat();
|
||||
} catch (Exception e) {
|
||||
|
@ -897,26 +888,6 @@ public class NotificationsController {
|
|||
}
|
||||
}
|
||||
|
||||
private void sendAlertToPebble(String message) {
|
||||
try {
|
||||
final Intent i = new Intent("com.getpebble.action.SEND_NOTIFICATION");
|
||||
|
||||
final HashMap<String, String> data = new HashMap<>();
|
||||
data.put("title", LocaleController.getString("AppName", R.string.AppName));
|
||||
data.put("body", message);
|
||||
final JSONObject jsonData = new JSONObject(data);
|
||||
final String notificationData = new JSONArray().put(jsonData).toString();
|
||||
|
||||
i.putExtra("messageType", "PEBBLE_ALERT");
|
||||
i.putExtra("sender", LocaleController.formatString("AppName", R.string.AppName));
|
||||
i.putExtra("notificationData", notificationData);
|
||||
|
||||
ApplicationLoader.applicationContext.sendBroadcast(i);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void processReadMessages(HashMap<Integer, Integer> inbox, long dialog_id, int max_date, int max_id, boolean isPopup) {
|
||||
int oldCount = popupMessages.size();
|
||||
if (inbox != null) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import android.provider.MediaStore;
|
|||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
import org.telegram.messenger.ConnectionsManager;
|
||||
import org.telegram.messenger.FileLoader;
|
||||
import org.telegram.messenger.FileLog;
|
||||
|
@ -557,6 +558,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
|
||||
for (int a = 0; a < messages.size(); a++) {
|
||||
MessageObject msgObj = messages.get(a);
|
||||
if (msgObj.getId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final TLRPC.Message newMsg = new TLRPC.TL_message();
|
||||
newMsg.flags |= TLRPC.MESSAGE_FLAG_FWD;
|
||||
|
@ -1858,6 +1862,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
return false;
|
||||
}
|
||||
MimeTypeMap myMime = MimeTypeMap.getSingleton();
|
||||
TLRPC.TL_documentAttributeAudio attributeAudio = null;
|
||||
if (uri != null) {
|
||||
String extension = null;
|
||||
if (mime != null) {
|
||||
|
@ -1885,8 +1890,31 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
if (idx != -1) {
|
||||
ext = path.substring(idx + 1);
|
||||
}
|
||||
if (ext.toLowerCase().equals("mp3") || ext.toLowerCase().equals("m4a")) {
|
||||
AudioInfo audioInfo = AudioInfo.getAudioInfo(f);
|
||||
if (audioInfo != null && audioInfo.getDuration() != 0) {
|
||||
if (isEncrypted) {
|
||||
attributeAudio = new TLRPC.TL_documentAttributeAudio_old();
|
||||
} else {
|
||||
attributeAudio = new TLRPC.TL_documentAttributeAudio();
|
||||
}
|
||||
attributeAudio.duration = (int) (audioInfo.getDuration() / 1000);
|
||||
attributeAudio.title = audioInfo.getTitle();
|
||||
attributeAudio.performer = audioInfo.getArtist();
|
||||
if (attributeAudio.title == null) {
|
||||
attributeAudio.title = "";
|
||||
}
|
||||
if (attributeAudio.performer == null) {
|
||||
attributeAudio.performer = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (originalPath != null) {
|
||||
originalPath += "" + f.length();
|
||||
if (attributeAudio != null) {
|
||||
originalPath += "audio" + f.length();
|
||||
} else {
|
||||
originalPath += "" + f.length();
|
||||
}
|
||||
}
|
||||
|
||||
TLRPC.TL_document document = null;
|
||||
|
@ -1905,6 +1933,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
document.attributes.add(fileName);
|
||||
document.size = (int) f.length();
|
||||
document.dc_id = 0;
|
||||
if (attributeAudio != null) {
|
||||
document.attributes.add(attributeAudio);
|
||||
}
|
||||
if (ext.length() != 0) {
|
||||
if (ext.toLowerCase().equals("webp")) {
|
||||
document.mime_type = "image/webp";
|
||||
|
@ -1990,6 +2021,56 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
|||
prepareSendingDocuments(paths, originalPaths, uris, mine, dialog_id, reply_to_msg);
|
||||
}
|
||||
|
||||
public static void prepareSendingAudioDocuments(final ArrayList<MessageObject> messageObjects, final long dialog_id, final MessageObject reply_to_msg) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int size = messageObjects.size();
|
||||
for (int a = 0; a < size; a++) {
|
||||
final MessageObject messageObject = messageObjects.get(a);
|
||||
String originalPath = messageObject.messageOwner.attachPath;
|
||||
final File f = new File(originalPath);
|
||||
|
||||
boolean isEncrypted = (int) dialog_id == 0;
|
||||
|
||||
|
||||
if (originalPath != null) {
|
||||
originalPath += "audio" + f.length();
|
||||
}
|
||||
|
||||
TLRPC.TL_document document = null;
|
||||
if (!isEncrypted) {
|
||||
document = (TLRPC.TL_document) MessagesStorage.getInstance().getSentFile(originalPath, !isEncrypted ? 1 : 4);
|
||||
}
|
||||
if (document == null) {
|
||||
document = (TLRPC.TL_document) messageObject.messageOwner.media.document;
|
||||
}
|
||||
|
||||
if (isEncrypted) {
|
||||
for (int b = 0; b < document.attributes.size(); b++) {
|
||||
if (document.attributes.get(b) instanceof TLRPC.TL_documentAttributeAudio) {
|
||||
TLRPC.TL_documentAttributeAudio_old old = new TLRPC.TL_documentAttributeAudio_old();
|
||||
old.duration = document.attributes.get(b).duration;
|
||||
document.attributes.remove(b);
|
||||
document.attributes.add(old);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final String originalPathFinal = originalPath;
|
||||
final TLRPC.TL_document documentFinal = document;
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
SendMessagesHelper.getInstance().sendMessage(documentFinal, originalPathFinal, messageObject.messageOwner.attachPath, dialog_id, reply_to_msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static void prepareSendingDocuments(final ArrayList<String> paths, final ArrayList<String> originalPaths, final ArrayList<Uri> uris, final String mime, final long dialog_id, final MessageObject reply_to_msg) {
|
||||
if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) {
|
||||
return;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package org.telegram.android;
|
||||
|
||||
import org.telegram.PhoneFormat.PhoneFormat;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.messenger.TLRPC;
|
||||
|
||||
|
@ -29,7 +30,8 @@ public class UserObject {
|
|||
if (user == null || isDeleted(user)) {
|
||||
return LocaleController.getString("HiddenName", R.string.HiddenName);
|
||||
}
|
||||
return ContactsController.formatName(user.first_name, user.last_name);
|
||||
String name = ContactsController.formatName(user.first_name, user.last_name);
|
||||
return name.length() != 0 || user.phone == null || user.phone.length() == 0 ? name : PhoneFormat.getInstance().format("+" + user.phone);
|
||||
}
|
||||
|
||||
public static String getFirstName(TLRPC.User user) {
|
||||
|
@ -40,6 +42,6 @@ public class UserObject {
|
|||
if (name == null || name.length() == 0) {
|
||||
name = user.last_name;
|
||||
}
|
||||
return name != null && name.length() > 0 ? name : "DELETED";
|
||||
return name != null && name.length() > 0 ? name : LocaleController.getString("HiddenName", R.string.HiddenName);
|
||||
}
|
||||
}
|
||||
|
|
154
TMessagesProj/src/main/java/org/telegram/android/audioinfo/AudioInfo.java
Executable file
154
TMessagesProj/src/main/java/org/telegram/android/audioinfo/AudioInfo.java
Executable file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import org.telegram.android.audioinfo.m4a.M4AInfo;
|
||||
import org.telegram.android.audioinfo.mp3.MP3Info;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
public abstract class AudioInfo {
|
||||
protected String brand; // brand, e.g. "M4A", "ID3", ...
|
||||
protected String version; // version, e.g. "0", "2.3.0", ...
|
||||
|
||||
protected long duration; // track duration (milliseconds)
|
||||
|
||||
protected String title; // track title
|
||||
protected String artist; // track artist
|
||||
protected String albumArtist; // album artist
|
||||
protected String album; // album title
|
||||
protected short year; // year...
|
||||
protected String genre; // genre name
|
||||
protected String comment; // comment...
|
||||
protected short track; // track number
|
||||
protected short tracks; // number of tracks
|
||||
protected short disc; // disc number
|
||||
protected short discs; // number of discs
|
||||
protected String copyright; // copyright notice
|
||||
protected String composer; // composer name
|
||||
protected String grouping; // track grouping
|
||||
protected boolean compilation; // compilation flag
|
||||
protected String lyrics; // song lyrics
|
||||
protected Bitmap cover; // cover image data
|
||||
protected Bitmap smallCover; // cover image data
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public long getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getAlbumArtist() {
|
||||
return albumArtist;
|
||||
}
|
||||
|
||||
public String getAlbum() {
|
||||
return album;
|
||||
}
|
||||
|
||||
public short getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public String getGenre() {
|
||||
return genre;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public short getTrack() {
|
||||
return track;
|
||||
}
|
||||
|
||||
public short getTracks() {
|
||||
return tracks;
|
||||
}
|
||||
|
||||
public short getDisc() {
|
||||
return disc;
|
||||
}
|
||||
|
||||
public short getDiscs() {
|
||||
return discs;
|
||||
}
|
||||
|
||||
public String getCopyright() {
|
||||
return copyright;
|
||||
}
|
||||
|
||||
public String getComposer() {
|
||||
return composer;
|
||||
}
|
||||
|
||||
public String getGrouping() {
|
||||
return grouping;
|
||||
}
|
||||
|
||||
public boolean isCompilation() {
|
||||
return compilation;
|
||||
}
|
||||
|
||||
public String getLyrics() {
|
||||
return lyrics;
|
||||
}
|
||||
|
||||
public Bitmap getCover() {
|
||||
return cover;
|
||||
}
|
||||
|
||||
public Bitmap getSmallCover() {
|
||||
return smallCover;
|
||||
}
|
||||
|
||||
public static AudioInfo getAudioInfo(File file) {
|
||||
try {
|
||||
byte header[] = new byte[12];
|
||||
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
|
||||
randomAccessFile.readFully(header, 0, 8);
|
||||
randomAccessFile.close();
|
||||
InputStream input = new BufferedInputStream(new FileInputStream(file));
|
||||
if (header[4] == 'f' && header[5] == 't' && header[6] == 'y' && header[7] == 'p') {
|
||||
return new M4AInfo(input);
|
||||
} else {
|
||||
return new MP3Info(input, file.length());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
325
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/M4AInfo.java
Executable file
325
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/M4AInfo.java
Executable file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.m4a;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
import org.telegram.android.audioinfo.mp3.ID3v1Genre;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class M4AInfo extends AudioInfo {
|
||||
static final Logger LOGGER = Logger.getLogger(M4AInfo.class.getName());
|
||||
|
||||
private static final String ASCII = "ISO8859_1";
|
||||
private static final String UTF_8 = "UTF-8";
|
||||
|
||||
private BigDecimal volume; // normal = 1.0
|
||||
private BigDecimal speed; // normal = 1.0
|
||||
|
||||
private short tempo;
|
||||
private byte rating; // none = 0, clean = 2, explicit = 4
|
||||
|
||||
private final Level debugLevel;
|
||||
|
||||
public M4AInfo(InputStream input) throws IOException {
|
||||
this(input, Level.FINEST);
|
||||
}
|
||||
|
||||
public M4AInfo(InputStream input, Level debugLevel) throws IOException {
|
||||
this.debugLevel = debugLevel;
|
||||
MP4Input mp4 = new MP4Input(input);
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, mp4.toString());
|
||||
}
|
||||
ftyp(mp4.nextChild("ftyp"));
|
||||
moov(mp4.nextChildUpTo("moov"));
|
||||
}
|
||||
|
||||
void ftyp(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
brand = atom.readString(4, ASCII).trim();
|
||||
if (brand.matches("M4V|MP4|mp42|isom")) { // experimental file types
|
||||
LOGGER.warning(atom.getPath() + ": brand=" + brand + " (experimental)");
|
||||
} else if (!brand.matches("M4A|M4P")) {
|
||||
LOGGER.warning(atom.getPath() + ": brand=" + brand + " (expected M4A or M4P)");
|
||||
}
|
||||
version = String.valueOf(atom.readInt());
|
||||
}
|
||||
|
||||
void moov(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
while (atom.hasMoreChildren()) {
|
||||
MP4Atom child = atom.nextChild();
|
||||
switch (child.getType()) {
|
||||
case "mvhd":
|
||||
mvhd(child);
|
||||
break;
|
||||
case "trak":
|
||||
trak(child);
|
||||
break;
|
||||
case "udta":
|
||||
udta(child);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mvhd(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
byte version = atom.readByte();
|
||||
atom.skip(3); // flags
|
||||
atom.skip(version == 1 ? 16 : 8); // created/modified date
|
||||
int scale = atom.readInt();
|
||||
long units = version == 1 ? atom.readLong() : atom.readInt();
|
||||
if (duration == 0) {
|
||||
duration = 1000 * units / scale;
|
||||
} else if (LOGGER.isLoggable(debugLevel) && Math.abs(duration - 1000 * units / scale) > 2) {
|
||||
LOGGER.log(debugLevel, "mvhd: duration " + duration + " -> " + (1000 * units / scale));
|
||||
}
|
||||
speed = atom.readIntegerFixedPoint();
|
||||
volume = atom.readShortFixedPoint();
|
||||
}
|
||||
|
||||
void trak(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
mdia(atom.nextChildUpTo("mdia"));
|
||||
}
|
||||
|
||||
void mdia(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
mdhd(atom.nextChild("mdhd"));
|
||||
}
|
||||
|
||||
void mdhd(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
byte version = atom.readByte();
|
||||
atom.skip(3);
|
||||
atom.skip(version == 1 ? 16 : 8); // created/modified date
|
||||
int sampleRate = atom.readInt();
|
||||
long samples = version == 1 ? atom.readLong() : atom.readInt();
|
||||
if (duration == 0) {
|
||||
duration = 1000 * samples / sampleRate;
|
||||
} else if (LOGGER.isLoggable(debugLevel) && Math.abs(duration - 1000 * samples / sampleRate) > 2) {
|
||||
LOGGER.log(debugLevel, "mdhd: duration " + duration + " -> " + (1000 * samples / sampleRate));
|
||||
}
|
||||
}
|
||||
|
||||
void udta(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
while (atom.hasMoreChildren()) {
|
||||
MP4Atom child = atom.nextChild();
|
||||
if ("meta".equals(child.getType())) {
|
||||
meta(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void meta(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
atom.skip(4); // version/flags
|
||||
while (atom.hasMoreChildren()) {
|
||||
MP4Atom child = atom.nextChild();
|
||||
if ("ilst".equals(child.getType())) {
|
||||
ilst(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ilst(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
while (atom.hasMoreChildren()) {
|
||||
MP4Atom child = atom.nextChild();
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, child.toString());
|
||||
}
|
||||
if (child.getRemaining() == 0) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, child.getPath() + ": contains no value");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
data(child.nextChildUpTo("data"));
|
||||
}
|
||||
}
|
||||
|
||||
void data(MP4Atom atom) throws IOException {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, atom.toString());
|
||||
}
|
||||
atom.skip(4); // version & flags
|
||||
atom.skip(4); // reserved
|
||||
switch (atom.getParent().getType()) {
|
||||
case "©alb":
|
||||
album = atom.readString(UTF_8);
|
||||
break;
|
||||
case "aART":
|
||||
albumArtist = atom.readString(UTF_8);
|
||||
break;
|
||||
case "©ART":
|
||||
artist = atom.readString(UTF_8);
|
||||
break;
|
||||
case "©cmt":
|
||||
comment = atom.readString(UTF_8);
|
||||
break;
|
||||
case "©com":
|
||||
case "©wrt":
|
||||
if (composer == null || composer.trim().length() == 0) {
|
||||
composer = atom.readString(UTF_8);
|
||||
}
|
||||
break;
|
||||
case "covr":
|
||||
try {
|
||||
byte[] bytes = atom.readBytes();
|
||||
BitmapFactory.Options opts = new BitmapFactory.Options();
|
||||
opts.inJustDecodeBounds = true;
|
||||
opts.inSampleSize = 1;
|
||||
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);
|
||||
if (opts.outWidth > 800 || opts.outHeight > 800) {
|
||||
int size = Math.max(opts.outWidth, opts.outHeight);
|
||||
while (size > 800) {
|
||||
opts.inSampleSize *= 2;
|
||||
size /= 2;
|
||||
}
|
||||
}
|
||||
opts.inJustDecodeBounds = false;
|
||||
cover = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);
|
||||
if (cover != null) {
|
||||
float scale = Math.max(cover.getWidth(), cover.getHeight()) / 120.0f;
|
||||
if (scale > 0) {
|
||||
smallCover = Bitmap.createScaledBitmap(cover, (int) (cover.getWidth() / scale), (int) (cover.getHeight() / scale), true);
|
||||
} else {
|
||||
smallCover = cover;
|
||||
}
|
||||
if (smallCover == null) {
|
||||
smallCover = cover;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
case "cpil":
|
||||
compilation = atom.readBoolean();
|
||||
break;
|
||||
case "cprt":
|
||||
case "©cpy":
|
||||
if (copyright == null || copyright.trim().length() == 0) {
|
||||
copyright = atom.readString(UTF_8);
|
||||
}
|
||||
break;
|
||||
case "©day":
|
||||
String day = atom.readString(UTF_8).trim();
|
||||
if (day.length() >= 4) {
|
||||
try {
|
||||
year = Short.valueOf(day.substring(0, 4));
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "disk":
|
||||
atom.skip(2); // padding?
|
||||
disc = atom.readShort();
|
||||
discs = atom.readShort();
|
||||
break;
|
||||
case "gnre":
|
||||
if (genre == null || genre.trim().length() == 0) {
|
||||
if (atom.getRemaining() == 2) { // id3v1 genre?
|
||||
int index = atom.readShort() - 1;
|
||||
ID3v1Genre id3v1Genre = ID3v1Genre.getGenre(index);
|
||||
if (id3v1Genre != null) {
|
||||
genre = id3v1Genre.getDescription();
|
||||
}
|
||||
} else {
|
||||
genre = atom.readString(UTF_8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "©gen":
|
||||
if (genre == null || genre.trim().length() == 0) {
|
||||
genre = atom.readString(UTF_8);
|
||||
}
|
||||
break;
|
||||
case "©grp":
|
||||
grouping = atom.readString(UTF_8);
|
||||
break;
|
||||
case "©lyr":
|
||||
lyrics = atom.readString(UTF_8);
|
||||
break;
|
||||
case "©nam":
|
||||
title = atom.readString(UTF_8);
|
||||
break;
|
||||
case "rtng":
|
||||
rating = atom.readByte();
|
||||
break;
|
||||
case "tmpo":
|
||||
tempo = atom.readShort();
|
||||
break;
|
||||
case "trkn":
|
||||
atom.skip(2); // padding?
|
||||
track = atom.readShort();
|
||||
tracks = atom.readShort();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public short getTempo() {
|
||||
return tempo;
|
||||
}
|
||||
|
||||
public byte getRating() {
|
||||
return rating;
|
||||
}
|
||||
|
||||
public BigDecimal getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public BigDecimal getVolume() {
|
||||
return volume;
|
||||
}
|
||||
}
|
151
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/MP4Atom.java
Executable file
151
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/MP4Atom.java
Executable file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.m4a;
|
||||
|
||||
import org.telegram.android.audioinfo.util.RangeInputStream;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class MP4Atom extends MP4Box<RangeInputStream> {
|
||||
public MP4Atom(RangeInputStream input, MP4Box<?> parent, String type) {
|
||||
super(input, parent, type);
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return getInput().getPosition() + getInput().getRemainingLength();
|
||||
}
|
||||
|
||||
public long getOffset() {
|
||||
return getParent().getPosition() - getPosition();
|
||||
}
|
||||
|
||||
public long getRemaining() {
|
||||
return getInput().getRemainingLength();
|
||||
}
|
||||
|
||||
public boolean hasMoreChildren() {
|
||||
return (getChild() != null ? getChild().getRemaining() : 0) < getRemaining();
|
||||
}
|
||||
|
||||
public MP4Atom nextChildUpTo(String expectedTypeExpression) throws IOException {
|
||||
while (getRemaining() > 0) {
|
||||
MP4Atom atom = nextChild();
|
||||
if (atom.getType().matches(expectedTypeExpression)) {
|
||||
return atom;
|
||||
}
|
||||
}
|
||||
throw new IOException("atom type mismatch, not found: " + expectedTypeExpression);
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws IOException {
|
||||
return data.readBoolean();
|
||||
}
|
||||
|
||||
public byte readByte() throws IOException {
|
||||
return data.readByte();
|
||||
}
|
||||
|
||||
public short readShort() throws IOException {
|
||||
return data.readShort();
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
return data.readInt();
|
||||
}
|
||||
|
||||
public long readLong() throws IOException {
|
||||
return data.readLong();
|
||||
}
|
||||
|
||||
public byte[] readBytes(int len) throws IOException {
|
||||
byte[] bytes = new byte[len];
|
||||
data.readFully(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public byte[] readBytes() throws IOException {
|
||||
return readBytes((int) getRemaining());
|
||||
}
|
||||
|
||||
public BigDecimal readShortFixedPoint() throws IOException {
|
||||
int integer = data.readByte();
|
||||
int decimal = data.readUnsignedByte();
|
||||
return new BigDecimal(String.valueOf(integer) + "" + String.valueOf(decimal));
|
||||
}
|
||||
|
||||
public BigDecimal readIntegerFixedPoint() throws IOException {
|
||||
int integer = data.readShort();
|
||||
int decimal = data.readUnsignedShort();
|
||||
return new BigDecimal(String.valueOf(integer) + "" + String.valueOf(decimal));
|
||||
}
|
||||
|
||||
public String readString(int len, String enc) throws IOException {
|
||||
String s = new String(readBytes(len), enc);
|
||||
int end = s.indexOf(0);
|
||||
return end < 0 ? s : s.substring(0, end);
|
||||
}
|
||||
|
||||
public String readString(String enc) throws IOException {
|
||||
return readString((int) getRemaining(), enc);
|
||||
}
|
||||
|
||||
public void skip(int len) throws IOException {
|
||||
int total = 0;
|
||||
while (total < len) {
|
||||
int current = data.skipBytes(len - total);
|
||||
if (current > 0) {
|
||||
total += current;
|
||||
} else {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip() throws IOException {
|
||||
while (getRemaining() > 0) {
|
||||
if (getInput().skip(getRemaining()) == 0) {
|
||||
throw new EOFException("Cannot skip atom");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuffer appendPath(StringBuffer s, MP4Box<?> box) {
|
||||
if (box.getParent() != null) {
|
||||
appendPath(s, box.getParent());
|
||||
s.append("/");
|
||||
}
|
||||
return s.append(box.getType());
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return appendPath(new StringBuffer(), this).toString();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer s = new StringBuffer();
|
||||
appendPath(s, this);
|
||||
s.append("[off=");
|
||||
s.append(getOffset());
|
||||
s.append(",pos=");
|
||||
s.append(getPosition());
|
||||
s.append(",len=");
|
||||
s.append(getLength());
|
||||
s.append("]");
|
||||
return s.toString();
|
||||
}
|
||||
}
|
87
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/MP4Box.java
Executable file
87
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/MP4Box.java
Executable file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.m4a;
|
||||
|
||||
import org.telegram.android.audioinfo.util.PositionInputStream;
|
||||
import org.telegram.android.audioinfo.util.RangeInputStream;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MP4Box<I extends PositionInputStream> {
|
||||
protected static final String ASCII = "ISO8859_1";
|
||||
|
||||
private final I input;
|
||||
private final MP4Box<?> parent;
|
||||
private final String type;
|
||||
|
||||
protected final DataInput data;
|
||||
|
||||
private MP4Atom child;
|
||||
|
||||
public MP4Box(I input, MP4Box<?> parent, String type) {
|
||||
this.input = input;
|
||||
this.parent = parent;
|
||||
this.type = type;
|
||||
this.data = new DataInputStream(input);
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public MP4Box<?> getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public long getPosition() {
|
||||
return input.getPosition();
|
||||
}
|
||||
|
||||
public I getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
protected MP4Atom getChild() {
|
||||
return child;
|
||||
}
|
||||
|
||||
public MP4Atom nextChild() throws IOException {
|
||||
if (child != null) {
|
||||
child.skip();
|
||||
}
|
||||
int atomLength = data.readInt();
|
||||
byte[] typeBytes = new byte[4];
|
||||
data.readFully(typeBytes);
|
||||
String atomType = new String(typeBytes, ASCII);
|
||||
RangeInputStream atomInput;
|
||||
if (atomLength == 1) { // extended length
|
||||
atomInput = new RangeInputStream(input, 16, data.readLong() - 16);
|
||||
} else {
|
||||
atomInput = new RangeInputStream(input, 8, atomLength - 8);
|
||||
}
|
||||
return child = new MP4Atom(atomInput, this, atomType);
|
||||
}
|
||||
|
||||
public MP4Atom nextChild(String expectedTypeExpression) throws IOException {
|
||||
MP4Atom atom = nextChild();
|
||||
if (atom.getType().matches(expectedTypeExpression)) {
|
||||
return atom;
|
||||
}
|
||||
throw new IOException("atom type mismatch, expected " + expectedTypeExpression + ", got " + atom.getType());
|
||||
}
|
||||
}
|
41
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/MP4Input.java
Executable file
41
TMessagesProj/src/main/java/org/telegram/android/audioinfo/m4a/MP4Input.java
Executable file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.m4a;
|
||||
|
||||
import org.telegram.android.audioinfo.util.PositionInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public final class MP4Input extends MP4Box<PositionInputStream> {
|
||||
|
||||
public MP4Input(InputStream delegate) {
|
||||
super(new PositionInputStream(delegate), null, "");
|
||||
}
|
||||
|
||||
public MP4Atom nextChildUpTo(String expectedTypeExpression) throws IOException {
|
||||
while (true) {
|
||||
MP4Atom atom = nextChild();
|
||||
if (atom.getType().matches(expectedTypeExpression)) {
|
||||
return atom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "mp4[pos=" + getPosition() + "]";
|
||||
}
|
||||
}
|
171
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/ID3v1Genre.java
Executable file
171
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/ID3v1Genre.java
Executable file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
public enum ID3v1Genre {
|
||||
/*
|
||||
* The following genres is defined in ID3v1 (0-79)
|
||||
*/
|
||||
Blues("Blues"),
|
||||
ClassicRock("Classic Rock"),
|
||||
Country("Country"),
|
||||
Dance("Dance"),
|
||||
Disco("Disco"),
|
||||
Funk("Funk"),
|
||||
Grunge("Grunge"),
|
||||
HipHop("Hip-Hop"),
|
||||
Jazz("Jazz"),
|
||||
Metal("Metal"),
|
||||
NewAge("New Age"),
|
||||
Oldies("Oldies"),
|
||||
Other("Other"),
|
||||
Pop("Pop"),
|
||||
RnB("R&B"),
|
||||
Rap("Rap"),
|
||||
Reggae("Reggae"),
|
||||
Rock("Rock"),
|
||||
Techno("Techno"),
|
||||
Industrial("Industrial"),
|
||||
Alternative("Alternative"),
|
||||
Ska("Ska"),
|
||||
DeathMetal("Death Metal"),
|
||||
Pranks("Pranks"),
|
||||
Soundtrack("Soundtrack"),
|
||||
EuroTechno("Euro-Techno"),
|
||||
Ambient("Ambient"),
|
||||
TripHop("Trip-Hop"),
|
||||
Vocal("Vocal"),
|
||||
JazzFunk("Jazz+Funk"),
|
||||
Fusion("Fusion"),
|
||||
Trance("Trance"),
|
||||
Classical("Classical"),
|
||||
Instrumental("Instrumental"),
|
||||
Acid("Acid"),
|
||||
House("House"),
|
||||
Game("Game"),
|
||||
SoundClip("Sound Clip"),
|
||||
Gospel("Gospel"),
|
||||
Noise("Noise"),
|
||||
AlternRock("AlternRock"),
|
||||
Bass("Bass"),
|
||||
Soul("Soul"),
|
||||
Punk("Punk"),
|
||||
Space("Space"),
|
||||
Meditative("Meditative"),
|
||||
InstrumentalPop("Instrumental Pop"),
|
||||
InstrumentalRock("Instrumental Rock"),
|
||||
Ethnic("Ethnic"),
|
||||
Gothic("Gothic"),
|
||||
Darkwave("Darkwave"),
|
||||
TechnoIndustrial("Techno-Industrial"),
|
||||
Electronic("Electronic"),
|
||||
PopFolk("Pop-Folk"),
|
||||
Eurodance("Eurodance"),
|
||||
Dream("Dream"),
|
||||
SouthernRock("Southern Rock"),
|
||||
Comedy("Comedy"),
|
||||
Cult("Cult"),
|
||||
Gangsta("Gangsta"),
|
||||
Top40("Top 40"),
|
||||
ChristianRap("Christian Rap"),
|
||||
PopFunk("Pop/Funk"),
|
||||
Jungle("Jungle"),
|
||||
NativeAmerican("Native American"),
|
||||
Cabaret("Cabaret"),
|
||||
NewWave("New Wave"),
|
||||
Psychadelic("Psychadelic"),
|
||||
Rave("Rave"),
|
||||
Showtunes("Showtunes"),
|
||||
Trailer("Trailer"),
|
||||
LoFi("Lo-Fi"),
|
||||
Tribal("Tribal"),
|
||||
AcidPunk("Acid Punk"),
|
||||
AcidJazz("Acid Jazz"),
|
||||
Polka("Polka"),
|
||||
Retro("Retro"),
|
||||
Musical("Musical"),
|
||||
RockAndRoll("Rock & Roll"),
|
||||
HardRock("Hard Rock"),
|
||||
|
||||
/*
|
||||
* The following genres are Winamp extensions (80-125)
|
||||
*/
|
||||
Folk("Folk"),
|
||||
FolkRock("Folk-Rock"),
|
||||
NationalFolk("National Folk"),
|
||||
Swing("Swing"),
|
||||
FastFusion("Fast Fusion"),
|
||||
Bebop("Bebop"),
|
||||
Latin("Latin"),
|
||||
Revival("Revival"),
|
||||
Celtic("Celtic"),
|
||||
Bluegrass("Bluegrass"),
|
||||
Avantgarde("Avantgarde"),
|
||||
GothicRock("Gothic Rock"),
|
||||
ProgressiveRock("Progressive Rock"),
|
||||
PsychedelicRock("Psychedelic Rock"),
|
||||
SymphonicRock("Symphonic Rock"),
|
||||
SlowRock("Slow Rock"),
|
||||
BigBand("Big Band"),
|
||||
Chorus("Chorus"),
|
||||
EasyListening("Easy Listening"),
|
||||
Acoustic("Acoustic"),
|
||||
Humour("Humour"),
|
||||
Speech("Speech"),
|
||||
Chanson("Chanson"),
|
||||
Opera("Opera"),
|
||||
ChamberMusic("Chamber Music"),
|
||||
Sonata("Sonata"),
|
||||
Symphony("Symphony"),
|
||||
BootyBass("Booty Bass"),
|
||||
Primus("Primus"),
|
||||
PornGroove("Porn Groove"),
|
||||
Satire("Satire"),
|
||||
SlowJam("Slow Jam"),
|
||||
Club("Club"),
|
||||
Tango("Tango"),
|
||||
Samba("Samba"),
|
||||
Folklore("Folklore"),
|
||||
Ballad("Ballad"),
|
||||
PowerBallad("Power Ballad"),
|
||||
RhytmicSoul("Rhythmic Soul"),
|
||||
Freestyle("Freestyle"),
|
||||
Duet("Duet"),
|
||||
PunkRock("Punk Rock"),
|
||||
DrumSolo("Drum Solo"),
|
||||
ACapella("A capella"),
|
||||
EuroHouse("Euro-House"),
|
||||
DanceHall("Dance Hall");
|
||||
|
||||
public static ID3v1Genre getGenre(int id) {
|
||||
ID3v1Genre[] values = values();
|
||||
return id >= 0 && id < values.length ? values[id] : null;
|
||||
}
|
||||
|
||||
private final String description;
|
||||
|
||||
ID3v1Genre(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return ordinal();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ID3v1Info extends AudioInfo {
|
||||
public static boolean isID3v1StartPosition(InputStream input) throws IOException {
|
||||
input.mark(3);
|
||||
try {
|
||||
return input.read() == 'T' && input.read() == 'A' && input.read() == 'G';
|
||||
} finally {
|
||||
input.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public ID3v1Info(InputStream input) throws IOException {
|
||||
if (isID3v1StartPosition(input)) {
|
||||
brand = "ID3";
|
||||
version = "1.0";
|
||||
byte[] bytes = readBytes(input, 128);
|
||||
title = extractString(bytes, 3, 30);
|
||||
artist = extractString(bytes, 33, 30);
|
||||
album = extractString(bytes, 63, 30);
|
||||
try {
|
||||
year = Short.parseShort(extractString(bytes, 93, 4));
|
||||
} catch (NumberFormatException e) {
|
||||
year = 0;
|
||||
}
|
||||
comment = extractString(bytes, 97, 30);
|
||||
ID3v1Genre id3v1Genre = ID3v1Genre.getGenre(bytes[127]);
|
||||
if (id3v1Genre != null) {
|
||||
genre = id3v1Genre.getDescription();
|
||||
}
|
||||
|
||||
/*
|
||||
* ID3v1.1
|
||||
*/
|
||||
if (bytes[125] == 0 && bytes[126] != 0) {
|
||||
version = "1.1";
|
||||
track = (short) (bytes[126] & 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] readBytes(InputStream input, int len) throws IOException {
|
||||
int total = 0;
|
||||
byte[] bytes = new byte[len];
|
||||
while (total < len) {
|
||||
int current = input.read(bytes, total, len - total);
|
||||
if (current > 0) {
|
||||
total += current;
|
||||
} else {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
String extractString(byte[] bytes, int offset, int length) {
|
||||
try {
|
||||
String text = new String(bytes, offset, length, "ISO-8859-1");
|
||||
int zeroIndex = text.indexOf(0);
|
||||
return zeroIndex < 0 ? text : text.substring(0, zeroIndex);
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ID3v2DataInput {
|
||||
private final InputStream input;
|
||||
|
||||
public ID3v2DataInput(InputStream in) {
|
||||
this.input = in;
|
||||
}
|
||||
|
||||
public final void readFully(byte b[], int off, int len) throws IOException {
|
||||
int total = 0;
|
||||
while (total < len) {
|
||||
int current = input.read(b, off + total, len - total);
|
||||
if (current > 0) {
|
||||
total += current;
|
||||
} else {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] readFully(int len) throws IOException {
|
||||
byte[] bytes = new byte[len];
|
||||
readFully(bytes, 0, len);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public void skipFully(long len) throws IOException {
|
||||
long total = 0;
|
||||
while (total < len) {
|
||||
long current = input.skip(len - total);
|
||||
if (current > 0) {
|
||||
total += current;
|
||||
} else {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte readByte() throws IOException {
|
||||
int b = input.read();
|
||||
if (b < 0) {
|
||||
throw new EOFException();
|
||||
}
|
||||
return (byte) b;
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
return ((readByte() & 0xFF) << 24) | ((readByte() & 0xFF) << 16) | ((readByte() & 0xFF) << 8) | (readByte() & 0xFF);
|
||||
}
|
||||
|
||||
public int readSyncsafeInt() throws IOException {
|
||||
return ((readByte() & 0x7F) << 21) | ((readByte() & 0x7F) << 14) | ((readByte() & 0x7F) << 7) | (readByte() & 0x7F);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public enum ID3v2Encoding {
|
||||
ISO_8859_1(Charset.forName("ISO-8859-1"), 1),
|
||||
UTF_16(Charset.forName("UTF-16"), 2),
|
||||
UTF_16BE(Charset.forName("UTF-16BE"), 2),
|
||||
UTF_8(Charset.forName("UTF-8"), 1);
|
||||
|
||||
private final Charset charset;
|
||||
private final int zeroBytes;
|
||||
|
||||
ID3v2Encoding(Charset charset, int zeroBytes) {
|
||||
this.charset = charset;
|
||||
this.zeroBytes = zeroBytes;
|
||||
}
|
||||
|
||||
public Charset getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
public int getZeroBytes() {
|
||||
return zeroBytes;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
public class ID3v2Exception extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ID3v2Exception(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import org.telegram.android.audioinfo.util.RangeInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ID3v2FrameBody {
|
||||
|
||||
static final class Buffer {
|
||||
byte[] bytes;
|
||||
|
||||
Buffer(int initialLength) {
|
||||
bytes = new byte[initialLength];
|
||||
}
|
||||
|
||||
byte[] bytes(int minLength) {
|
||||
if (minLength > bytes.length) {
|
||||
int length = bytes.length * 2;
|
||||
while (minLength > length) {
|
||||
length *= 2;
|
||||
}
|
||||
bytes = new byte[length];
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static final ThreadLocal<Buffer> textBuffer = new ThreadLocal<Buffer>() {
|
||||
@Override
|
||||
protected Buffer initialValue() {
|
||||
return new Buffer(4096);
|
||||
}
|
||||
};
|
||||
|
||||
private final RangeInputStream input;
|
||||
private final ID3v2TagHeader tagHeader;
|
||||
private final ID3v2FrameHeader frameHeader;
|
||||
private final ID3v2DataInput data;
|
||||
|
||||
ID3v2FrameBody(InputStream delegate, long position, int dataLength, ID3v2TagHeader tagHeader, ID3v2FrameHeader frameHeader) throws IOException {
|
||||
this.input = new RangeInputStream(delegate, position, dataLength);
|
||||
this.data = new ID3v2DataInput(input);
|
||||
this.tagHeader = tagHeader;
|
||||
this.frameHeader = frameHeader;
|
||||
}
|
||||
|
||||
public ID3v2DataInput getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public long getPosition() {
|
||||
return input.getPosition();
|
||||
}
|
||||
|
||||
public long getRemainingLength() {
|
||||
return input.getRemainingLength();
|
||||
}
|
||||
|
||||
public ID3v2TagHeader getTagHeader() {
|
||||
return tagHeader;
|
||||
}
|
||||
|
||||
public ID3v2FrameHeader getFrameHeader() {
|
||||
return frameHeader;
|
||||
}
|
||||
|
||||
private String extractString(byte[] bytes, int offset, int length, ID3v2Encoding encoding, boolean searchZeros) {
|
||||
if (searchZeros) {
|
||||
int zeros = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
// UTF-16LE may have a zero byte as second byte of a 2-byte character -> skip first zero at odd index
|
||||
if (bytes[offset + i] == 0 && (encoding != ID3v2Encoding.UTF_16 || zeros != 0 || (offset + i) % 2 == 0)) {
|
||||
if (++zeros == encoding.getZeroBytes()) {
|
||||
length = i + 1 - encoding.getZeroBytes();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
zeros = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
String string = new String(bytes, offset, length, encoding.getCharset().name());
|
||||
if (string.length() > 0 && string.charAt(0) == '\uFEFF') { // remove BOM
|
||||
string = string.substring(1);
|
||||
}
|
||||
return string;
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public String readZeroTerminatedString(int maxLength, ID3v2Encoding encoding) throws IOException, ID3v2Exception {
|
||||
int zeros = 0;
|
||||
int length = Math.min(maxLength, (int) getRemainingLength());
|
||||
byte[] bytes = textBuffer.get().bytes(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
// UTF-16LE may have a zero byte as second byte of a 2-byte character -> skip first zero at odd index
|
||||
if ((bytes[i] = data.readByte()) == 0 && (encoding != ID3v2Encoding.UTF_16 || zeros != 0 || i % 2 == 0)) {
|
||||
if (++zeros == encoding.getZeroBytes()) {
|
||||
return extractString(bytes, 0, i + 1 - encoding.getZeroBytes(), encoding, false);
|
||||
}
|
||||
} else {
|
||||
zeros = 0;
|
||||
}
|
||||
}
|
||||
throw new ID3v2Exception("Could not read zero-termiated string");
|
||||
}
|
||||
|
||||
public String readFixedLengthString(int length, ID3v2Encoding encoding) throws IOException, ID3v2Exception {
|
||||
if (length > getRemainingLength()) {
|
||||
throw new ID3v2Exception("Could not read fixed-length string of length: " + length);
|
||||
}
|
||||
byte[] bytes = textBuffer.get().bytes(length);
|
||||
data.readFully(bytes, 0, length);
|
||||
return extractString(bytes, 0, length, encoding, true);
|
||||
}
|
||||
|
||||
public ID3v2Encoding readEncoding() throws IOException, ID3v2Exception {
|
||||
byte value = data.readByte();
|
||||
switch (value) {
|
||||
case 0:
|
||||
return ID3v2Encoding.ISO_8859_1;
|
||||
case 1:
|
||||
return ID3v2Encoding.UTF_16;
|
||||
case 2:
|
||||
return ID3v2Encoding.UTF_16BE;
|
||||
case 3:
|
||||
return ID3v2Encoding.UTF_8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new ID3v2Exception("Invalid encoding: " + value);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id3v2frame[pos=" + getPosition() + ", " + getRemainingLength() + " left]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ID3v2FrameHeader {
|
||||
private String frameId;
|
||||
private int headerSize;
|
||||
private int bodySize;
|
||||
private boolean unsynchronization;
|
||||
private boolean compression;
|
||||
private boolean encryption;
|
||||
private int dataLengthIndicator;
|
||||
|
||||
public ID3v2FrameHeader(ID3v2TagBody input) throws IOException, ID3v2Exception {
|
||||
long startPosition = input.getPosition();
|
||||
|
||||
ID3v2DataInput data = input.getData();
|
||||
|
||||
/*
|
||||
* Frame Id
|
||||
*/
|
||||
if (input.getTagHeader().getVersion() == 2) { // $xx xx xx (three characters)
|
||||
frameId = new String(data.readFully(3), "ISO-8859-1");
|
||||
} else { // $xx xx xx xx (four characters)
|
||||
frameId = new String(data.readFully(4), "ISO-8859-1");
|
||||
}
|
||||
|
||||
/*
|
||||
* Size
|
||||
*/
|
||||
if (input.getTagHeader().getVersion() == 2) { // $xx xx xx
|
||||
bodySize = ((data.readByte() & 0xFF) << 16) | ((data.readByte() & 0xFF) << 8) | (data.readByte() & 0xFF);
|
||||
} else if (input.getTagHeader().getVersion() == 3) { // $xx xx xx xx
|
||||
bodySize = data.readInt();
|
||||
} else { // 4 * %0xxxxxxx (sync-save integer)
|
||||
bodySize = data.readSyncsafeInt();
|
||||
}
|
||||
|
||||
/*
|
||||
* Flags
|
||||
*/
|
||||
if (input.getTagHeader().getVersion() > 2) { // $xx xx
|
||||
data.readByte(); // status flags
|
||||
byte formatFlags = data.readByte();
|
||||
int compressionMask;
|
||||
int encryptionMask;
|
||||
int groupingIdentityMask;
|
||||
int unsynchronizationMask = 0x00;
|
||||
int dataLengthIndicatorMask = 0x00;
|
||||
if (input.getTagHeader().getVersion() == 3) { // %(compression)(encryption)(groupingIdentity)00000
|
||||
compressionMask = 0x80;
|
||||
encryptionMask = 0x40;
|
||||
groupingIdentityMask = 0x20;
|
||||
} else { // %0(groupingIdentity)00(compression)(encryption)(unsynchronization)(dataLengthIndicator)
|
||||
groupingIdentityMask = 0x40;
|
||||
compressionMask = 0x08;
|
||||
encryptionMask = 0x04;
|
||||
unsynchronizationMask = 0x02;
|
||||
dataLengthIndicatorMask = 0x01;
|
||||
}
|
||||
compression = (formatFlags & compressionMask) != 0;
|
||||
unsynchronization = (formatFlags & unsynchronizationMask) != 0;
|
||||
encryption = (formatFlags & encryptionMask) != 0;
|
||||
|
||||
/*
|
||||
* Read flag attachments in the order of the flags (version dependent).
|
||||
*/
|
||||
if (input.getTagHeader().getVersion() == 3) {
|
||||
if (compression) {
|
||||
dataLengthIndicator = data.readInt();
|
||||
bodySize -= 4;
|
||||
}
|
||||
if (encryption) {
|
||||
data.readByte(); // just skip
|
||||
bodySize -= 1;
|
||||
}
|
||||
if ((formatFlags & groupingIdentityMask) != 0) {
|
||||
data.readByte(); // just skip
|
||||
bodySize -= 1;
|
||||
}
|
||||
} else {
|
||||
if ((formatFlags & groupingIdentityMask) != 0) {
|
||||
data.readByte(); // just skip
|
||||
bodySize -= 1;
|
||||
}
|
||||
if (encryption) {
|
||||
data.readByte(); // just skip
|
||||
bodySize -= 1;
|
||||
}
|
||||
if ((formatFlags & dataLengthIndicatorMask) != 0) {
|
||||
dataLengthIndicator = data.readSyncsafeInt();
|
||||
bodySize -= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
headerSize = (int) (input.getPosition() - startPosition);
|
||||
}
|
||||
|
||||
public String getFrameId() {
|
||||
return frameId;
|
||||
}
|
||||
|
||||
public int getHeaderSize() {
|
||||
return headerSize;
|
||||
}
|
||||
|
||||
public int getBodySize() {
|
||||
return bodySize;
|
||||
}
|
||||
|
||||
public boolean isCompression() {
|
||||
return compression;
|
||||
}
|
||||
|
||||
public boolean isEncryption() {
|
||||
return encryption;
|
||||
}
|
||||
|
||||
public boolean isUnsynchronization() {
|
||||
return unsynchronization;
|
||||
}
|
||||
|
||||
public int getDataLengthIndicator() {
|
||||
return dataLengthIndicator;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
for (int i = 0; i < frameId.length(); i++) {
|
||||
if ((frameId.charAt(i) < 'A' || frameId.charAt(i) > 'Z') && (frameId.charAt(i) < '0' || frameId.charAt(i) > '9')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return bodySize > 0;
|
||||
}
|
||||
|
||||
public boolean isPadding() {
|
||||
for (int i = 0; i < frameId.length(); i++) {
|
||||
if (frameId.charAt(0) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return bodySize == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[id=%s, bodysize=%d]", getClass().getSimpleName(), frameId, bodySize);
|
||||
}
|
||||
}
|
376
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/ID3v2Info.java
Executable file
376
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/ID3v2Info.java
Executable file
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ID3v2Info extends AudioInfo {
|
||||
static final Logger LOGGER = Logger.getLogger(ID3v2Info.class.getName());
|
||||
|
||||
static class AttachedPicture {
|
||||
static final byte TYPE_OTHER = 0x00;
|
||||
static final byte TYPE_COVER_FRONT = 0x03;
|
||||
|
||||
final byte type;
|
||||
final String description;
|
||||
final String imageType;
|
||||
final byte[] imageData;
|
||||
|
||||
public AttachedPicture(byte type, String description, String imageType, byte[] imageData) {
|
||||
this.type = type;
|
||||
this.description = description;
|
||||
this.imageType = imageType;
|
||||
this.imageData = imageData;
|
||||
}
|
||||
}
|
||||
|
||||
static class CommentOrUnsynchronizedLyrics {
|
||||
final String language;
|
||||
final String description;
|
||||
final String text;
|
||||
|
||||
public CommentOrUnsynchronizedLyrics(String language, String description, String text) {
|
||||
this.language = language;
|
||||
this.description = description;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isID3v2StartPosition(InputStream input) throws IOException {
|
||||
input.mark(3);
|
||||
try {
|
||||
return input.read() == 'I' && input.read() == 'D' && input.read() == '3';
|
||||
} finally {
|
||||
input.reset();
|
||||
}
|
||||
}
|
||||
|
||||
private final Level debugLevel;
|
||||
|
||||
private byte coverPictureType;
|
||||
|
||||
public ID3v2Info(InputStream input) throws IOException, ID3v2Exception {
|
||||
this(input, Level.FINEST);
|
||||
}
|
||||
|
||||
public ID3v2Info(InputStream input, Level debugLevel) throws IOException, ID3v2Exception {
|
||||
this.debugLevel = debugLevel;
|
||||
if (isID3v2StartPosition(input)) {
|
||||
ID3v2TagHeader tagHeader = new ID3v2TagHeader(input);
|
||||
brand = "ID3";
|
||||
version = String.format("2.%d.%d", tagHeader.getVersion(), tagHeader.getRevision());
|
||||
ID3v2TagBody tagBody = tagHeader.tagBody(input);
|
||||
try {
|
||||
while (tagBody.getRemainingLength() > 10) { // TODO > tag.minimumFrameSize()
|
||||
ID3v2FrameHeader frameHeader = new ID3v2FrameHeader(tagBody);
|
||||
if (frameHeader.isPadding()) { // we ran into padding
|
||||
break;
|
||||
}
|
||||
if (frameHeader.getBodySize() > tagBody.getRemainingLength()) { // something wrong...
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "ID3 frame claims to extend frames area");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (frameHeader.isValid() && !frameHeader.isEncryption()) {
|
||||
ID3v2FrameBody frameBody = tagBody.frameBody(frameHeader);
|
||||
try {
|
||||
parseFrame(frameBody);
|
||||
} catch (ID3v2Exception e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, String.format("ID3 exception occured in frame %s: %s", frameHeader.getFrameId(), e.getMessage()));
|
||||
}
|
||||
} finally {
|
||||
frameBody.getData().skipFully(frameBody.getRemainingLength());
|
||||
}
|
||||
} else {
|
||||
tagBody.getData().skipFully(frameHeader.getBodySize());
|
||||
}
|
||||
}
|
||||
} catch (ID3v2Exception e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "ID3 exception occured: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
tagBody.getData().skipFully(tagBody.getRemainingLength());
|
||||
if (tagHeader.getFooterSize() > 0) {
|
||||
input.skip(tagHeader.getFooterSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseFrame(ID3v2FrameBody frame) throws IOException, ID3v2Exception {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Parsing frame: " + frame.getFrameHeader().getFrameId());
|
||||
}
|
||||
switch (frame.getFrameHeader().getFrameId()) {
|
||||
case "PIC":
|
||||
case "APIC": // cover: prefer TYPE_COVER_FRONT, then TYPE_OTHER, then anything else
|
||||
if (cover == null || coverPictureType != AttachedPicture.TYPE_COVER_FRONT) {
|
||||
AttachedPicture picture = parseAttachedPictureFrame(frame);
|
||||
if (cover == null || picture.type == AttachedPicture.TYPE_COVER_FRONT || picture.type == AttachedPicture.TYPE_OTHER) {
|
||||
try {
|
||||
byte[] bytes = picture.imageData;
|
||||
BitmapFactory.Options opts = new BitmapFactory.Options();
|
||||
opts.inJustDecodeBounds = true;
|
||||
opts.inSampleSize = 1;
|
||||
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);
|
||||
if (opts.outWidth > 800 || opts.outHeight > 800) {
|
||||
int size = Math.max(opts.outWidth, opts.outHeight);
|
||||
while (size > 800) {
|
||||
opts.inSampleSize *= 2;
|
||||
size /= 2;
|
||||
}
|
||||
}
|
||||
opts.inJustDecodeBounds = false;
|
||||
cover = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts);
|
||||
if (cover != null) {
|
||||
float scale = Math.max(cover.getWidth(), cover.getHeight()) / 120.0f;
|
||||
if (scale > 0) {
|
||||
smallCover = Bitmap.createScaledBitmap(cover, (int) (cover.getWidth() / scale), (int) (cover.getHeight() / scale), true);
|
||||
} else {
|
||||
smallCover = cover;
|
||||
}
|
||||
if (smallCover == null) {
|
||||
smallCover = cover;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
coverPictureType = picture.type;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "COM":
|
||||
case "COMM":
|
||||
CommentOrUnsynchronizedLyrics comm = parseCommentOrUnsynchronizedLyricsFrame(frame);
|
||||
if (comment == null || comm.description == null || "".equals(comm.description)) { // prefer "default" comment (without description)
|
||||
comment = comm.text;
|
||||
}
|
||||
break;
|
||||
case "TAL":
|
||||
case "TALB":
|
||||
album = parseTextFrame(frame);
|
||||
break;
|
||||
case "TCP":
|
||||
case "TCMP":
|
||||
compilation = "1".equals(parseTextFrame(frame));
|
||||
break;
|
||||
case "TCM":
|
||||
case "TCOM":
|
||||
composer = parseTextFrame(frame);
|
||||
break;
|
||||
case "TCO":
|
||||
case "TCON":
|
||||
String tcon = parseTextFrame(frame);
|
||||
if (tcon.length() > 0) {
|
||||
genre = tcon;
|
||||
try {
|
||||
ID3v1Genre id3v1Genre = null;
|
||||
if (tcon.charAt(0) == '(') {
|
||||
int pos = tcon.indexOf(')');
|
||||
if (pos > 1) { // (123)
|
||||
id3v1Genre = ID3v1Genre.getGenre(Integer.parseInt(tcon.substring(1, pos)));
|
||||
if (id3v1Genre == null && tcon.length() > pos + 1) { // (789)Special
|
||||
genre = tcon.substring(pos + 1);
|
||||
}
|
||||
}
|
||||
} else { // 123
|
||||
id3v1Genre = ID3v1Genre.getGenre(Integer.parseInt(tcon));
|
||||
}
|
||||
if (id3v1Genre != null) {
|
||||
genre = id3v1Genre.getDescription();
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "TCR":
|
||||
case "TCOP":
|
||||
copyright = parseTextFrame(frame);
|
||||
break;
|
||||
case "TDRC": // v2.4, replaces TYER
|
||||
String tdrc = parseTextFrame(frame);
|
||||
if (tdrc.length() >= 4) {
|
||||
try {
|
||||
year = Short.valueOf(tdrc.substring(0, 4));
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse year from: " + tdrc);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "TLE":
|
||||
case "TLEN":
|
||||
String tlen = parseTextFrame(frame);
|
||||
try {
|
||||
duration = Long.valueOf(tlen);
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse track duration: " + tlen);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "TP1":
|
||||
case "TPE1":
|
||||
artist = parseTextFrame(frame);
|
||||
break;
|
||||
case "TP2":
|
||||
case "TPE2":
|
||||
albumArtist = parseTextFrame(frame);
|
||||
break;
|
||||
case "TPA":
|
||||
case "TPOS":
|
||||
String tpos = parseTextFrame(frame);
|
||||
if (tpos.length() > 0) {
|
||||
int index = tpos.indexOf('/');
|
||||
if (index < 0) {
|
||||
try {
|
||||
disc = Short.valueOf(tpos);
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse disc number: " + tpos);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
disc = Short.valueOf(tpos.substring(0, index));
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse disc number: " + tpos);
|
||||
}
|
||||
}
|
||||
try {
|
||||
discs = Short.valueOf(tpos.substring(index + 1));
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse number of discs: " + tpos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "TRK":
|
||||
case "TRCK":
|
||||
String trck = parseTextFrame(frame);
|
||||
if (trck.length() > 0) {
|
||||
int index = trck.indexOf('/');
|
||||
if (index < 0) {
|
||||
try {
|
||||
track = Short.valueOf(trck);
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse track number: " + trck);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
track = Short.valueOf(trck.substring(0, index));
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse track number: " + trck);
|
||||
}
|
||||
}
|
||||
try {
|
||||
tracks = Short.valueOf(trck.substring(index + 1));
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse number of tracks: " + trck);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "TT1":
|
||||
case "TIT1":
|
||||
grouping = parseTextFrame(frame);
|
||||
break;
|
||||
case "TT2":
|
||||
case "TIT2":
|
||||
title = parseTextFrame(frame);
|
||||
break;
|
||||
case "TYE":
|
||||
case "TYER":
|
||||
String tyer = parseTextFrame(frame);
|
||||
if (tyer.length() > 0) {
|
||||
try {
|
||||
year = Short.valueOf(tyer);
|
||||
} catch (NumberFormatException e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not parse year: " + tyer);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "ULT":
|
||||
case "USLT":
|
||||
if (lyrics == null) {
|
||||
lyrics = parseCommentOrUnsynchronizedLyricsFrame(frame).text;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String parseTextFrame(ID3v2FrameBody frame) throws IOException, ID3v2Exception {
|
||||
ID3v2Encoding encoding = frame.readEncoding();
|
||||
return frame.readFixedLengthString((int) frame.getRemainingLength(), encoding);
|
||||
}
|
||||
|
||||
CommentOrUnsynchronizedLyrics parseCommentOrUnsynchronizedLyricsFrame(ID3v2FrameBody data) throws IOException, ID3v2Exception {
|
||||
ID3v2Encoding encoding = data.readEncoding();
|
||||
String language = data.readFixedLengthString(3, ID3v2Encoding.ISO_8859_1);
|
||||
String description = data.readZeroTerminatedString(200, encoding);
|
||||
String text = data.readFixedLengthString((int) data.getRemainingLength(), encoding);
|
||||
return new CommentOrUnsynchronizedLyrics(language, description, text);
|
||||
}
|
||||
|
||||
AttachedPicture parseAttachedPictureFrame(ID3v2FrameBody data) throws IOException, ID3v2Exception {
|
||||
ID3v2Encoding encoding = data.readEncoding();
|
||||
String imageType;
|
||||
if (data.getTagHeader().getVersion() == 2) { // file type, e.g. "JPG"
|
||||
String fileType = data.readFixedLengthString(3, ID3v2Encoding.ISO_8859_1);
|
||||
switch (fileType.toUpperCase()) {
|
||||
case "PNG":
|
||||
imageType = "image/png";
|
||||
break;
|
||||
case "JPG":
|
||||
imageType = "image/jpeg";
|
||||
break;
|
||||
default:
|
||||
imageType = "image/unknown";
|
||||
}
|
||||
} else { // mime type, e.g. "image/jpeg"
|
||||
imageType = data.readZeroTerminatedString(20, ID3v2Encoding.ISO_8859_1);
|
||||
}
|
||||
byte pictureType = data.getData().readByte();
|
||||
String description = data.readZeroTerminatedString(200, encoding);
|
||||
byte[] imageData = data.getData().readFully((int) data.getRemainingLength());
|
||||
return new AttachedPicture(pictureType, description, imageType, imageData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import org.telegram.android.audioinfo.util.RangeInputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public class ID3v2TagBody {
|
||||
private final RangeInputStream input;
|
||||
private final ID3v2TagHeader tagHeader;
|
||||
private final ID3v2DataInput data;
|
||||
|
||||
ID3v2TagBody(InputStream delegate, long position, int length, ID3v2TagHeader tagHeader) throws IOException {
|
||||
this.input = new RangeInputStream(delegate, position, length);
|
||||
this.data = new ID3v2DataInput(input);
|
||||
this.tagHeader = tagHeader;
|
||||
}
|
||||
|
||||
public ID3v2DataInput getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public long getPosition() {
|
||||
return input.getPosition();
|
||||
}
|
||||
|
||||
public long getRemainingLength() {
|
||||
return input.getRemainingLength();
|
||||
}
|
||||
|
||||
public ID3v2TagHeader getTagHeader() {
|
||||
return tagHeader;
|
||||
}
|
||||
|
||||
public ID3v2FrameBody frameBody(ID3v2FrameHeader frameHeader) throws IOException, ID3v2Exception {
|
||||
int dataLength = frameHeader.getBodySize();
|
||||
InputStream input = this.input;
|
||||
if (frameHeader.isUnsynchronization()) {
|
||||
byte[] bytes = data.readFully(frameHeader.getBodySize());
|
||||
boolean ff = false;
|
||||
int len = 0;
|
||||
for (byte b : bytes) {
|
||||
if (!ff || b != 0) {
|
||||
bytes[len++] = b;
|
||||
}
|
||||
ff = (b == 0xFF);
|
||||
}
|
||||
dataLength = len;
|
||||
input = new ByteArrayInputStream(bytes, 0, len);
|
||||
}
|
||||
if (frameHeader.isEncryption()) {
|
||||
throw new ID3v2Exception("Frame encryption is not supported");
|
||||
}
|
||||
if (frameHeader.isCompression()) {
|
||||
dataLength = frameHeader.getDataLengthIndicator();
|
||||
input = new InflaterInputStream(input);
|
||||
}
|
||||
return new ID3v2FrameBody(input, frameHeader.getHeaderSize(), dataLength, tagHeader, frameHeader);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id3v2tag[pos=" + getPosition() + ", " + getRemainingLength() + " left]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import org.telegram.android.audioinfo.util.PositionInputStream;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class ID3v2TagHeader {
|
||||
private int version = 0;
|
||||
private int revision = 0;
|
||||
private int headerSize = 0; // size of header, including extended header (with attachments)
|
||||
private int totalTagSize = 0; // everything, i.e. inluding tag header, extended header, footer & padding
|
||||
private int paddingSize = 0; // size of zero padding after frames
|
||||
private int footerSize = 0; // size of footer (version 4 only)
|
||||
private boolean unsynchronization;
|
||||
private boolean compression;
|
||||
|
||||
public ID3v2TagHeader(InputStream input) throws IOException, ID3v2Exception {
|
||||
this(new PositionInputStream(input));
|
||||
}
|
||||
|
||||
ID3v2TagHeader(PositionInputStream input) throws IOException, ID3v2Exception {
|
||||
long startPosition = input.getPosition();
|
||||
|
||||
ID3v2DataInput data = new ID3v2DataInput(input);
|
||||
|
||||
/*
|
||||
* Identifier: "ID3"
|
||||
*/
|
||||
String id = new String(data.readFully(3), "ISO-8859-1");
|
||||
if (!"ID3".equals(id)) {
|
||||
throw new ID3v2Exception("Invalid ID3 identifier: " + id);
|
||||
}
|
||||
|
||||
/*
|
||||
* Version: $02, $03 or $04
|
||||
*/
|
||||
version = data.readByte();
|
||||
if (version != 2 && version != 3 && version != 4) {
|
||||
throw new ID3v2Exception("Unsupported ID3v2 version: " + version);
|
||||
}
|
||||
|
||||
/*
|
||||
* Revision: $xx
|
||||
*/
|
||||
revision = data.readByte();
|
||||
|
||||
/*
|
||||
* Flags (evaluated below)
|
||||
*/
|
||||
byte flags = data.readByte();
|
||||
|
||||
/*
|
||||
* Size: 4 * %0xxxxxxx (sync-save integer)
|
||||
*/
|
||||
totalTagSize = 10 + data.readSyncsafeInt();
|
||||
|
||||
/*
|
||||
* Evaluate flags
|
||||
*/
|
||||
if (version == 2) { // %(unsynchronisation)(compression)000000
|
||||
unsynchronization = (flags & 0x80) != 0;
|
||||
compression = (flags & 0x40) != 0;
|
||||
} else { // %(unsynchronisation)(extendedHeader)(experimentalIndicator)(version == 3 ? 0 : footerPresent)0000
|
||||
unsynchronization = (flags & 0x80) != 0;
|
||||
|
||||
/*
|
||||
* Extended Header
|
||||
*/
|
||||
if ((flags & 0x40) != 0) {
|
||||
if (version == 3) {
|
||||
/*
|
||||
* Extended header size: $xx xx xx xx (6 or 10 if CRC data present)
|
||||
* In version 3, the size excludes itself.
|
||||
*/
|
||||
int extendedHeaderSize = data.readInt();
|
||||
|
||||
/*
|
||||
* Extended Flags: $xx xx (skip)
|
||||
*/
|
||||
data.readByte(); // flags...
|
||||
data.readByte(); // more flags...
|
||||
|
||||
/*
|
||||
* Size of padding: $xx xx xx xx
|
||||
*/
|
||||
paddingSize = data.readInt();
|
||||
|
||||
/*
|
||||
* consume the rest
|
||||
*/
|
||||
data.skipFully(extendedHeaderSize - 6);
|
||||
} else {
|
||||
/*
|
||||
* Extended header size: 4 * %0xxxxxxx (sync-save integer)
|
||||
* In version 4, the size includes itself.
|
||||
*/
|
||||
int extendedHeaderSize = data.readSyncsafeInt();
|
||||
|
||||
/*
|
||||
* consume the rest
|
||||
*/
|
||||
data.skipFully(extendedHeaderSize - 4);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Footer Present
|
||||
*/
|
||||
if (version >= 4 && (flags & 0x10) != 0) { // footer present
|
||||
footerSize = 10;
|
||||
totalTagSize += 10;
|
||||
}
|
||||
}
|
||||
|
||||
headerSize = (int) (input.getPosition() - startPosition);
|
||||
}
|
||||
|
||||
public ID3v2TagBody tagBody(InputStream input) throws IOException, ID3v2Exception {
|
||||
if (compression) {
|
||||
throw new ID3v2Exception("Tag compression is not supported");
|
||||
}
|
||||
if (version < 4 && unsynchronization) {
|
||||
byte[] bytes = new ID3v2DataInput(input).readFully(totalTagSize - headerSize);
|
||||
boolean ff = false;
|
||||
int len = 0;
|
||||
for (byte b : bytes) {
|
||||
if (!ff || b != 0) {
|
||||
bytes[len++] = b;
|
||||
}
|
||||
ff = (b == 0xFF);
|
||||
}
|
||||
return new ID3v2TagBody(new ByteArrayInputStream(bytes, 0, len), headerSize, len, this);
|
||||
} else {
|
||||
return new ID3v2TagBody(input, headerSize, totalTagSize - headerSize - footerSize, this);
|
||||
}
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public int getTotalTagSize() {
|
||||
return totalTagSize;
|
||||
}
|
||||
|
||||
public boolean isUnsynchronization() {
|
||||
return unsynchronization;
|
||||
}
|
||||
|
||||
public boolean isCompression() {
|
||||
return compression;
|
||||
}
|
||||
|
||||
public int getHeaderSize() {
|
||||
return headerSize;
|
||||
}
|
||||
|
||||
public int getFooterSize() {
|
||||
return footerSize;
|
||||
}
|
||||
|
||||
public int getPaddingSize() {
|
||||
return paddingSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[version=%s, totalTagSize=%d]", getClass().getSimpleName(), version, totalTagSize);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
public class MP3Exception extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MP3Exception(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
315
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/MP3Frame.java
Executable file
315
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/MP3Frame.java
Executable file
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
|
||||
public class MP3Frame {
|
||||
static final class CRC16 {
|
||||
private short crc = (short) 0xFFFF;
|
||||
|
||||
public void update(int value, int length) {
|
||||
int mask = 1 << (length - 1);
|
||||
do {
|
||||
if (((crc & 0x8000) == 0) ^ ((value & mask) == 0)) {
|
||||
crc <<= 1;
|
||||
crc ^= 0x8005;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
} while ((mask >>>= 1) != 0);
|
||||
}
|
||||
|
||||
public void update(byte value) {
|
||||
update(value, 8);
|
||||
}
|
||||
|
||||
public short getValue() {
|
||||
return crc;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
crc = (short) 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Header {
|
||||
private static final int MPEG_LAYER_RESERVED = 0;
|
||||
private static final int MPEG_VERSION_RESERVED = 1;
|
||||
private static final int MPEG_BITRATE_FREE = 0;
|
||||
private static final int MPEG_BITRATE_RESERVED = 15;
|
||||
private static final int MPEG_FRQUENCY_RESERVED = 3;
|
||||
|
||||
// [frequency][version]
|
||||
private static final int[][] FREQUENCIES = new int[][] {
|
||||
// 2.5 reserved 2 1
|
||||
{ 11025, -1, 22050, 44100 },
|
||||
{ 12000, -1, 24000, 48000 },
|
||||
{ 8000, -1, 16000, 32000 },
|
||||
{ -1, -1, -1, -1 } // reserved
|
||||
};
|
||||
|
||||
// [bitrate][version,layer]
|
||||
private static final int[][] BITRATES = new int[][] {
|
||||
{ 0, 0, 0, 0, 0 }, // free
|
||||
{ 32000, 32000, 32000, 32000, 8000 },
|
||||
{ 64000, 48000, 40000, 48000, 16000 },
|
||||
{ 96000, 56000, 48000, 56000, 24000 },
|
||||
{ 128000, 64000, 56000, 64000, 32000 },
|
||||
{ 160000, 80000, 64000, 80000, 40000 },
|
||||
{ 192000, 96000, 80000, 96000, 48000 },
|
||||
{ 224000, 112000, 96000, 112000, 56000 },
|
||||
{ 256000, 128000, 112000, 128000, 64000 },
|
||||
{ 288000, 160000, 128000, 144000, 80000 },
|
||||
{ 320000, 192000, 160000, 160000, 96000 },
|
||||
{ 352000, 224000, 192000, 176000, 112000 },
|
||||
{ 384000, 256000, 224000, 192000, 128000 },
|
||||
{ 416000, 320000, 256000, 224000, 144000 },
|
||||
{ 448000, 384000, 320000, 256000, 160000 },
|
||||
{ -1, -1, -1, -1, -1 } // reserved
|
||||
};
|
||||
|
||||
// [version][layer]
|
||||
private static final int[][] BITRATES_COLUMN = new int[][] {
|
||||
// reserved III II I
|
||||
{ -1, 4, 4, 3 }, // 2.5
|
||||
{ -1, -1, -1, -1 }, // reserved
|
||||
{ -1, 4, 4, 3 }, // 2
|
||||
{ -1, 2, 1, 0 } // 1
|
||||
};
|
||||
|
||||
// [version][layer]
|
||||
private static final int[][] SIZE_COEFFICIENTS = new int[][] {
|
||||
// reserved III II I
|
||||
{ -1, 72, 144, 12 }, // 2.5
|
||||
{ -1, -1, -1, -1 }, // reserved
|
||||
{ -1, 72, 144, 12 }, // 2
|
||||
{ -1, 144, 144, 12 } // 1
|
||||
};
|
||||
|
||||
// [layer]
|
||||
private static final int[] SLOT_SIZES = new int[] {
|
||||
// reserved III II I
|
||||
-1, 1, 1, 4
|
||||
};
|
||||
|
||||
// [channelMode][version]
|
||||
private static final int[][] SIDE_INFO_SIZES = new int[][] {
|
||||
// 2.5 reserved 2 1
|
||||
{ 17, -1, 17, 32 }, // stereo
|
||||
{ 17, -1, 17, 32 }, // joint stereo
|
||||
{ 17, -1, 17, 32 }, // dual channel
|
||||
{ 9, -1, 9, 17 }, // mono
|
||||
};
|
||||
|
||||
public static final int MPEG_LAYER_1 = 3;
|
||||
public static final int MPEG_LAYER_2 = 2;
|
||||
public static final int MPEG_LAYER_3 = 1;
|
||||
|
||||
public static final int MPEG_VERSION_1 = 3;
|
||||
public static final int MPEG_VERSION_2 = 2;
|
||||
public static final int MPEG_VERSION_2_5 = 0;
|
||||
|
||||
public static final int MPEG_CHANNEL_MODE_MONO = 3;
|
||||
public static final int MPEG_PROTECTION_CRC = 0;
|
||||
|
||||
private final int version;
|
||||
private final int layer;
|
||||
private final int frequency;
|
||||
private final int bitrate;
|
||||
private final int channelMode;
|
||||
private final int padding;
|
||||
private final int protection;
|
||||
|
||||
public Header(int b1, int b2, int b3) throws MP3Exception {
|
||||
version = b1 >> 3 & 0x3;
|
||||
if (version == MPEG_VERSION_RESERVED) {
|
||||
throw new MP3Exception("Reserved version");
|
||||
}
|
||||
layer = b1 >> 1 & 0x3;
|
||||
if (layer == MPEG_LAYER_RESERVED) {
|
||||
throw new MP3Exception("Reserved layer");
|
||||
}
|
||||
bitrate = b2 >> 4 & 0xF;
|
||||
if (bitrate == MPEG_BITRATE_RESERVED) {
|
||||
throw new MP3Exception("Reserved bitrate");
|
||||
}
|
||||
if (bitrate == MPEG_BITRATE_FREE) {
|
||||
throw new MP3Exception("Free bitrate");
|
||||
}
|
||||
frequency = b2 >> 2 & 0x3;
|
||||
if (frequency == MPEG_FRQUENCY_RESERVED) {
|
||||
throw new MP3Exception("Reserved frequency");
|
||||
}
|
||||
channelMode = b3 >> 6 & 0x3;
|
||||
padding = b2 >> 1 & 0x1;
|
||||
protection = b1 & 0x1;
|
||||
|
||||
int minFrameSize = 4;
|
||||
if (protection == MPEG_PROTECTION_CRC) {
|
||||
minFrameSize += 2;
|
||||
}
|
||||
if (layer == MPEG_LAYER_3) {
|
||||
minFrameSize += getSideInfoSize();
|
||||
}
|
||||
if (getFrameSize() < minFrameSize) {
|
||||
throw new MP3Exception("Frame size must be at least " + minFrameSize);
|
||||
}
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public int getLayer() {
|
||||
return layer;
|
||||
}
|
||||
|
||||
public int getFrequency() {
|
||||
return FREQUENCIES[frequency][version];
|
||||
}
|
||||
|
||||
public int getChannelMode() {
|
||||
return channelMode;
|
||||
}
|
||||
|
||||
public int getProtection() {
|
||||
return protection;
|
||||
}
|
||||
|
||||
public int getSampleCount() {
|
||||
if (layer == MPEG_LAYER_1) {
|
||||
return 384;
|
||||
} else { // TODO correct?
|
||||
return 1152;
|
||||
}
|
||||
}
|
||||
|
||||
public int getFrameSize() {
|
||||
return ((SIZE_COEFFICIENTS[version][layer] * getBitrate() / getFrequency()) + padding) * SLOT_SIZES[layer];
|
||||
}
|
||||
|
||||
public int getBitrate() {
|
||||
return BITRATES[bitrate][BITRATES_COLUMN[version][layer]];
|
||||
}
|
||||
|
||||
public int getDuration() {
|
||||
return (int)getTotalDuration(getFrameSize());
|
||||
}
|
||||
|
||||
public long getTotalDuration(long totalSize) {
|
||||
long duration = 1000L * (getSampleCount() * totalSize) / (getFrameSize() * getFrequency());
|
||||
if (getVersion() != MPEG_VERSION_1 && getChannelMode() == MPEG_CHANNEL_MODE_MONO) {
|
||||
duration /= 2;
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
public boolean isCompatible(Header header) {
|
||||
return layer == header.layer && version == header.version && frequency == header.frequency && channelMode == header.channelMode;
|
||||
}
|
||||
|
||||
public int getSideInfoSize() {
|
||||
return SIDE_INFO_SIZES[channelMode][version];
|
||||
}
|
||||
|
||||
public int getXingOffset() {
|
||||
return 4 + getSideInfoSize();
|
||||
}
|
||||
|
||||
public int getVBRIOffset() {
|
||||
return 4 + 32;
|
||||
}
|
||||
}
|
||||
|
||||
private final byte[] bytes;
|
||||
private final Header header;
|
||||
|
||||
MP3Frame(Header header, byte[] bytes) {
|
||||
this.header = header;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
boolean isChecksumError() {
|
||||
if (header.getProtection() == Header.MPEG_PROTECTION_CRC) {
|
||||
if (header.getLayer() == Header.MPEG_LAYER_3) {
|
||||
CRC16 crc16 = new CRC16();
|
||||
crc16.update(bytes[2]);
|
||||
crc16.update(bytes[3]);
|
||||
// skip crc bytes 4+5
|
||||
int sideInfoSize = header.getSideInfoSize();
|
||||
for (int i = 0; i < sideInfoSize; i++) {
|
||||
crc16.update(bytes[6 + i]);
|
||||
}
|
||||
int crc = ((bytes[4] & 0xFF) << 8) | (bytes[5] & 0xFF);
|
||||
return crc != crc16.getValue();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return bytes.length;
|
||||
}
|
||||
|
||||
public Header getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
boolean isXingFrame() {
|
||||
int xingOffset = header.getXingOffset();
|
||||
if (bytes.length < xingOffset + 12) { // minimum Xing header size == 12
|
||||
return false;
|
||||
}
|
||||
if (xingOffset < 0 || bytes.length < xingOffset + 8) {
|
||||
return false;
|
||||
}
|
||||
if (bytes[xingOffset] == 'X' && bytes[xingOffset + 1] == 'i' && bytes[xingOffset + 2] == 'n' && bytes[xingOffset + 3] == 'g') {
|
||||
return true;
|
||||
}
|
||||
if (bytes[xingOffset] == 'I' && bytes[xingOffset + 1] == 'n' && bytes[xingOffset + 2] == 'f' && bytes[xingOffset + 3] == 'o') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isVBRIFrame() {
|
||||
int vbriOffset = header.getVBRIOffset();
|
||||
if (bytes.length < vbriOffset + 26) { // minimum VBRI header size == 26
|
||||
return false;
|
||||
}
|
||||
return bytes[vbriOffset] == 'V' && bytes[vbriOffset + 1] == 'B' && bytes[vbriOffset + 2] == 'R' && bytes[vbriOffset + 3] == 'I';
|
||||
}
|
||||
|
||||
public int getNumberOfFrames() {
|
||||
if (isXingFrame()) {
|
||||
int xingOffset = header.getXingOffset();
|
||||
byte flags = bytes[xingOffset + 7];
|
||||
if ((flags & 0x01) != 0) {
|
||||
return ((bytes[xingOffset + 8] & 0xFF) << 24) |
|
||||
((bytes[xingOffset + 9] & 0xFF) << 16) |
|
||||
((bytes[xingOffset + 10] & 0xFF) << 8) |
|
||||
( bytes[xingOffset + 11] & 0xFF);
|
||||
}
|
||||
} else if (isVBRIFrame()) {
|
||||
int vbriOffset = header.getVBRIOffset();
|
||||
return ((bytes[vbriOffset + 14] & 0xFF) << 24) |
|
||||
((bytes[vbriOffset + 15] & 0xFF) << 16) |
|
||||
((bytes[vbriOffset + 16] & 0xFF) << 8) |
|
||||
( bytes[vbriOffset + 17] & 0xFF);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
270
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/MP3Info.java
Executable file
270
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/MP3Info.java
Executable file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MP3Info extends AudioInfo {
|
||||
static final Logger LOGGER = Logger.getLogger(MP3Info.class.getName());
|
||||
|
||||
interface StopReadCondition {
|
||||
boolean stopRead(MP3Input data) throws IOException;
|
||||
}
|
||||
|
||||
public MP3Info(InputStream input, long fileLength) throws IOException, ID3v2Exception, MP3Exception {
|
||||
this(input, fileLength, Level.FINEST);
|
||||
}
|
||||
|
||||
public MP3Info(InputStream input, final long fileLength, Level debugLevel) throws IOException, ID3v2Exception, MP3Exception {
|
||||
brand = "MP3";
|
||||
version = "0";
|
||||
MP3Input data = new MP3Input(input);
|
||||
if (ID3v2Info.isID3v2StartPosition(data)) {
|
||||
ID3v2Info info = new ID3v2Info(data, debugLevel);
|
||||
album = info.getAlbum();
|
||||
albumArtist = info.getAlbumArtist();
|
||||
artist = info.getArtist();
|
||||
comment = info.getComment();
|
||||
cover = info.getCover();
|
||||
smallCover = info.getSmallCover();
|
||||
compilation = info.isCompilation();
|
||||
composer = info.getComposer();
|
||||
copyright = info.getCopyright();
|
||||
disc = info.getDisc();
|
||||
discs = info.getDiscs();
|
||||
duration = info.getDuration();
|
||||
genre = info.getGenre();
|
||||
grouping = info.getGrouping();
|
||||
lyrics = info.getLyrics();
|
||||
title = info.getTitle();
|
||||
track = info.getTrack();
|
||||
tracks = info.getTracks();
|
||||
year = info.getYear();
|
||||
}
|
||||
if (duration <= 0 || duration >= 3600000L) { // don't trust strange durations (e.g. old lame versions always write TLEN 97391548)
|
||||
try {
|
||||
duration = calculateDuration(data, fileLength, new StopReadCondition() {
|
||||
final long stopPosition = fileLength - 128;
|
||||
|
||||
@Override
|
||||
public boolean stopRead(MP3Input data) throws IOException {
|
||||
return (data.getPosition() == stopPosition) && ID3v1Info.isID3v1StartPosition(data);
|
||||
}
|
||||
});
|
||||
} catch (MP3Exception e) {
|
||||
if (LOGGER.isLoggable(debugLevel)) {
|
||||
LOGGER.log(debugLevel, "Could not determine MP3 duration", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (title == null || album == null || artist == null) {
|
||||
if (data.getPosition() <= fileLength - 128) { // position to last 128 bytes
|
||||
data.skipFully(fileLength - 128 - data.getPosition());
|
||||
if (ID3v1Info.isID3v1StartPosition(input)) {
|
||||
ID3v1Info info = new ID3v1Info(input);
|
||||
if (album == null) {
|
||||
album = info.getAlbum();
|
||||
}
|
||||
if (artist == null) {
|
||||
artist = info.getArtist();
|
||||
}
|
||||
if (comment == null) {
|
||||
comment = info.getComment();
|
||||
}
|
||||
if (genre == null) {
|
||||
genre = info.getGenre();
|
||||
}
|
||||
if (title == null) {
|
||||
title = info.getTitle();
|
||||
}
|
||||
if (track == 0) {
|
||||
track = info.getTrack();
|
||||
}
|
||||
if (year == 0) {
|
||||
year = info.getYear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MP3Frame readFirstFrame(MP3Input data, StopReadCondition stopCondition) throws IOException {
|
||||
int b0 = 0;
|
||||
int b1 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
while (b1 != -1) {
|
||||
if (b0 == 0xFF && (b1 & 0xE0) == 0xE0) { // first 11 bits should be 1
|
||||
data.mark(2); // set mark at b2
|
||||
int b2 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
if (b2 == -1) {
|
||||
break;
|
||||
}
|
||||
int b3 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
if (b3 == -1) {
|
||||
break;
|
||||
}
|
||||
MP3Frame.Header header = null;
|
||||
try {
|
||||
header = new MP3Frame.Header(b1, b2, b3);
|
||||
} catch (MP3Exception e) {
|
||||
// not a valid frame header
|
||||
}
|
||||
if (header != null) { // we have a candidate
|
||||
/*
|
||||
* The code gets a bit complex here, because we need to be able to reset() to b2 if
|
||||
* the check fails. Thus, we have to reset() to b2 before doing a call to mark().
|
||||
*/
|
||||
data.reset(); // reset input to b2
|
||||
data.mark(header.getFrameSize() + 2); // rest of frame (size - 2) + next header
|
||||
/*
|
||||
* read frame data
|
||||
*/
|
||||
byte[] frameBytes = new byte[header.getFrameSize()];
|
||||
frameBytes[0] = (byte) 0xFF;
|
||||
frameBytes[1] = (byte) b1;
|
||||
try {
|
||||
data.readFully(frameBytes, 2, frameBytes.length - 2); // may throw EOFException
|
||||
} catch (EOFException e) {
|
||||
break;
|
||||
}
|
||||
|
||||
MP3Frame frame = new MP3Frame(header, frameBytes);
|
||||
/*
|
||||
* read next header
|
||||
*/
|
||||
if (!frame.isChecksumError()) {
|
||||
int nextB0 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
int nextB1 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
if (nextB0 == -1 || nextB1 == -1) {
|
||||
return frame;
|
||||
}
|
||||
if (nextB0 == 0xFF && (nextB1 & 0xFE) == (b1 & 0xFE)) { // quick check: nextB1 must match b1's version & layer
|
||||
int nextB2 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
int nextB3 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
if (nextB2 == -1 || nextB3 == -1) {
|
||||
return frame;
|
||||
}
|
||||
try {
|
||||
if (new MP3Frame.Header(nextB1, nextB2, nextB3).isCompatible(header)) {
|
||||
data.reset(); // reset input to b2
|
||||
data.skipFully(frameBytes.length - 2); // skip to end of frame
|
||||
return frame;
|
||||
}
|
||||
} catch (MP3Exception e) {
|
||||
// not a valid frame header
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* seems to be a false sync...
|
||||
*/
|
||||
data.reset(); // reset input to b2
|
||||
}
|
||||
|
||||
/*
|
||||
* read next byte
|
||||
*/
|
||||
b0 = b1;
|
||||
b1 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
MP3Frame readNextFrame(MP3Input data, StopReadCondition stopCondition, MP3Frame previousFrame) throws IOException {
|
||||
MP3Frame.Header previousHeader = previousFrame.getHeader();
|
||||
data.mark(4);
|
||||
int b0 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
int b1 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
if (b0 == -1 || b1 == -1) {
|
||||
return null;
|
||||
}
|
||||
if (b0 == 0xFF && (b1 & 0xE0) == 0xE0) { // first 11 bits should be 1
|
||||
int b2 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
int b3 = stopCondition.stopRead(data) ? -1 : data.read();
|
||||
if (b2 == -1 || b3 == -1) {
|
||||
return null;
|
||||
}
|
||||
MP3Frame.Header nextHeader = null;
|
||||
try {
|
||||
nextHeader = new MP3Frame.Header(b1, b2, b3);
|
||||
} catch (MP3Exception e) {
|
||||
// not a valid frame header
|
||||
}
|
||||
if (nextHeader != null && nextHeader.isCompatible(previousHeader)) {
|
||||
byte[] frameBytes = new byte[nextHeader.getFrameSize()];
|
||||
frameBytes[0] = (byte) b0;
|
||||
frameBytes[1] = (byte) b1;
|
||||
frameBytes[2] = (byte) b2;
|
||||
frameBytes[3] = (byte) b3;
|
||||
try {
|
||||
data.readFully(frameBytes, 4, frameBytes.length - 4);
|
||||
} catch (EOFException e) {
|
||||
return null;
|
||||
}
|
||||
return new MP3Frame(nextHeader, frameBytes);
|
||||
}
|
||||
}
|
||||
data.reset();
|
||||
return null;
|
||||
}
|
||||
|
||||
long calculateDuration(MP3Input data, long totalLength, StopReadCondition stopCondition) throws IOException, MP3Exception {
|
||||
MP3Frame frame = readFirstFrame(data, stopCondition);
|
||||
if (frame != null) {
|
||||
// check for Xing header
|
||||
int numberOfFrames = frame.getNumberOfFrames();
|
||||
if (numberOfFrames > 0) { // from Xing/VBRI header
|
||||
return frame.getHeader().getTotalDuration(numberOfFrames * frame.getSize());
|
||||
} else { // scan file
|
||||
numberOfFrames = 1;
|
||||
|
||||
long firstFramePosition = data.getPosition() - frame.getSize();
|
||||
long frameSizeSum = frame.getSize();
|
||||
|
||||
int firstFrameBitrate = frame.getHeader().getBitrate();
|
||||
long bitrateSum = firstFrameBitrate;
|
||||
boolean vbr = false;
|
||||
int cbrThreshold = 10000 / frame.getHeader().getDuration(); // assume CBR after 10 seconds
|
||||
|
||||
while (true) {
|
||||
if (numberOfFrames == cbrThreshold && !vbr && totalLength > 0) {
|
||||
return frame.getHeader().getTotalDuration(totalLength - firstFramePosition);
|
||||
}
|
||||
if ((frame = readNextFrame(data, stopCondition, frame)) == null) {
|
||||
break;
|
||||
}
|
||||
int bitrate = frame.getHeader().getBitrate();
|
||||
if (bitrate != firstFrameBitrate) {
|
||||
vbr = true;
|
||||
}
|
||||
bitrateSum += bitrate;
|
||||
frameSizeSum += frame.getSize();
|
||||
numberOfFrames++;
|
||||
}
|
||||
return 1000L * frameSizeSum * numberOfFrames * 8 / bitrateSum;
|
||||
}
|
||||
} else {
|
||||
throw new MP3Exception("No audio frame");
|
||||
}
|
||||
}
|
||||
}
|
60
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/MP3Input.java
Executable file
60
TMessagesProj/src/main/java/org/telegram/android/audioinfo/mp3/MP3Input.java
Executable file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.mp3;
|
||||
|
||||
import org.telegram.android.audioinfo.util.PositionInputStream;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class MP3Input extends PositionInputStream {
|
||||
public MP3Input(InputStream delegate) throws IOException {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
public MP3Input(InputStream delegate, long position) {
|
||||
super(delegate, position);
|
||||
}
|
||||
|
||||
public final void readFully(byte b[], int off, int len) throws IOException {
|
||||
int total = 0;
|
||||
while (total < len) {
|
||||
int current = read(b, off + total, len - total);
|
||||
if (current > 0) {
|
||||
total += current;
|
||||
} else {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skipFully(long len) throws IOException {
|
||||
long total = 0;
|
||||
while (total < len) {
|
||||
long current = skip(len - total);
|
||||
if (current > 0) {
|
||||
total += current;
|
||||
} else {
|
||||
throw new EOFException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "mp3[pos=" + getPosition() + "]";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.util;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class PositionInputStream extends FilterInputStream {
|
||||
private long position;
|
||||
private long positionMark;
|
||||
|
||||
public PositionInputStream(InputStream delegate) {
|
||||
this(delegate, 0L);
|
||||
}
|
||||
|
||||
public PositionInputStream(InputStream delegate, long position) {
|
||||
super(delegate);
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
positionMark = position;
|
||||
super.mark(readlimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
super.reset();
|
||||
position = positionMark;
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
int data = super.read();
|
||||
if (data >= 0) {
|
||||
position++;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
long p = position;
|
||||
int read = super.read(b, off, len);
|
||||
if (read > 0) {
|
||||
position = p + read;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
long p = position;
|
||||
long skipped = super.skip(n);
|
||||
position = p + skipped;
|
||||
return skipped;
|
||||
}
|
||||
|
||||
public long getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2013-2014 Odysseus Software GmbH
|
||||
*
|
||||
* 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.android.audioinfo.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Input stream filter that keeps track of the current read position
|
||||
* and has a read length limit.
|
||||
*/
|
||||
public class RangeInputStream extends PositionInputStream {
|
||||
private final long endPosition;
|
||||
|
||||
public RangeInputStream(InputStream delegate, long position, long length) throws IOException {
|
||||
super(delegate, position);
|
||||
this.endPosition = position + length;
|
||||
}
|
||||
|
||||
public long getRemainingLength() {
|
||||
return endPosition - getPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (getPosition() == endPosition) {
|
||||
return -1;
|
||||
}
|
||||
return super.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (getPosition() + len > endPosition) {
|
||||
len = (int)(endPosition - getPosition());
|
||||
if (len == 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return super.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
if (getPosition() + n > endPosition) {
|
||||
n = (int)(endPosition - getPosition());
|
||||
}
|
||||
return super.skip(n);
|
||||
}
|
||||
}
|
|
@ -400,4 +400,38 @@ public class SharedMediaQuery {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void loadMusic(final long uid, final int max_id) {
|
||||
MessagesStorage.getInstance().getStorageQueue().postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final ArrayList<MessageObject> arrayList = new ArrayList<>();
|
||||
try {
|
||||
SQLiteCursor cursor = MessagesStorage.getInstance().getDatabase().queryFinalized(String.format(Locale.US, "SELECT data, mid FROM media_v2 WHERE uid = %d AND mid < %d AND type = %d ORDER BY date DESC, mid DESC LIMIT 1000", uid, max_id, MEDIA_FILE));
|
||||
|
||||
while (cursor.next()) {
|
||||
ByteBufferDesc data = MessagesStorage.getInstance().getBuffersStorage().getFreeBuffer(cursor.byteArrayLength(0));
|
||||
if (data != null && cursor.byteBufferValue(0, data.buffer) != 0) {
|
||||
TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false);
|
||||
if (MessageObject.isMusicMessage(message)) {
|
||||
message.id = cursor.intValue(1);
|
||||
message.dialog_id = uid;
|
||||
arrayList.add(0, new MessageObject(message, null, false));
|
||||
}
|
||||
}
|
||||
MessagesStorage.getInstance().getBuffersStorage().reuseFreeBuffer(data);
|
||||
}
|
||||
cursor.dispose();
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.musicDidLoaded, uid, arrayList);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.support.v7.util;
|
||||
package org.telegram.android.support.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A Sorted list implementation that can keep items in order and also notify for changes in the
|
||||
|
@ -418,6 +419,19 @@ public class SortedList<T> {
|
|||
mSize++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items from the SortedList.
|
||||
*/
|
||||
public void clear() {
|
||||
if (mSize == 0) {
|
||||
return;
|
||||
}
|
||||
final int prevSize = mSize;
|
||||
Arrays.fill(mData, 0, prevSize, null);
|
||||
mSize = 0;
|
||||
mCallback.onRemoved(0, prevSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* The class that controls the behavior of the {@link SortedList}.
|
||||
* <p>
|
||||
|
|
|
@ -19,6 +19,9 @@ package org.telegram.android.support.widget;
|
|||
import android.support.v4.util.Pools;
|
||||
import android.util.Log;
|
||||
|
||||
import org.telegram.android.support.widget.OpReorderer;
|
||||
import org.telegram.android.support.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.telegram.android.support.widget;
|
|||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||
|
||||
import org.telegram.android.support.widget.RecyclerView.ViewHolder;
|
||||
import android.view.View;
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import android.util.SparseIntArray;
|
|||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.telegram.android.support.widget.RecyclerView;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
|
|
|
@ -60,11 +60,14 @@ class LayoutState {
|
|||
int mLayoutDirection;
|
||||
|
||||
/**
|
||||
* Used if you want to pre-layout items that are not yet visible.
|
||||
* The difference with {@link #mAvailable} is that, when recycling, distance rendered for
|
||||
* {@link #mExtra} is not considered not to recycle visible children.
|
||||
* This is the target pixel closest to the start of the layout that we are trying to fill
|
||||
*/
|
||||
int mExtra = 0;
|
||||
int mStartLine = 0;
|
||||
|
||||
/**
|
||||
* This is the target pixel closest to the end of the layout that we are trying to fill
|
||||
*/
|
||||
int mEndLine = 0;
|
||||
|
||||
/**
|
||||
* @return true if there are more items in the data adapter
|
||||
|
@ -84,4 +87,16 @@ class LayoutState {
|
|||
mCurrentPosition += mItemDirection;
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LayoutState{" +
|
||||
"mAvailable=" + mAvailable +
|
||||
", mCurrentPosition=" + mCurrentPosition +
|
||||
", mItemDirection=" + mItemDirection +
|
||||
", mLayoutDirection=" + mLayoutDirection +
|
||||
", mStartLine=" + mStartLine +
|
||||
", mEndLine=" + mEndLine +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,12 @@ import android.os.Parcelable;
|
|||
import android.support.v4.view.ViewCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
||||
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import org.telegram.android.support.widget.OrientationHelper;
|
||||
import org.telegram.android.support.widget.RecyclerView;
|
||||
import org.telegram.android.support.widget.ScrollbarHelper;
|
||||
import org.telegram.android.support.widget.helper.ItemTouchHelper;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -33,10 +39,11 @@ import java.util.List;
|
|||
import static org.telegram.android.support.widget.RecyclerView.NO_POSITION;
|
||||
|
||||
/**
|
||||
* A {@link android.support.v7.widget.RecyclerView.LayoutManager} implementation which provides
|
||||
* A {@link RecyclerView.LayoutManager} implementation which provides
|
||||
* similar functionality to {@link android.widget.ListView}.
|
||||
*/
|
||||
public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
||||
public class LinearLayoutManager extends RecyclerView.LayoutManager implements
|
||||
ItemTouchHelper.ViewDropHandler {
|
||||
|
||||
private static final String TAG = "LinearLayoutManager";
|
||||
|
||||
|
@ -130,7 +137,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
* Re-used variable to keep anchor information on re-layout.
|
||||
* Anchor position and coordinate defines the reference point for LLM while doing a layout.
|
||||
* */
|
||||
final AnchorInfo mAnchorInfo;
|
||||
final AnchorInfo mAnchorInfo = new AnchorInfo();
|
||||
|
||||
/**
|
||||
* Creates a vertical LinearLayoutManager
|
||||
|
@ -148,7 +155,6 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
* @param reverseLayout When set to true, layouts from end to start.
|
||||
*/
|
||||
public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
|
||||
mAnchorInfo = new AnchorInfo();
|
||||
setOrientation(orientation);
|
||||
setReverseLayout(reverseLayout);
|
||||
}
|
||||
|
@ -342,8 +348,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
* laid out at the end of the UI, second item is laid out before it etc.
|
||||
*
|
||||
* For horizontal layouts, it depends on the layout direction.
|
||||
* When set to true, If {@link android.support.v7.widget.RecyclerView} is LTR, than it will
|
||||
* layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout
|
||||
* When set to true, If {@link RecyclerView} is LTR, than it will
|
||||
* layout from RTL, if {@link RecyclerView}} is RTL, it will layout
|
||||
* from LTR.
|
||||
*
|
||||
* If you are looking for the exact same behavior of
|
||||
|
@ -371,9 +377,13 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
final int firstChild = getPosition(getChildAt(0));
|
||||
final int viewPosition = position - firstChild;
|
||||
if (viewPosition >= 0 && viewPosition < childCount) {
|
||||
return getChildAt(viewPosition);
|
||||
final View child = getChildAt(viewPosition);
|
||||
if (getPosition(child) == position) {
|
||||
return child; // in pre-layout, this may not match
|
||||
}
|
||||
}
|
||||
return null;
|
||||
// fallback to traversal. This might be necessary in pre-layout.
|
||||
return super.findViewByPosition(position);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -796,6 +806,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
}
|
||||
// override layout from end values for consistency
|
||||
anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
|
||||
// if this changes, we should update prepareForDrop as well
|
||||
if (mShouldReverseLayout) {
|
||||
anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
|
||||
mPendingScrollPositionOffset;
|
||||
|
@ -941,7 +952,6 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
* <code>item[10]</code>'s bottom is 20 pixels above the RecyclerView's bottom.
|
||||
* <p>
|
||||
* Note that scroll position change will not be reflected until the next layout call.
|
||||
*
|
||||
* <p>
|
||||
* If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
|
||||
*
|
||||
|
@ -1175,11 +1185,10 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
/**
|
||||
* Recycles views that went out of bounds after scrolling towards the end of the layout.
|
||||
*
|
||||
* @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
|
||||
* @param recycler Recycler instance of {@link RecyclerView}
|
||||
* @param dt This can be used to add additional padding to the visible area. This is used
|
||||
* to
|
||||
* detect children that will go out of bounds after scrolling, without actually
|
||||
* moving them.
|
||||
* to detect children that will go out of bounds after scrolling, without
|
||||
* actually moving them.
|
||||
*/
|
||||
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
|
||||
if (dt < 0) {
|
||||
|
@ -1215,7 +1224,7 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
/**
|
||||
* Recycles views that went out of bounds after scrolling towards the start of the layout.
|
||||
*
|
||||
* @param recycler Recycler instance of {@link android.support.v7.widget.RecyclerView}
|
||||
* @param recycler Recycler instance of {@link RecyclerView}
|
||||
* @param dt This can be used to add additional padding to the visible area. This is used
|
||||
* to detect children that will go out of bounds after scrolling, without
|
||||
* actually moving them.
|
||||
|
@ -1257,8 +1266,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
* @param layoutState Current layout state. Right now, this object does not change but
|
||||
* we may consider moving it out of this view so passing around as a
|
||||
* parameter for now, rather than accessing {@link #mLayoutState}
|
||||
* @see #recycleViewsFromStart(android.support.v7.widget.RecyclerView.Recycler, int)
|
||||
* @see #recycleViewsFromEnd(android.support.v7.widget.RecyclerView.Recycler, int)
|
||||
* @see #recycleViewsFromStart(RecyclerView.Recycler, int)
|
||||
* @see #recycleViewsFromEnd(RecyclerView.Recycler, int)
|
||||
* @see android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection
|
||||
*/
|
||||
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
|
||||
|
@ -1788,6 +1797,40 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
|||
return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide This method should be called by ItemTouchHelper only.
|
||||
*/
|
||||
@Override
|
||||
public void prepareForDrop(View view, View target, int x, int y) {
|
||||
assertNotInLayoutOrScroll("Cannot drop a view during a scroll or layout calculation");
|
||||
ensureLayoutState();
|
||||
resolveShouldLayoutReverse();
|
||||
final int myPos = getPosition(view);
|
||||
final int targetPos = getPosition(target);
|
||||
final int dropDirection = myPos < targetPos ? LayoutState.ITEM_DIRECTION_TAIL :
|
||||
LayoutState.ITEM_DIRECTION_HEAD;
|
||||
if (mShouldReverseLayout) {
|
||||
if (dropDirection == LayoutState.ITEM_DIRECTION_TAIL) {
|
||||
scrollToPositionWithOffset(targetPos,
|
||||
mOrientationHelper.getEndAfterPadding() -
|
||||
(mOrientationHelper.getDecoratedStart(target) +
|
||||
mOrientationHelper.getDecoratedMeasurement(view)));
|
||||
} else {
|
||||
scrollToPositionWithOffset(targetPos,
|
||||
mOrientationHelper.getEndAfterPadding() -
|
||||
mOrientationHelper.getDecoratedEnd(target));
|
||||
}
|
||||
} else {
|
||||
if (dropDirection == LayoutState.ITEM_DIRECTION_HEAD) {
|
||||
scrollToPositionWithOffset(targetPos, mOrientationHelper.getDecoratedStart(target));
|
||||
} else {
|
||||
scrollToPositionWithOffset(targetPos,
|
||||
mOrientationHelper.getDecoratedEnd(target) -
|
||||
mOrientationHelper.getDecoratedMeasurement(view));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that keeps temporary state while {LayoutManager} is filling out the empty
|
||||
* space.
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
|
||||
package org.telegram.android.support.widget;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.telegram.android.support.widget.AdapterHelper.UpdateOp;
|
||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.ADD;
|
||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.MOVE;
|
||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.REMOVE;
|
||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.UPDATE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class OpReorderer {
|
||||
|
||||
final Callback mCallback;
|
||||
|
@ -58,7 +58,7 @@ class OpReorderer {
|
|||
}
|
||||
|
||||
void swapMoveRemove(List<UpdateOp> list, int movePos, UpdateOp moveOp,
|
||||
int removePos, UpdateOp removeOp) {
|
||||
int removePos, UpdateOp removeOp) {
|
||||
UpdateOp extraRm = null;
|
||||
// check if move is nulled out by remove
|
||||
boolean revertedMove = false;
|
||||
|
@ -83,7 +83,7 @@ class OpReorderer {
|
|||
removeOp.positionStart--;
|
||||
} else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
|
||||
// move is removed.
|
||||
removeOp.itemCount --;
|
||||
removeOp.itemCount--;
|
||||
moveOp.cmd = REMOVE;
|
||||
moveOp.itemCount = 1;
|
||||
if (removeOp.itemCount == 0) {
|
||||
|
@ -157,7 +157,7 @@ class OpReorderer {
|
|||
}
|
||||
|
||||
private void swapMoveAdd(List<UpdateOp> list, int move, UpdateOp moveOp, int add,
|
||||
UpdateOp addOp) {
|
||||
UpdateOp addOp) {
|
||||
int offset = 0;
|
||||
// going in reverse, first revert the effect of add
|
||||
if (moveOp.itemCount < addOp.positionStart) {
|
||||
|
@ -178,7 +178,7 @@ class OpReorderer {
|
|||
}
|
||||
|
||||
void swapMoveUpdate(List<UpdateOp> list, int move, UpdateOp moveOp, int update,
|
||||
UpdateOp updateOp) {
|
||||
UpdateOp updateOp) {
|
||||
UpdateOp extraUp1 = null;
|
||||
UpdateOp extraUp2 = null;
|
||||
// going in reverse, first revert the effect of add
|
||||
|
@ -228,7 +228,7 @@ class OpReorderer {
|
|||
return -1;
|
||||
}
|
||||
|
||||
static interface Callback {
|
||||
interface Callback {
|
||||
|
||||
UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount);
|
||||
|
||||
|
|
|
@ -456,5 +456,4 @@ class PositionMap<E> implements Cloneable {
|
|||
return ~lo; // value not present
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.telegram.android.support.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.Observable;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PointF;
|
||||
|
@ -30,6 +31,8 @@ import android.support.annotation.Nullable;
|
|||
import android.support.v4.util.ArrayMap;
|
||||
import android.support.v4.view.InputDeviceCompat;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.support.v4.view.NestedScrollingChild;
|
||||
import android.support.v4.view.NestedScrollingChildHelper;
|
||||
import android.support.v4.view.ScrollingView;
|
||||
import android.support.v4.view.VelocityTrackerCompat;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
|
@ -39,9 +42,6 @@ import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
|
|||
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
|
||||
import android.support.v4.widget.EdgeEffectCompat;
|
||||
import android.support.v4.widget.ScrollerCompat;
|
||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp;
|
||||
import static org.telegram.android.support.widget.AdapterHelper.Callback;
|
||||
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
@ -58,12 +58,15 @@ import android.view.accessibility.AccessibilityEvent;
|
|||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.telegram.android.support.widget.AdapterHelper.Callback;
|
||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp;
|
||||
|
||||
/**
|
||||
* A flexible view for providing a limited window into a large data set.
|
||||
*
|
||||
|
@ -128,8 +131,10 @@ import java.util.List;
|
|||
* <p>
|
||||
* When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
|
||||
* writing an {@link Adapter}, you probably want to use adapter positions.
|
||||
*
|
||||
* @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
|
||||
*/
|
||||
public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
|
||||
|
||||
private static final String TAG = "RecyclerView";
|
||||
|
||||
|
@ -221,6 +226,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
* >Try increasing your pool size and item cache size.
|
||||
*/
|
||||
private static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
|
||||
private static final Class<?>[] LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE =
|
||||
new Class[]{Context.class, AttributeSet.class, int.class, int.class};
|
||||
|
||||
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
|
||||
|
||||
|
@ -283,6 +290,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
private boolean mAdapterUpdateDuringMeasure;
|
||||
private final boolean mPostUpdatesOnAnimation;
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
|
||||
|
||||
/**
|
||||
* Set to true when an adapter data set changed notification is received.
|
||||
|
@ -354,11 +362,17 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
new ItemAnimatorRestoreListener();
|
||||
private boolean mPostedAnimatorRunner = false;
|
||||
private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
|
||||
private ChildDrawingOrderCallback mChildDrawingOrderCallback;
|
||||
|
||||
// simple array to keep min and max child position during a layout calculation
|
||||
// preserved not to create a new one in each layout pass
|
||||
private final int[] mMinMaxLayoutPositions = new int[2];
|
||||
|
||||
private final NestedScrollingChildHelper mScrollingChildHelper;
|
||||
private final int[] mScrollOffset = new int[2];
|
||||
private final int[] mScrollConsumed = new int[2];
|
||||
private final int[] mNestedOffsets = new int[2];
|
||||
|
||||
private Runnable mItemAnimatorRunner = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -408,6 +422,10 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
mAccessibilityManager = (AccessibilityManager) getContext()
|
||||
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
|
||||
// Create the layoutManager if specified.
|
||||
|
||||
mScrollingChildHelper = new NestedScrollingChildHelper(this);
|
||||
setNestedScrollingEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -428,6 +446,72 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
ViewCompat.setAccessibilityDelegate(this, mAccessibilityDelegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate and set a LayoutManager, if specified in the attributes.
|
||||
*/
|
||||
private void createLayoutManager(Context context, String className, AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
if (className != null) {
|
||||
className = className.trim();
|
||||
if (className.length() != 0) { // Can't use isEmpty since it was added in API 9.
|
||||
className = getFullClassName(context, className);
|
||||
try {
|
||||
ClassLoader classLoader;
|
||||
if (isInEditMode()) {
|
||||
// Stupid layoutlib cannot handle simple class loaders.
|
||||
classLoader = this.getClass().getClassLoader();
|
||||
} else {
|
||||
classLoader = context.getClassLoader();
|
||||
}
|
||||
Class<? extends LayoutManager> layoutManagerClass =
|
||||
classLoader.loadClass(className).asSubclass(LayoutManager.class);
|
||||
Constructor<? extends LayoutManager> constructor;
|
||||
Object[] constructorArgs = null;
|
||||
try {
|
||||
constructor = layoutManagerClass
|
||||
.getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);
|
||||
constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};
|
||||
} catch (NoSuchMethodException e) {
|
||||
try {
|
||||
constructor = layoutManagerClass.getConstructor();
|
||||
} catch (NoSuchMethodException e1) {
|
||||
e1.initCause(e);
|
||||
throw new IllegalStateException(attrs.getPositionDescription() +
|
||||
": Error creating LayoutManager " + className, e1);
|
||||
}
|
||||
}
|
||||
constructor.setAccessible(true);
|
||||
setLayoutManager(constructor.newInstance(constructorArgs));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalStateException(attrs.getPositionDescription()
|
||||
+ ": Unable to find LayoutManager " + className, e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new IllegalStateException(attrs.getPositionDescription()
|
||||
+ ": Could not instantiate the LayoutManager: " + className, e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new IllegalStateException(attrs.getPositionDescription()
|
||||
+ ": Could not instantiate the LayoutManager: " + className, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException(attrs.getPositionDescription()
|
||||
+ ": Cannot access non-public constructor " + className, e);
|
||||
} catch (ClassCastException e) {
|
||||
throw new IllegalStateException(attrs.getPositionDescription()
|
||||
+ ": Class is not a LayoutManager " + className, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getFullClassName(Context context, String className) {
|
||||
if (className.charAt(0) == '.') {
|
||||
return context.getPackageName() + className;
|
||||
}
|
||||
if (className.contains("")) {
|
||||
return className;
|
||||
}
|
||||
return RecyclerView.class.getPackage().getName() + '.' + className;
|
||||
}
|
||||
|
||||
private void initChildrenHelper() {
|
||||
mChildHelper = new ChildHelper(new ChildHelper.Callback() {
|
||||
@Override
|
||||
|
@ -769,6 +853,46 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a listener that will be notified whenever a child view is attached to or detached
|
||||
* from RecyclerView.
|
||||
*
|
||||
* <p>This listener will be called when a LayoutManager or the RecyclerView decides
|
||||
* that a child view is no longer needed. If an application associates expensive
|
||||
* or heavyweight data with item views, this may be a good place to release
|
||||
* or free those resources.</p>
|
||||
*
|
||||
* @param listener Listener to register
|
||||
*/
|
||||
public void addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
|
||||
if (mOnChildAttachStateListeners == null) {
|
||||
mOnChildAttachStateListeners = new ArrayList<OnChildAttachStateChangeListener>();
|
||||
}
|
||||
mOnChildAttachStateListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the provided listener from child attached state listeners list.
|
||||
*
|
||||
* @param listener Listener to unregister
|
||||
*/
|
||||
public void removeOnChildAttachStateChangeListener(OnChildAttachStateChangeListener listener) {
|
||||
if (mOnChildAttachStateListeners == null) {
|
||||
return;
|
||||
}
|
||||
mOnChildAttachStateListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all listeners that were added via
|
||||
* {@link #addOnChildAttachStateChangeListener(OnChildAttachStateChangeListener)}.
|
||||
*/
|
||||
public void clearOnChildAttachStateChangeListeners() {
|
||||
if (mOnChildAttachStateListeners != null) {
|
||||
mOnChildAttachStateListeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link LayoutManager} that this RecyclerView will use.
|
||||
*
|
||||
|
@ -993,7 +1117,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
public void addItemDecoration(ItemDecoration decor, int index) {
|
||||
if (mLayout != null) {
|
||||
mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or"
|
||||
+ " layout");
|
||||
+ "layout");
|
||||
}
|
||||
if (mItemDecorations.isEmpty()) {
|
||||
setWillNotDraw(false);
|
||||
|
@ -1035,7 +1159,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
public void removeItemDecoration(ItemDecoration decor) {
|
||||
if (mLayout != null) {
|
||||
mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll or"
|
||||
+ " layout");
|
||||
+ "layout");
|
||||
}
|
||||
mItemDecorations.remove(decor);
|
||||
if (mItemDecorations.isEmpty()) {
|
||||
|
@ -1045,6 +1169,26 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ChildDrawingOrderCallback} to be used for drawing children.
|
||||
* <p>
|
||||
* See {@link ViewGroup#getChildDrawingOrder(int, int)} for details. Calling this method will
|
||||
* always call {@link ViewGroup#setChildrenDrawingOrderEnabled(boolean)}. The parameter will be
|
||||
* true if childDrawingOrderCallback is not null, false otherwise.
|
||||
* <p>
|
||||
* Note that child drawing order may be overridden by View's elevation.
|
||||
*
|
||||
* @param childDrawingOrderCallback The ChildDrawingOrderCallback to be used by the drawing
|
||||
* system.
|
||||
*/
|
||||
public void setChildDrawingOrderCallback(ChildDrawingOrderCallback childDrawingOrderCallback) {
|
||||
if (childDrawingOrderCallback == mChildDrawingOrderCallback) {
|
||||
return;
|
||||
}
|
||||
mChildDrawingOrderCallback = childDrawingOrderCallback;
|
||||
setChildrenDrawingOrderEnabled(mChildDrawingOrderCallback != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a listener that will be notified of any changes in scroll state or position.
|
||||
*
|
||||
|
@ -1153,7 +1297,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
|
||||
final boolean canScrollVertical = mLayout.canScrollVertically();
|
||||
if (canScrollHorizontal || canScrollVertical) {
|
||||
scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, false, 0, 0);
|
||||
scrollByInternal(canScrollHorizontal ? x : 0, canScrollVertical ? y : 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1176,29 +1320,25 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
*
|
||||
* @param x The amount of horizontal scroll request
|
||||
* @param y The amount of vertical scroll request
|
||||
* @param fromMotionEvent If request is originated from a MotionEvent, this should be set to
|
||||
* true and motionX/motionY should be provided, false otherwise.
|
||||
* @param motionX The x coordinate of the MotionEvent which triggered this scroll. Unused if
|
||||
* fromMotionEvent is false.
|
||||
* @param motionY The y coordinate of the MotionEvent which triggered this scroll. Unused if
|
||||
* fromMotionEvent is false.
|
||||
* @param ev The originating MotionEvent, or null if not from a touch event.
|
||||
*
|
||||
* @return Whether any scroll was consumed in either direction.
|
||||
*/
|
||||
boolean scrollByInternal(int x, int y, boolean fromMotionEvent, int motionX, int motionY) {
|
||||
int overscrollX = 0, overscrollY = 0;
|
||||
int hresult = 0, vresult = 0;
|
||||
boolean scrollByInternal(int x, int y, MotionEvent ev) {
|
||||
int unconsumedX = 0, unconsumedY = 0;
|
||||
int consumedX = 0, consumedY = 0;
|
||||
|
||||
consumePendingUpdateOperations();
|
||||
if (mAdapter != null) {
|
||||
eatRequestLayout();
|
||||
onEnterLayoutOrScroll();
|
||||
if (x != 0) {
|
||||
hresult = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
|
||||
overscrollX = x - hresult;
|
||||
consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
|
||||
unconsumedX = x - consumedX;
|
||||
}
|
||||
if (y != 0) {
|
||||
vresult = mLayout.scrollVerticallyBy(y, mRecycler, mState);
|
||||
overscrollY = y - vresult;
|
||||
consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
|
||||
unconsumedY = y - consumedY;
|
||||
}
|
||||
if (supportsChangeAnimations()) {
|
||||
// Fix up shadow views used by changing animations
|
||||
|
@ -1227,19 +1367,27 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
if (!mItemDecorations.isEmpty()) {
|
||||
invalidate();
|
||||
}
|
||||
if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
|
||||
if (fromMotionEvent) {
|
||||
pullGlows(motionX, overscrollX, motionY, overscrollY);
|
||||
|
||||
if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
|
||||
// Update the last touch co-ords, taking any scroll offset into account
|
||||
mLastTouchX -= mScrollOffset[0];
|
||||
mLastTouchY -= mScrollOffset[1];
|
||||
ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
|
||||
mNestedOffsets[0] += mScrollOffset[0];
|
||||
mNestedOffsets[1] += mScrollOffset[1];
|
||||
} else if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
|
||||
if (ev != null) {
|
||||
pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
|
||||
}
|
||||
considerReleasingGlowsOnScroll(x, y);
|
||||
}
|
||||
if (hresult != 0 || vresult != 0) {
|
||||
dispatchOnScrolled(hresult, vresult);
|
||||
if (consumedX != 0 || consumedY != 0) {
|
||||
dispatchOnScrolled(consumedX, consumedY);
|
||||
}
|
||||
if (!awakenScrollBars()) {
|
||||
invalidate();
|
||||
}
|
||||
return hresult != 0 || vresult != 0;
|
||||
return consumedX != 0 || consumedY != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1432,19 +1580,31 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
"Call setLayoutManager with a non-null argument.");
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
|
||||
final boolean canScrollVertical = mLayout.canScrollVertically();
|
||||
|
||||
if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
|
||||
velocityX = 0;
|
||||
}
|
||||
if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
|
||||
velocityY = 0;
|
||||
}
|
||||
velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
|
||||
velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
|
||||
if (velocityX != 0 || velocityY != 0) {
|
||||
mViewFlinger.fling(velocityX, velocityY);
|
||||
return true;
|
||||
if (velocityX == 0 && velocityY == 0) {
|
||||
// If we don't have any velocity, return false
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!dispatchNestedPreFling(velocityX, velocityY)) {
|
||||
final boolean canScroll = canScrollHorizontal || canScrollVertical;
|
||||
dispatchNestedFling(velocityX, velocityY, canScroll);
|
||||
|
||||
if (canScroll) {
|
||||
velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
|
||||
velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
|
||||
mViewFlinger.fling(velocityX, velocityY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1468,29 +1628,52 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum velocity to start a fling.
|
||||
*
|
||||
* @return The minimum velocity to start a fling
|
||||
*/
|
||||
public int getMinFlingVelocity() {
|
||||
return mMinFlingVelocity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the maximum fling velocity used by this RecyclerView.
|
||||
*
|
||||
* @return The maximum fling velocity used by this RecyclerView.
|
||||
*/
|
||||
public int getMaxFlingVelocity() {
|
||||
return mMaxFlingVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a pull to relevant overscroll glow effects
|
||||
*/
|
||||
private void pullGlows(int x, int overscrollX, int y, int overscrollY) {
|
||||
private void pullGlows(float x, float overscrollX, float y, float overscrollY) {
|
||||
boolean invalidate = false;
|
||||
if (overscrollX < 0) {
|
||||
ensureLeftGlow();
|
||||
invalidate = mLeftGlow.onPull(-overscrollX / (float) getWidth(),
|
||||
1f - y / (float) getHeight()) || invalidate;
|
||||
if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y / getHeight())) {
|
||||
invalidate = true;
|
||||
}
|
||||
} else if (overscrollX > 0) {
|
||||
ensureRightGlow();
|
||||
invalidate = mRightGlow.onPull(overscrollX / (float) getWidth(),
|
||||
y / (float) getHeight()) || invalidate;
|
||||
if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
|
||||
invalidate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (overscrollY < 0) {
|
||||
ensureTopGlow();
|
||||
invalidate = mTopGlow.onPull(-overscrollY / (float) getHeight(),
|
||||
x / (float) getWidth()) || invalidate;
|
||||
if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
|
||||
invalidate = true;
|
||||
}
|
||||
} else if (overscrollY > 0) {
|
||||
ensureBottomGlow();
|
||||
invalidate = mBottomGlow.onPull(overscrollY / (float) getHeight(),
|
||||
1f - x / (float) getWidth()) || invalidate;
|
||||
if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
|
||||
invalidate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidate || overscrollX != 0 || overscrollY != 0) {
|
||||
|
@ -1693,6 +1876,14 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
removeCallbacks(mItemAnimatorRunner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if RecyclerView is attached to window.
|
||||
*/
|
||||
// @override
|
||||
public boolean isAttachedToWindow() {
|
||||
return mIsAttached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if RecyclerView is in the middle of a layout or scroll and throws an
|
||||
* {@link IllegalStateException} if it <b>is not</b>.
|
||||
|
@ -1739,6 +1930,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
* for each incoming MotionEvent until the end of the gesture.</p>
|
||||
*
|
||||
* @param listener Listener to add
|
||||
* @see SimpleOnItemTouchListener
|
||||
*/
|
||||
public void addOnItemTouchListener(OnItemTouchListener listener) {
|
||||
mOnItemTouchListeners.add(listener);
|
||||
|
@ -1832,6 +2024,15 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
setScrollState(SCROLL_STATE_DRAGGING);
|
||||
}
|
||||
|
||||
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
|
||||
if (canScrollHorizontally) {
|
||||
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
|
||||
}
|
||||
if (canScrollVertically) {
|
||||
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
|
||||
}
|
||||
startNestedScroll(nestedScrollAxis);
|
||||
break;
|
||||
|
||||
case MotionEventCompat.ACTION_POINTER_DOWN:
|
||||
|
@ -1874,6 +2075,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
|
||||
case MotionEvent.ACTION_UP: {
|
||||
mVelocityTracker.clear();
|
||||
stopNestedScroll();
|
||||
} break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
|
@ -1883,6 +2085,16 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
return mScrollState == SCROLL_STATE_DRAGGING;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
final int listenerCount = mOnItemTouchListeners.size();
|
||||
for (int i = 0; i < listenerCount; i++) {
|
||||
final OnItemTouchListener listener = mOnItemTouchListeners.get(i);
|
||||
listener.onRequestDisallowInterceptTouchEvent(disallowIntercept);
|
||||
}
|
||||
super.requestDisallowInterceptTouchEvent(disallowIntercept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent e) {
|
||||
if (dispatchOnItemTouch(e)) {
|
||||
|
@ -1898,14 +2110,29 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
}
|
||||
mVelocityTracker.addMovement(e);
|
||||
|
||||
final MotionEvent vtev = MotionEvent.obtain(e);
|
||||
final int action = MotionEventCompat.getActionMasked(e);
|
||||
final int actionIndex = MotionEventCompat.getActionIndex(e);
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
mNestedOffsets[0] = mNestedOffsets[1] = 0;
|
||||
}
|
||||
vtev.offsetLocation(mNestedOffsets[0], mNestedOffsets[1]);
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
|
||||
mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
|
||||
mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);
|
||||
|
||||
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
|
||||
if (canScrollHorizontally) {
|
||||
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
|
||||
}
|
||||
if (canScrollVertically) {
|
||||
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
|
||||
}
|
||||
startNestedScroll(nestedScrollAxis);
|
||||
} break;
|
||||
|
||||
case MotionEventCompat.ACTION_POINTER_DOWN: {
|
||||
|
@ -1924,32 +2151,52 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
|
||||
final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
|
||||
final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
|
||||
int dx = mLastTouchX - x;
|
||||
int dy = mLastTouchY - y;
|
||||
|
||||
if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {
|
||||
dx -= mScrollConsumed[0];
|
||||
dy -= mScrollConsumed[1];
|
||||
vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
|
||||
// Updated the nested offsets
|
||||
mNestedOffsets[0] += mScrollOffset[0];
|
||||
mNestedOffsets[1] += mScrollOffset[1];
|
||||
}
|
||||
|
||||
if (mScrollState != SCROLL_STATE_DRAGGING) {
|
||||
final int dx = x - mInitialTouchX;
|
||||
final int dy = y - mInitialTouchY;
|
||||
boolean startScroll = false;
|
||||
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
|
||||
mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
|
||||
if (dx > 0) {
|
||||
dx -= mTouchSlop;
|
||||
} else {
|
||||
dx += mTouchSlop;
|
||||
}
|
||||
startScroll = true;
|
||||
}
|
||||
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
|
||||
mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
|
||||
if (dy > 0) {
|
||||
dy -= mTouchSlop;
|
||||
} else {
|
||||
dy += mTouchSlop;
|
||||
}
|
||||
startScroll = true;
|
||||
}
|
||||
if (startScroll) {
|
||||
setScrollState(SCROLL_STATE_DRAGGING);
|
||||
}
|
||||
}
|
||||
|
||||
if (mScrollState == SCROLL_STATE_DRAGGING) {
|
||||
final int dx = x - mLastTouchX;
|
||||
final int dy = y - mLastTouchY;
|
||||
if (scrollByInternal(canScrollHorizontally ? -dx : 0,
|
||||
canScrollVertically ? -dy : 0, true, x, y)) {
|
||||
mLastTouchX = x - mScrollOffset[0];
|
||||
mLastTouchY = y - mScrollOffset[1];
|
||||
|
||||
if (scrollByInternal(
|
||||
canScrollHorizontally ? dx : 0,
|
||||
canScrollVertically ? dy : 0,
|
||||
vtev)) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
}
|
||||
mLastTouchX = x;
|
||||
mLastTouchY = y;
|
||||
} break;
|
||||
|
||||
case MotionEventCompat.ACTION_POINTER_UP: {
|
||||
|
@ -1965,6 +2212,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
|
||||
setScrollState(SCROLL_STATE_IDLE);
|
||||
}
|
||||
|
||||
mVelocityTracker.clear();
|
||||
releaseGlows();
|
||||
} break;
|
||||
|
@ -1974,6 +2222,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
} break;
|
||||
}
|
||||
|
||||
vtev.recycle();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1981,6 +2231,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
stopNestedScroll();
|
||||
releaseGlows();
|
||||
setScrollState(SCROLL_STATE_IDLE);
|
||||
}
|
||||
|
@ -2823,6 +3074,18 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
return mLayout.generateLayoutParams(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if RecyclerView is currently running some animations.
|
||||
* <p>
|
||||
* If you want to be notified when animations are finished, use
|
||||
* {@link ItemAnimator#isRunning(ItemAnimator.ItemAnimatorFinishedListener)}.
|
||||
*
|
||||
* @return True if there are some item animations currently running or waiting to be started.
|
||||
*/
|
||||
public boolean isAnimating() {
|
||||
return mItemAnimator != null && mItemAnimator.isRunning();
|
||||
}
|
||||
|
||||
void saveOldPositions() {
|
||||
final int childCount = mChildHelper.getUnfilteredChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
|
@ -3232,6 +3495,11 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the bounds of all child views by <code>dy</code> pixels.
|
||||
* Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
|
||||
|
@ -5153,17 +5421,32 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
}
|
||||
|
||||
private void dispatchChildDetached(View child) {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.onViewDetachedFromWindow(getChildViewHolderInt(child));
|
||||
}
|
||||
final ViewHolder viewHolder = getChildViewHolderInt(child);
|
||||
onChildDetachedFromWindow(child);
|
||||
if (mAdapter != null && viewHolder != null) {
|
||||
mAdapter.onViewDetachedFromWindow(viewHolder);
|
||||
}
|
||||
if (mOnChildAttachStateListeners != null) {
|
||||
final int cnt = mOnChildAttachStateListeners.size();
|
||||
for (int i = cnt - 1; i >= 0; i--) {
|
||||
mOnChildAttachStateListeners.get(i).onChildViewDetachedFromWindow(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchChildAttached(View child) {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.onViewAttachedToWindow(getChildViewHolderInt(child));
|
||||
}
|
||||
final ViewHolder viewHolder = getChildViewHolderInt(child);
|
||||
onChildAttachedToWindow(child);
|
||||
if (mAdapter != null && viewHolder != null) {
|
||||
mAdapter.onViewAttachedToWindow(viewHolder);
|
||||
}
|
||||
if (mOnChildAttachStateListeners != null) {
|
||||
final int cnt = mOnChildAttachStateListeners.size();
|
||||
for (int i = cnt - 1; i >= 0; i--) {
|
||||
mOnChildAttachStateListeners.get(i).onChildViewAttachedToWindow(child);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5173,6 +5456,14 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
* a <code>RecyclerView</code> can be used to implement a standard vertically scrolling list,
|
||||
* a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
|
||||
* layout managers are provided for general use.
|
||||
* <p/>
|
||||
* If the LayoutManager specifies a default constructor or one with the signature
|
||||
* ({@link Context}, {@link AttributeSet}, {@code int}, {@code int}), RecyclerView will
|
||||
* instantiate and set the LayoutManager when being inflated. Most used properties can
|
||||
* be then obtained from {@link #getProperties(Context, AttributeSet, int, int)}. In case
|
||||
* a LayoutManager specifies both constructors, the non-default constructor will take
|
||||
* precedence.
|
||||
*
|
||||
*/
|
||||
public static abstract class LayoutManager {
|
||||
ChildHelper mChildHelper;
|
||||
|
@ -7259,6 +7550,20 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
int action, Bundle args) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some general properties that a LayoutManager may want to use.
|
||||
*/
|
||||
public static class Properties {
|
||||
/** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
|
||||
public int orientation;
|
||||
/** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount */
|
||||
public int spanCount;
|
||||
/** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
|
||||
public boolean reverseLayout;
|
||||
/** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd */
|
||||
public boolean stackFromEnd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7359,8 +7664,10 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
* manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
|
||||
* a touch interaction already in progress even if the RecyclerView is already handling that
|
||||
* gesture stream itself for the purposes of scrolling.</p>
|
||||
*
|
||||
* @see SimpleOnItemTouchListener
|
||||
*/
|
||||
public interface OnItemTouchListener {
|
||||
public static interface OnItemTouchListener {
|
||||
/**
|
||||
* Silently observe and/or take over touch events sent to the RecyclerView
|
||||
* before they are handled by either the RecyclerView itself or its child views.
|
||||
|
@ -7385,8 +7692,44 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
* the RecyclerView's coordinate system.
|
||||
*/
|
||||
public void onTouchEvent(RecyclerView rv, MotionEvent e);
|
||||
|
||||
/**
|
||||
* Called when a child of RecyclerView does not want RecyclerView and its ancestors to
|
||||
* intercept touch events with
|
||||
* {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
|
||||
*
|
||||
* @param disallowIntercept True if the child does not want the parent to
|
||||
* intercept touch events.
|
||||
* @see ViewParent#requestDisallowInterceptTouchEvent(boolean)
|
||||
*/
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
|
||||
* default return values.
|
||||
* <p>
|
||||
* You may prefer to extend this class if you don't need to override all methods. Another
|
||||
* benefit of using this class is future compatibility. As the interface may change, we'll
|
||||
* always provide a default implementation on this class so that your code won't break when
|
||||
* you update to a new version of the support library.
|
||||
*/
|
||||
public class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An OnScrollListener can be set on a RecyclerView to receive messages
|
||||
* when a scrolling event has occurred on that RecyclerView.
|
||||
|
@ -7399,7 +7742,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
* want your components to be able to easily replace the listener use
|
||||
* RecyclerView#setOnScrollListener.
|
||||
*/
|
||||
abstract static public class OnScrollListener {
|
||||
public abstract static class OnScrollListener {
|
||||
/**
|
||||
* Callback method to be invoked when RecyclerView's scroll state changes.
|
||||
*
|
||||
|
@ -7444,6 +7787,27 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
public void onViewRecycled(ViewHolder holder);
|
||||
}
|
||||
|
||||
/**
|
||||
* A Listener interface that can be attached to a RecylcerView to get notified
|
||||
* whenever a ViewHolder is attached to or detached from RecyclerView.
|
||||
*/
|
||||
public interface OnChildAttachStateChangeListener {
|
||||
|
||||
/**
|
||||
* Called when a view is attached to the RecyclerView.
|
||||
*
|
||||
* @param view The View which is attached to the RecyclerView
|
||||
*/
|
||||
public void onChildViewAttachedToWindow(View view);
|
||||
|
||||
/**
|
||||
* Called when a view is detached from RecyclerView.
|
||||
*
|
||||
* @param view The View which is being detached from the RecyclerView
|
||||
*/
|
||||
public void onChildViewDetachedFromWindow(View view);
|
||||
}
|
||||
|
||||
/**
|
||||
* A ViewHolder describes an item view and metadata about its place within the RecyclerView.
|
||||
*
|
||||
|
@ -7862,6 +8226,55 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
|
||||
}
|
||||
|
||||
// NestedScrollingChild
|
||||
|
||||
@Override
|
||||
public void setNestedScrollingEnabled(boolean enabled) {
|
||||
mScrollingChildHelper.setNestedScrollingEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNestedScrollingEnabled() {
|
||||
return mScrollingChildHelper.isNestedScrollingEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startNestedScroll(int axes) {
|
||||
return mScrollingChildHelper.startNestedScroll(axes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopNestedScroll() {
|
||||
mScrollingChildHelper.stopNestedScroll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNestedScrollingParent() {
|
||||
return mScrollingChildHelper.hasNestedScrollingParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
|
||||
int dyUnconsumed, int[] offsetInWindow) {
|
||||
return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed,
|
||||
dxUnconsumed, dyUnconsumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
|
||||
return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
|
||||
return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
|
||||
return mScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
|
||||
* {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
|
||||
|
@ -9318,4 +9731,35 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
|||
this.bottom = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getChildDrawingOrder(int childCount, int i) {
|
||||
if (mChildDrawingOrderCallback == null) {
|
||||
return super.getChildDrawingOrder(childCount, i);
|
||||
} else {
|
||||
return mChildDrawingOrderCallback.onGetChildDrawingOrder(childCount, i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback interface that can be used to alter the drawing order of RecyclerView children.
|
||||
* <p>
|
||||
* It works using the {@link ViewGroup#getChildDrawingOrder(int, int)} method, so any case
|
||||
* that applies to that method also applies to this callback. For example, changing the drawing
|
||||
* order of two views will not have any effect if their elevation values are different since
|
||||
* elevation overrides the result of this callback.
|
||||
*/
|
||||
public static interface ChildDrawingOrderCallback {
|
||||
/**
|
||||
* Returns the index of the child to draw for this iteration. Override this
|
||||
* if you want to change the drawing order of children. By default, it
|
||||
* returns i.
|
||||
*
|
||||
* @param i The current iteration.
|
||||
* @return The index of the child to draw this iteration.
|
||||
*
|
||||
* @see RecyclerView#setChildDrawingOrderCallback(RecyclerView.ChildDrawingOrderCallback)
|
||||
*/
|
||||
public int onGetChildDrawingOrder(int childCount, int i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -537,7 +537,6 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
@Override
|
||||
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
ensureOrientationHelper();
|
||||
|
||||
final AnchorInfo anchorInfo = mAnchorInfo;
|
||||
anchorInfo.reset();
|
||||
|
||||
|
@ -577,21 +576,22 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
detachAndScrapAttachedViews(recycler);
|
||||
mLaidOutInvalidFullSpan = false;
|
||||
updateMeasureSpecs();
|
||||
updateLayoutState(anchorInfo.mPosition, state);
|
||||
if (anchorInfo.mLayoutFromEnd) {
|
||||
// Layout start.
|
||||
updateLayoutStateToFillStart(anchorInfo.mPosition, state);
|
||||
setLayoutStateDirection(LAYOUT_START);
|
||||
fill(recycler, mLayoutState, state);
|
||||
// Layout end.
|
||||
updateLayoutStateToFillEnd(anchorInfo.mPosition, state);
|
||||
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
|
||||
setLayoutStateDirection(LAYOUT_END);
|
||||
mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection;
|
||||
fill(recycler, mLayoutState, state);
|
||||
} else {
|
||||
// Layout end.
|
||||
updateLayoutStateToFillEnd(anchorInfo.mPosition, state);
|
||||
setLayoutStateDirection(LAYOUT_END);
|
||||
fill(recycler, mLayoutState, state);
|
||||
// Layout start.
|
||||
updateLayoutStateToFillStart(anchorInfo.mPosition, state);
|
||||
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
|
||||
setLayoutStateDirection(LAYOUT_START);
|
||||
mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection;
|
||||
fill(recycler, mLayoutState, state);
|
||||
}
|
||||
|
||||
|
@ -1254,40 +1254,37 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void updateLayoutStateToFillStart(int anchorPosition, RecyclerView.State state) {
|
||||
private void updateLayoutState(int anchorPosition, RecyclerView.State state) {
|
||||
mLayoutState.mAvailable = 0;
|
||||
mLayoutState.mCurrentPosition = anchorPosition;
|
||||
int startExtra = 0;
|
||||
int endExtra = 0;
|
||||
if (isSmoothScrolling()) {
|
||||
final int targetPos = state.getTargetScrollPosition();
|
||||
if (mShouldReverseLayout == targetPos < anchorPosition) {
|
||||
mLayoutState.mExtra = 0;
|
||||
} else {
|
||||
mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace();
|
||||
if (targetPos != NO_POSITION) {
|
||||
if (mShouldReverseLayout == targetPos < anchorPosition) {
|
||||
endExtra = mPrimaryOrientation.getTotalSpace();
|
||||
} else {
|
||||
startExtra = mPrimaryOrientation.getTotalSpace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mLayoutState.mExtra = 0;
|
||||
}
|
||||
mLayoutState.mLayoutDirection = LAYOUT_START;
|
||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL
|
||||
: ITEM_DIRECTION_HEAD;
|
||||
|
||||
// Line of the furthest row.
|
||||
final boolean clipToPadding = getClipToPadding();
|
||||
if (clipToPadding) {
|
||||
mLayoutState.mStartLine = mPrimaryOrientation.getStartAfterPadding() - startExtra;
|
||||
mLayoutState.mEndLine = mPrimaryOrientation.getEndAfterPadding() + endExtra;
|
||||
} else {
|
||||
mLayoutState.mEndLine = mPrimaryOrientation.getEnd() + endExtra;
|
||||
mLayoutState.mStartLine = -startExtra;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateLayoutStateToFillEnd(int anchorPosition, RecyclerView.State state) {
|
||||
mLayoutState.mAvailable = 0;
|
||||
mLayoutState.mCurrentPosition = anchorPosition;
|
||||
if (isSmoothScrolling()) {
|
||||
final int targetPos = state.getTargetScrollPosition();
|
||||
if (mShouldReverseLayout == targetPos > anchorPosition) {
|
||||
mLayoutState.mExtra = 0;
|
||||
} else {
|
||||
mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace();
|
||||
}
|
||||
} else {
|
||||
mLayoutState.mExtra = 0;
|
||||
}
|
||||
mLayoutState.mLayoutDirection = LAYOUT_END;
|
||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD
|
||||
: ITEM_DIRECTION_TAIL;
|
||||
private void setLayoutStateDirection(int direction) {
|
||||
mLayoutState.mLayoutDirection = direction;
|
||||
mLayoutState.mItemDirection = (mShouldReverseLayout == (direction == LAYOUT_START)) ?
|
||||
ITEM_DIRECTION_TAIL : ITEM_DIRECTION_HEAD;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1383,31 +1380,25 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
mRemainingSpans.set(0, mSpanCount, true);
|
||||
// The target position we are trying to reach.
|
||||
final int targetLine;
|
||||
/*
|
||||
* The line until which we can recycle, as long as we add views.
|
||||
* Keep in mind, it is still the line in layout direction which means; to calculate the
|
||||
* actual recycle line, we should subtract/add the size in orientation.
|
||||
*/
|
||||
final int recycleLine;
|
||||
|
||||
// Line of the furthest row.
|
||||
if (layoutState.mLayoutDirection == LAYOUT_END) {
|
||||
// ignore padding for recycler
|
||||
recycleLine = mPrimaryOrientation.getEndAfterPadding() + mLayoutState.mAvailable;
|
||||
targetLine = recycleLine + mLayoutState.mExtra + mPrimaryOrientation.getEndPadding();
|
||||
|
||||
targetLine = layoutState.mEndLine + layoutState.mAvailable;
|
||||
} else { // LAYOUT_START
|
||||
// ignore padding for recycler
|
||||
recycleLine = mPrimaryOrientation.getStartAfterPadding() - mLayoutState.mAvailable;
|
||||
targetLine = recycleLine - mLayoutState.mExtra -
|
||||
mPrimaryOrientation.getStartAfterPadding();
|
||||
targetLine = layoutState.mStartLine - layoutState.mAvailable;
|
||||
}
|
||||
|
||||
updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "FILLING targetLine: " + targetLine + "," +
|
||||
"remaining spans:" + mRemainingSpans + ", state: " + layoutState);
|
||||
}
|
||||
|
||||
// the default coordinate to add new view.
|
||||
final int defaultNewViewLine = mShouldReverseLayout
|
||||
? mPrimaryOrientation.getEndAfterPadding()
|
||||
: mPrimaryOrientation.getStartAfterPadding();
|
||||
|
||||
boolean added = false;
|
||||
while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty()) {
|
||||
View view = layoutState.next(recycler);
|
||||
LayoutParams lp = ((LayoutParams) view.getLayoutParams());
|
||||
|
@ -1500,18 +1491,21 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
} else {
|
||||
updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine);
|
||||
}
|
||||
recycle(recycler, mLayoutState, currentSpan, recycleLine);
|
||||
recycle(recycler, mLayoutState);
|
||||
added = true;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "fill, " + getChildCount());
|
||||
if (!added) {
|
||||
recycle(recycler, mLayoutState);
|
||||
}
|
||||
final int diff;
|
||||
if (mLayoutState.mLayoutDirection == LAYOUT_START) {
|
||||
final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding());
|
||||
return Math.max(0, mLayoutState.mAvailable + (recycleLine - minStart));
|
||||
diff = mPrimaryOrientation.getStartAfterPadding() - minStart;
|
||||
} else {
|
||||
final int max = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
|
||||
return Math.max(0, mLayoutState.mAvailable + (max - recycleLine));
|
||||
final int maxEnd = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
|
||||
diff = maxEnd - mPrimaryOrientation.getEndAfterPadding();
|
||||
}
|
||||
return diff > 0 ? Math.min(layoutState.mAvailable, diff) : 0;
|
||||
}
|
||||
|
||||
private LazySpanLookup.FullSpanItem createFullSpanItemFromEnd(int newItemTop) {
|
||||
|
@ -1548,19 +1542,40 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
}
|
||||
}
|
||||
|
||||
private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState,
|
||||
Span updatedSpan, int recycleLine) {
|
||||
if (layoutState.mLayoutDirection == LAYOUT_START) {
|
||||
// calculate recycle line
|
||||
int maxStart = getMaxStart(updatedSpan.getStartLine());
|
||||
recycleFromEnd(recycler, Math.max(recycleLine, maxStart) +
|
||||
(mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding()));
|
||||
private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState) {
|
||||
if (layoutState.mAvailable == 0) {
|
||||
// easy, recycle line is still valid
|
||||
if (layoutState.mLayoutDirection == LAYOUT_START) {
|
||||
recycleFromEnd(recycler, layoutState.mEndLine);
|
||||
} else {
|
||||
recycleFromStart(recycler, layoutState.mStartLine);
|
||||
}
|
||||
} else {
|
||||
// calculate recycle line
|
||||
int minEnd = getMinEnd(updatedSpan.getEndLine());
|
||||
recycleFromStart(recycler, Math.min(recycleLine, minEnd) -
|
||||
(mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding()));
|
||||
// scrolling case, recycle line can be shifted by how much space we could cover
|
||||
// by adding new views
|
||||
if (layoutState.mLayoutDirection == LAYOUT_START) {
|
||||
// calculate recycle line
|
||||
int scrolled = layoutState.mStartLine - getMaxStart(layoutState.mStartLine);
|
||||
final int line;
|
||||
if (scrolled < 0) {
|
||||
line = layoutState.mEndLine;
|
||||
} else {
|
||||
line = layoutState.mEndLine - Math.min(scrolled, layoutState.mAvailable);
|
||||
}
|
||||
recycleFromEnd(recycler, line);
|
||||
} else {
|
||||
// calculate recycle line
|
||||
int scrolled = getMinEnd(layoutState.mEndLine) - layoutState.mEndLine;
|
||||
final int line;
|
||||
if (scrolled < 0) {
|
||||
line = layoutState.mStartLine;
|
||||
} else {
|
||||
line = layoutState.mStartLine + Math.min(scrolled, layoutState.mAvailable);
|
||||
}
|
||||
recycleFromStart(recycler, line);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void appendViewToAllSpans(View view) {
|
||||
|
@ -1602,12 +1617,12 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
final int deletedSize = span.getDeletedSize();
|
||||
if (layoutDir == LAYOUT_START) {
|
||||
final int line = span.getStartLine();
|
||||
if (line + deletedSize < targetLine) {
|
||||
if (line + deletedSize <= targetLine) {
|
||||
mRemainingSpans.set(span.mIndex, false);
|
||||
}
|
||||
} else {
|
||||
final int line = span.getEndLine();
|
||||
if (line - deletedSize > targetLine) {
|
||||
if (line - deletedSize >= targetLine) {
|
||||
mRemainingSpans.set(span.mIndex, false);
|
||||
}
|
||||
}
|
||||
|
@ -1678,18 +1693,24 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
}
|
||||
|
||||
private void recycleFromStart(RecyclerView.Recycler recycler, int line) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "recycling from start for line " + line);
|
||||
}
|
||||
while (getChildCount() > 0) {
|
||||
View child = getChildAt(0);
|
||||
if (mPrimaryOrientation.getDecoratedEnd(child) < line) {
|
||||
if (mPrimaryOrientation.getDecoratedEnd(child) <= line) {
|
||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
// Don't recycle the last View in a span not to lose span's start/end lines
|
||||
if (lp.mFullSpan) {
|
||||
for (int j = 0; j < mSpanCount; j++) {
|
||||
if (mSpans[j].mViews.size() == 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < mSpanCount; j++) {
|
||||
mSpans[j].popStart();
|
||||
}
|
||||
} else {
|
||||
if (lp.mSpan.mViews.size() == 1) {
|
||||
return;
|
||||
}
|
||||
lp.mSpan.popStart();
|
||||
}
|
||||
removeAndRecycleView(child, recycler);
|
||||
|
@ -1704,13 +1725,22 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
int i;
|
||||
for (i = childCount - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (mPrimaryOrientation.getDecoratedStart(child) > line) {
|
||||
if (mPrimaryOrientation.getDecoratedStart(child) >= line) {
|
||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
// Don't recycle the last View in a span not to lose span's start/end lines
|
||||
if (lp.mFullSpan) {
|
||||
for (int j = 0; j < mSpanCount; j++) {
|
||||
if (mSpans[j].mViews.size() == 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < mSpanCount; j++) {
|
||||
mSpans[j].popEnd();
|
||||
}
|
||||
} else {
|
||||
if (lp.mSpan.mViews.size() == 1) {
|
||||
return;
|
||||
}
|
||||
lp.mSpan.popEnd();
|
||||
}
|
||||
removeAndRecycleView(child, recycler);
|
||||
|
@ -1860,21 +1890,19 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
|||
int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||
ensureOrientationHelper();
|
||||
final int referenceChildPosition;
|
||||
final int layoutDir;
|
||||
if (dt > 0) { // layout towards end
|
||||
mLayoutState.mLayoutDirection = LAYOUT_END;
|
||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD
|
||||
: ITEM_DIRECTION_TAIL;
|
||||
layoutDir = LAYOUT_END;
|
||||
referenceChildPosition = getLastChildPosition();
|
||||
} else {
|
||||
mLayoutState.mLayoutDirection = LAYOUT_START;
|
||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL
|
||||
: ITEM_DIRECTION_HEAD;
|
||||
layoutDir = LAYOUT_START;
|
||||
referenceChildPosition = getFirstChildPosition();
|
||||
}
|
||||
updateLayoutState(referenceChildPosition, state);
|
||||
setLayoutStateDirection(layoutDir);
|
||||
mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
|
||||
final int absDt = Math.abs(dt);
|
||||
mLayoutState.mAvailable = absDt;
|
||||
mLayoutState.mExtra = isSmoothScrolling() ? mPrimaryOrientation.getTotalSpace() : 0;
|
||||
int consumed = fill(recycler, mLayoutState, state);
|
||||
final int totalScroll;
|
||||
if (absDt < consumed) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.android.support.widget.helper;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.view.View;
|
||||
|
||||
import org.telegram.android.support.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* Utility class for {@link ItemTouchHelper} which handles item transformations for different
|
||||
* API versions.
|
||||
* <p/>
|
||||
* This class has methods that map to {@link ItemTouchHelper.Callback}'s drawing methods. Default
|
||||
* implementations in {@link ItemTouchHelper.Callback} call these methods with
|
||||
* {@link RecyclerView.ViewHolder#itemView} and {@link ItemTouchUIUtil} makes necessary changes
|
||||
* on the View depending on the API level. You can access the instance of {@link ItemTouchUIUtil}
|
||||
* via {@link ItemTouchHelper.Callback#getDefaultUIUtil()} and call its methods with the children
|
||||
* of ViewHolder that you want to apply default effects.
|
||||
*
|
||||
* @see ItemTouchHelper.Callback#getDefaultUIUtil()
|
||||
*/
|
||||
public interface ItemTouchUIUtil {
|
||||
|
||||
/**
|
||||
* The default implementation for {@link ItemTouchHelper.Callback#onChildDraw(Canvas,
|
||||
* RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}
|
||||
*/
|
||||
void onDraw(Canvas c, RecyclerView recyclerView, View view,
|
||||
float dX, float dY, int actionState, boolean isCurrentlyActive);
|
||||
|
||||
/**
|
||||
* The default implementation for {@link ItemTouchHelper.Callback#onChildDrawOver(Canvas,
|
||||
* RecyclerView, RecyclerView.ViewHolder, float, float, int, boolean)}
|
||||
*/
|
||||
void onDrawOver(Canvas c, RecyclerView recyclerView, View view,
|
||||
float dX, float dY, int actionState, boolean isCurrentlyActive);
|
||||
|
||||
/**
|
||||
* The default implementation for {@link ItemTouchHelper.Callback#clearView(RecyclerView,
|
||||
* RecyclerView.ViewHolder)}
|
||||
*/
|
||||
void clearView(View view);
|
||||
|
||||
/**
|
||||
* The default implementation for {@link ItemTouchHelper.Callback#onSelectedChanged(
|
||||
* RecyclerView.ViewHolder, int)}
|
||||
*/
|
||||
void onSelected(View view);
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright (C) 2015 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.android.support.widget.helper;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import org.telegram.android.support.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
|
||||
/**
|
||||
* Package private class to keep implementations. Putting them inside ItemTouchUIUtil makes them
|
||||
* public API, which is not desired in this case.
|
||||
*/
|
||||
class ItemTouchUIUtilImpl {
|
||||
final static int item_touch_helper_previous_elevation = 123;
|
||||
static class Lollipop extends Honeycomb {
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView recyclerView, View view,
|
||||
float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
if (isCurrentlyActive) {
|
||||
Object originalElevation = view.getTag(item_touch_helper_previous_elevation);
|
||||
if (originalElevation == null) {
|
||||
originalElevation = ViewCompat.getElevation(view);
|
||||
float newElevation = 1f + findMaxElevation(recyclerView, view);
|
||||
ViewCompat.setElevation(view, newElevation);
|
||||
view.setTag(item_touch_helper_previous_elevation, originalElevation);
|
||||
}
|
||||
}
|
||||
super.onDraw(c, recyclerView, view, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
|
||||
private float findMaxElevation(RecyclerView recyclerView, View itemView) {
|
||||
final int childCount = recyclerView.getChildCount();
|
||||
float max = 0;
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = recyclerView.getChildAt(i);
|
||||
if (child == itemView) {
|
||||
continue;
|
||||
}
|
||||
final float elevation = ViewCompat.getElevation(child);
|
||||
if (elevation > max) {
|
||||
max = elevation;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(View view) {
|
||||
final Object tag = view.getTag(item_touch_helper_previous_elevation);
|
||||
if (tag != null && tag instanceof Float) {
|
||||
ViewCompat.setElevation(view, (Float) tag);
|
||||
}
|
||||
view.setTag(item_touch_helper_previous_elevation, null);
|
||||
super.clearView(view);
|
||||
}
|
||||
}
|
||||
|
||||
static class Honeycomb implements ItemTouchUIUtil {
|
||||
|
||||
@Override
|
||||
public void clearView(View view) {
|
||||
ViewCompat.setTranslationX(view, 0f);
|
||||
ViewCompat.setTranslationY(view, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelected(View view) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView recyclerView, View view,
|
||||
float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
ViewCompat.setTranslationX(view, dX);
|
||||
ViewCompat.setTranslationY(view, dY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawOver(Canvas c, RecyclerView recyclerView,
|
||||
View view, float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class Gingerbread implements ItemTouchUIUtil {
|
||||
|
||||
private void draw(Canvas c, RecyclerView parent, View view,
|
||||
float dX, float dY) {
|
||||
c.save();
|
||||
c.translate(dX, dY);
|
||||
parent.drawChild(c, view, 0);
|
||||
c.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(View view) {
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelected(View view) {
|
||||
view.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView recyclerView, View view,
|
||||
float dX, float dY, int actionState, boolean isCurrentlyActive) {
|
||||
if (actionState != ItemTouchHelper.ACTION_STATE_DRAG) {
|
||||
draw(c, recyclerView, view, dX, dY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawOver(Canvas c, RecyclerView recyclerView,
|
||||
View view, float dX, float dY,
|
||||
int actionState, boolean isCurrentlyActive) {
|
||||
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
|
||||
draw(c, recyclerView, view, dX, dY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.telegram.android.support.widget.util;
|
||||
|
||||
import android.support.v7.util.SortedList;
|
||||
import org.telegram.android.support.util.SortedList;
|
||||
import org.telegram.android.support.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
|
|
|
@ -90,7 +90,6 @@ public class ApplicationLoader extends Application {
|
|||
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
|
||||
int selectedBackground = preferences.getInt("selectedBackground", 1000001);
|
||||
selectedColor = preferences.getInt("selectedColor", 0);
|
||||
int cacheColorHint = 0;
|
||||
if (selectedColor == 0) {
|
||||
if (selectedBackground == 1000001) {
|
||||
cachedWallpaper = applicationContext.getResources().getDrawable(R.drawable.background_hd);
|
||||
|
|
|
@ -363,7 +363,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
|
|||
try {
|
||||
SerializedData data = new SerializedData(configFile);
|
||||
isTestBackend = data.readInt32(false);
|
||||
int version = data.readInt32(false);
|
||||
data.readInt32(false);
|
||||
sessionsToDestroy.clear();
|
||||
int count = data.readInt32(false);
|
||||
for (int a = 0; a < count; a++) {
|
||||
|
@ -2700,7 +2700,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
|
|||
return;
|
||||
}
|
||||
|
||||
int messageLength = data.readInt32(false);
|
||||
data.readInt32(false);
|
||||
|
||||
TLObject message = deserialize(getRequestWithMessageId(messageId), data, true);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ public class FileLoadOperation {
|
|||
private final static int stateFinished = 3;
|
||||
|
||||
private final static int downloadChunkSize = 1024 * 32;
|
||||
private final static int downloadChunkSizeBig = 1024 * 128;
|
||||
private final static int maxDownloadRequests = 3;
|
||||
|
||||
private int datacenter_id;
|
||||
|
@ -38,6 +39,7 @@ public class FileLoadOperation {
|
|||
private FileLoadOperationDelegate delegate;
|
||||
private byte[] key;
|
||||
private byte[] iv;
|
||||
private int currentDownloadChunkSize;
|
||||
|
||||
private int nextDownloadOffset = 0;
|
||||
private ArrayList<RequestInfo> requestInfos = new ArrayList<>(maxDownloadRequests);
|
||||
|
@ -165,6 +167,7 @@ public class FileLoadOperation {
|
|||
if (state != stateIdle) {
|
||||
return;
|
||||
}
|
||||
currentDownloadChunkSize = totalBytesCount >= 1024 * 1024 * 30 ? downloadChunkSizeBig : downloadChunkSize;
|
||||
state = stateDownloading;
|
||||
if (location == null) {
|
||||
Utilities.stageQueue.postRunnable(new Runnable() {
|
||||
|
@ -175,7 +178,6 @@ public class FileLoadOperation {
|
|||
});
|
||||
return;
|
||||
}
|
||||
Long mediaId = null;
|
||||
String fileNameFinal;
|
||||
String fileNameTemp;
|
||||
String fileNameIv = null;
|
||||
|
@ -223,7 +225,7 @@ public class FileLoadOperation {
|
|||
cacheFileTemp = new File(tempPath, fileNameTemp);
|
||||
if (cacheFileTemp.exists()) {
|
||||
downloadedBytes = (int)cacheFileTemp.length();
|
||||
nextDownloadOffset = downloadedBytes = downloadedBytes / 1024 * 1024;
|
||||
nextDownloadOffset = downloadedBytes = downloadedBytes / currentDownloadChunkSize * currentDownloadChunkSize;
|
||||
}
|
||||
if (fileNameIv != null) {
|
||||
cacheIvTemp = new File(tempPath, fileNameIv);
|
||||
|
@ -388,10 +390,10 @@ public class FileLoadOperation {
|
|||
}
|
||||
}
|
||||
|
||||
if (currentBytesSize != downloadChunkSize) {
|
||||
if (currentBytesSize != currentDownloadChunkSize) {
|
||||
onFinishLoadingFile();
|
||||
} else {
|
||||
if (totalBytesCount != downloadedBytes && downloadedBytes % downloadChunkSize == 0 || totalBytesCount > 0 && totalBytesCount > downloadedBytes) {
|
||||
if (totalBytesCount != downloadedBytes && downloadedBytes % currentDownloadChunkSize == 0 || totalBytesCount > 0 && totalBytesCount > downloadedBytes) {
|
||||
startDownloadRequest();
|
||||
} else {
|
||||
onFinishLoadingFile();
|
||||
|
@ -422,7 +424,7 @@ public class FileLoadOperation {
|
|||
startDownloadRequest();
|
||||
}
|
||||
} else if (error.text.contains("OFFSET_INVALID")) {
|
||||
if (downloadedBytes % downloadChunkSize == 0) {
|
||||
if (downloadedBytes % currentDownloadChunkSize == 0) {
|
||||
try {
|
||||
onFinishLoadingFile();
|
||||
} catch (Exception e) {
|
||||
|
@ -460,12 +462,12 @@ public class FileLoadOperation {
|
|||
if (totalBytesCount > 0 && nextDownloadOffset >= totalBytesCount) {
|
||||
break;
|
||||
}
|
||||
boolean isLast = totalBytesCount <= 0 || a == count - 1 || totalBytesCount > 0 && nextDownloadOffset + downloadChunkSize >= totalBytesCount;
|
||||
boolean isLast = totalBytesCount <= 0 || a == count - 1 || totalBytesCount > 0 && nextDownloadOffset + currentDownloadChunkSize >= totalBytesCount;
|
||||
TLRPC.TL_upload_getFile req = new TLRPC.TL_upload_getFile();
|
||||
req.location = location;
|
||||
req.offset = nextDownloadOffset;
|
||||
req.limit = downloadChunkSize;
|
||||
nextDownloadOffset += downloadChunkSize;
|
||||
req.limit = currentDownloadChunkSize;
|
||||
nextDownloadOffset += currentDownloadChunkSize;
|
||||
|
||||
final RequestInfo requestInfo = new RequestInfo();
|
||||
requestInfos.add(requestInfo);
|
||||
|
|
|
@ -675,6 +675,15 @@ public class FileLoader {
|
|||
return closestObject;
|
||||
}
|
||||
|
||||
public static String getFileExtension(File file) {
|
||||
String name = file.getName();
|
||||
try {
|
||||
return name.substring(name.lastIndexOf(".") + 1);
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getDocumentFileName(TLRPC.Document document) {
|
||||
if (document != null) {
|
||||
if (document.file_name != null) {
|
||||
|
|
|
@ -637,7 +637,7 @@ public class HandshakeAction extends Action implements TcpConnection.TcpConnecti
|
|||
FileLog.d("tmessages", String.format("===== Duplicate message id %d received, ignoring", messageId));
|
||||
return;
|
||||
}
|
||||
int messageLength = data.readInt32(false);
|
||||
data.readInt32(false);
|
||||
|
||||
int constructor = data.readInt32(false);
|
||||
TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, false);
|
||||
|
|
|
@ -11634,6 +11634,9 @@ public class TLRPC {
|
|||
case 0x9eddf188:
|
||||
result = new TL_inputMessagesFilterDocument();
|
||||
break;
|
||||
case 0x5afbf764:
|
||||
result = new TL_inputMessagesFilterAudioDocuments();
|
||||
break;
|
||||
case 0x9fc00e65:
|
||||
result = new TL_inputMessagesFilterVideo();
|
||||
break;
|
||||
|
@ -11672,6 +11675,15 @@ public class TLRPC {
|
|||
}
|
||||
}
|
||||
|
||||
public static class TL_inputMessagesFilterAudioDocuments extends MessagesFilter {
|
||||
public static int constructor = 0x5afbf764;
|
||||
|
||||
|
||||
public void serializeToStream(AbsSerializedData stream) {
|
||||
stream.writeInt32(constructor);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TL_inputMessagesFilterVideo extends MessagesFilter {
|
||||
public static int constructor = 0x9fc00e65;
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ public class TcpConnection extends ConnectionContext {
|
|||
FileLog.e("tmessages", e2);
|
||||
}
|
||||
|
||||
FileLog.d("tmessages", String.format(TcpConnection.this + " Connecting (%s:%d)", hostAddress, hostPort));
|
||||
FileLog.d("tmessages", String.format(TcpConnection.this + " Connecting (%s:%d), connection class %d", hostAddress, hostPort, transportRequestClass));
|
||||
firstPacket = true;
|
||||
if (restOfTheData != null) {
|
||||
BuffersStorage.getInstance().reuseFreeBuffer(restOfTheData);
|
||||
|
|
|
@ -15,7 +15,6 @@ import android.os.Build;
|
|||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
@ -23,6 +22,7 @@ import android.widget.ImageView;
|
|||
import android.widget.TextView;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
|
||||
|
@ -38,7 +38,6 @@ public class ActionBar extends FrameLayout {
|
|||
}
|
||||
}
|
||||
|
||||
private FrameLayout titleFrameLayout;
|
||||
private ImageView backButtonImageView;
|
||||
private TextView titleTextView;
|
||||
private TextView subTitleTextView;
|
||||
|
@ -60,116 +59,6 @@ public class ActionBar extends FrameLayout {
|
|||
|
||||
public ActionBar(Context context) {
|
||||
super(context);
|
||||
titleFrameLayout = new FrameLayout(context);
|
||||
addView(titleFrameLayout);
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)titleFrameLayout.getLayoutParams();
|
||||
layoutParams.width = LayoutHelper.WRAP_CONTENT;
|
||||
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
titleFrameLayout.setLayoutParams(layoutParams);
|
||||
titleFrameLayout.setPadding(0, 0, AndroidUtilities.dp(4), 0);
|
||||
titleFrameLayout.setEnabled(false);
|
||||
}
|
||||
|
||||
private void positionBackImage(int height) {
|
||||
if (backButtonImageView != null) {
|
||||
LayoutParams layoutParams = (LayoutParams)backButtonImageView.getLayoutParams();
|
||||
layoutParams.width = AndroidUtilities.dp(54);
|
||||
layoutParams.height = height;
|
||||
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
backButtonImageView.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
private void positionTitle(int width, int height) {
|
||||
int offset = AndroidUtilities.dp(2);
|
||||
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
offset = AndroidUtilities.dp(1);
|
||||
}
|
||||
int maxTextWidth = 0;
|
||||
|
||||
LayoutParams layoutParams;
|
||||
|
||||
if (titleTextView != null && titleTextView.getVisibility() == VISIBLE) {
|
||||
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
|
||||
} else {
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
|
||||
}
|
||||
|
||||
layoutParams = (LayoutParams) titleTextView.getLayoutParams();
|
||||
layoutParams.width = LayoutHelper.WRAP_CONTENT;
|
||||
layoutParams.height = LayoutHelper.WRAP_CONTENT;
|
||||
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
titleTextView.setLayoutParams(layoutParams);
|
||||
titleTextView.measure(width, height);
|
||||
maxTextWidth = titleTextView.getMeasuredWidth();
|
||||
}
|
||||
if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) {
|
||||
if (!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
subTitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
} else {
|
||||
subTitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
}
|
||||
|
||||
layoutParams = (LayoutParams) subTitleTextView.getLayoutParams();
|
||||
layoutParams.width = LayoutHelper.WRAP_CONTENT;
|
||||
layoutParams.height = LayoutHelper.WRAP_CONTENT;
|
||||
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
subTitleTextView.setLayoutParams(layoutParams);
|
||||
subTitleTextView.measure(width, height);
|
||||
maxTextWidth = Math.max(maxTextWidth, subTitleTextView.getMeasuredWidth());
|
||||
}
|
||||
|
||||
int x;
|
||||
if (backButtonImageView != null && backButtonImageView.getVisibility() == VISIBLE) {
|
||||
x = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 80 : 72);
|
||||
} else {
|
||||
x = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 26 : 18);
|
||||
}
|
||||
|
||||
if (menu != null) {
|
||||
maxTextWidth = Math.min(maxTextWidth, width - menu.getMeasuredWidth() - AndroidUtilities.dp(16) - x);
|
||||
}
|
||||
|
||||
if (titleTextView != null && titleTextView.getVisibility() == VISIBLE) {
|
||||
layoutParams = (LayoutParams) titleTextView.getLayoutParams();
|
||||
layoutParams.width = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams.height = titleTextView.getMeasuredHeight();
|
||||
int y;
|
||||
if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) {
|
||||
y = (height / 2 - titleTextView.getMeasuredHeight()) / 2 + offset;
|
||||
} else {
|
||||
y = (height - titleTextView.getMeasuredHeight()) / 2 - AndroidUtilities.dp(1);
|
||||
}
|
||||
layoutParams.setMargins(x, y, 0, 0);
|
||||
titleTextView.setLayoutParams(layoutParams);
|
||||
}
|
||||
if (subTitleTextView != null && subTitleTextView.getVisibility() == VISIBLE) {
|
||||
layoutParams = (LayoutParams) subTitleTextView.getLayoutParams();
|
||||
layoutParams.width = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams.height = subTitleTextView.getMeasuredHeight();
|
||||
layoutParams.setMargins(x, height / 2 + (height / 2 - subTitleTextView.getMeasuredHeight()) / 2 - offset, 0, 0);
|
||||
subTitleTextView.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
MarginLayoutParams layoutParams1 = (MarginLayoutParams) titleFrameLayout.getLayoutParams();
|
||||
layoutParams1.width = x + maxTextWidth + (isSearchFieldVisible ? 0 : AndroidUtilities.dp(6));
|
||||
layoutParams1.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
|
||||
titleFrameLayout.setLayoutParams(layoutParams1);
|
||||
}
|
||||
|
||||
public void positionMenu(int width, int height) {
|
||||
if (menu == null) {
|
||||
return;
|
||||
}
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)menu.getLayoutParams();
|
||||
layoutParams.width = isSearchFieldVisible ? LayoutHelper.MATCH_PARENT : LayoutHelper.WRAP_CONTENT;
|
||||
layoutParams.height = height;
|
||||
layoutParams.leftMargin = isSearchFieldVisible ? AndroidUtilities.dp(AndroidUtilities.isTablet() ? 74 : 66) : 0;
|
||||
layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
|
||||
menu.setLayoutParams(layoutParams);
|
||||
menu.measure(width, height);
|
||||
}
|
||||
|
||||
private void createBackButtonImage() {
|
||||
|
@ -177,9 +66,10 @@ public class ActionBar extends FrameLayout {
|
|||
return;
|
||||
}
|
||||
backButtonImageView = new ImageView(getContext());
|
||||
titleFrameLayout.addView(backButtonImageView);
|
||||
backButtonImageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||
backButtonImageView.setBackgroundResource(itemsBackgroundResourceId);
|
||||
addView(backButtonImageView, LayoutHelper.createFrame(54, 54, Gravity.LEFT | Gravity.TOP));
|
||||
|
||||
backButtonImageView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
@ -195,31 +85,19 @@ public class ActionBar extends FrameLayout {
|
|||
}
|
||||
|
||||
public void setBackButtonDrawable(Drawable drawable) {
|
||||
boolean reposition = false;
|
||||
if (backButtonImageView == null) {
|
||||
createBackButtonImage();
|
||||
} else {
|
||||
reposition = true;
|
||||
}
|
||||
backButtonImageView.setVisibility(drawable == null ? GONE : VISIBLE);
|
||||
backButtonImageView.setImageDrawable(drawable);
|
||||
if (reposition) {
|
||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
public void setBackButtonImage(int resource) {
|
||||
boolean reposition = false;
|
||||
if (backButtonImageView == null) {
|
||||
createBackButtonImage();
|
||||
} else {
|
||||
reposition = true;
|
||||
}
|
||||
backButtonImageView.setVisibility(resource == 0 ? GONE : VISIBLE);
|
||||
backButtonImageView.setImageResource(resource);
|
||||
if (reposition) {
|
||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
private void createSubtitleTextView() {
|
||||
|
@ -227,13 +105,13 @@ public class ActionBar extends FrameLayout {
|
|||
return;
|
||||
}
|
||||
subTitleTextView = new TextView(getContext());
|
||||
titleFrameLayout.addView(subTitleTextView);
|
||||
subTitleTextView.setGravity(Gravity.LEFT);
|
||||
subTitleTextView.setTextColor(0xffd7e8f7);
|
||||
subTitleTextView.setSingleLine(true);
|
||||
subTitleTextView.setLines(1);
|
||||
subTitleTextView.setMaxLines(1);
|
||||
subTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
addView(subTitleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
|
||||
}
|
||||
|
||||
public void setSubtitle(CharSequence value) {
|
||||
|
@ -243,22 +121,6 @@ public class ActionBar extends FrameLayout {
|
|||
if (subTitleTextView != null) {
|
||||
subTitleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
||||
subTitleTextView.setText(value);
|
||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
public void setSubTitleIcon(int resourceId, Drawable drawable, int padding) {
|
||||
if ((resourceId != 0 || drawable != null) && subTitleTextView == null) {
|
||||
createSubtitleTextView();
|
||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
if (subTitleTextView != null) {
|
||||
if (drawable != null) {
|
||||
subTitleTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
|
||||
} else {
|
||||
subTitleTextView.setCompoundDrawablesWithIntrinsicBounds(resourceId, 0, 0, 0);
|
||||
}
|
||||
subTitleTextView.setCompoundDrawablePadding(padding);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,39 +130,32 @@ public class ActionBar extends FrameLayout {
|
|||
}
|
||||
titleTextView = new TextView(getContext());
|
||||
titleTextView.setGravity(Gravity.LEFT);
|
||||
titleTextView.setSingleLine(true);
|
||||
titleTextView.setLines(1);
|
||||
titleTextView.setMaxLines(1);
|
||||
titleTextView.setSingleLine(true);
|
||||
titleTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
titleFrameLayout.addView(titleTextView);
|
||||
titleTextView.setTextColor(0xffffffff);
|
||||
titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence value) {
|
||||
boolean created = false;
|
||||
if (value != null && titleTextView == null) {
|
||||
createTitleTextView();
|
||||
created = true;
|
||||
}
|
||||
if (titleTextView != null) {
|
||||
lastTitle = value;
|
||||
titleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
||||
titleTextView.setText(value);
|
||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
||||
if (!created) {
|
||||
titleTextView.setText(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTitleIcon(int resourceId, int padding) {
|
||||
if (resourceId != 0 && titleTextView == null) {
|
||||
createTitleTextView();
|
||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
titleTextView.setCompoundDrawablesWithIntrinsicBounds(resourceId, 0, 0, 0);
|
||||
titleTextView.setCompoundDrawablePadding(padding);
|
||||
public TextView getSubTitleTextView() {
|
||||
return subTitleTextView;
|
||||
}
|
||||
|
||||
public TextView getTitleTextView() {
|
||||
return titleTextView;
|
||||
}
|
||||
|
||||
public Drawable getSubTitleIcon() {
|
||||
|
@ -332,17 +187,6 @@ public class ActionBar extends FrameLayout {
|
|||
actionBarMenuOnItemClick = listener;
|
||||
}
|
||||
|
||||
public void setCustomView(int resourceId) {
|
||||
LayoutInflater li = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View view = li.inflate(resourceId, null);
|
||||
addView(view);
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams)view.getLayoutParams();
|
||||
layoutParams.width = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams.topMargin = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
|
||||
view.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
public ActionBarMenu createActionMode() {
|
||||
if (actionMode != null) {
|
||||
return actionMode;
|
||||
|
@ -358,7 +202,7 @@ public class ActionBar extends FrameLayout {
|
|||
actionMode.setLayoutParams(layoutParams);
|
||||
actionMode.setVisibility(INVISIBLE);
|
||||
|
||||
if (occupyStatusBar) {
|
||||
if (occupyStatusBar && actionModeTop == null) {
|
||||
actionModeTop = new View(getContext());
|
||||
actionModeTop.setBackgroundColor(0x99000000);
|
||||
addView(actionModeTop);
|
||||
|
@ -381,8 +225,14 @@ public class ActionBar extends FrameLayout {
|
|||
if (occupyStatusBar && actionModeTop != null) {
|
||||
actionModeTop.setVisibility(VISIBLE);
|
||||
}
|
||||
if (titleFrameLayout != null) {
|
||||
titleFrameLayout.setVisibility(INVISIBLE);
|
||||
if (titleTextView != null) {
|
||||
titleTextView.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (subTitleTextView != null) {
|
||||
subTitleTextView.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (backButtonImageView != null) {
|
||||
backButtonImageView.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (menu != null) {
|
||||
menu.setVisibility(INVISIBLE);
|
||||
|
@ -397,14 +247,33 @@ public class ActionBar extends FrameLayout {
|
|||
if (occupyStatusBar && actionModeTop != null) {
|
||||
actionModeTop.setVisibility(INVISIBLE);
|
||||
}
|
||||
if (titleFrameLayout != null) {
|
||||
titleFrameLayout.setVisibility(VISIBLE);
|
||||
if (titleTextView != null) {
|
||||
titleTextView.setVisibility(VISIBLE);
|
||||
}
|
||||
if (subTitleTextView != null) {
|
||||
subTitleTextView.setVisibility(VISIBLE);
|
||||
}
|
||||
if (backButtonImageView != null) {
|
||||
backButtonImageView.setVisibility(VISIBLE);
|
||||
}
|
||||
if (menu != null) {
|
||||
menu.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void showActionModeTop() {
|
||||
if (occupyStatusBar && actionModeTop == null) {
|
||||
actionModeTop = new View(getContext());
|
||||
actionModeTop.setBackgroundColor(0x99000000);
|
||||
addView(actionModeTop);
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) actionModeTop.getLayoutParams();
|
||||
layoutParams.height = AndroidUtilities.statusBarHeight;
|
||||
layoutParams.width = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
actionModeTop.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isActionModeShowed() {
|
||||
return actionMode != null && actionMode.getVisibility() == VISIBLE;
|
||||
}
|
||||
|
@ -439,12 +308,136 @@ public class ActionBar extends FrameLayout {
|
|||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int actionBarHeight = AndroidUtilities.getCurrentActionBarHeight();
|
||||
positionBackImage(actionBarHeight);
|
||||
positionMenu(MeasureSpec.getSize(widthMeasureSpec), actionBarHeight);
|
||||
positionTitle(MeasureSpec.getSize(widthMeasureSpec), actionBarHeight);
|
||||
actionBarHeight += occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(actionBarHeight + extraHeight, MeasureSpec.EXACTLY));
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int actionBarHeight = getCurrentActionBarHeight();
|
||||
int actionBarHeightSpec = MeasureSpec.makeMeasureSpec(actionBarHeight, MeasureSpec.EXACTLY);
|
||||
|
||||
setMeasuredDimension(width, actionBarHeight + extraHeight + (occupyStatusBar ? AndroidUtilities.statusBarHeight : 0));
|
||||
|
||||
int textLeft;
|
||||
if (backButtonImageView != null && backButtonImageView.getVisibility() != GONE) {
|
||||
backButtonImageView.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(54), MeasureSpec.EXACTLY), actionBarHeightSpec);
|
||||
textLeft = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 80 : 72);
|
||||
} else {
|
||||
textLeft = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 26 : 18);
|
||||
}
|
||||
|
||||
if (menu != null && menu.getVisibility() != GONE) {
|
||||
int menuWidth;
|
||||
if (isSearchFieldVisible) {
|
||||
menuWidth = MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(AndroidUtilities.isTablet() ? 74 : 66), MeasureSpec.EXACTLY);
|
||||
} else {
|
||||
menuWidth = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
|
||||
}
|
||||
menu.measure(menuWidth, actionBarHeightSpec);
|
||||
}
|
||||
|
||||
if (titleTextView != null && titleTextView.getVisibility() != GONE || subTitleTextView != null && subTitleTextView.getVisibility() != GONE) {
|
||||
int availableWidth = width - (menu != null ? menu.getMeasuredWidth() : 0) - AndroidUtilities.dp(16) - textLeft;
|
||||
|
||||
if (titleTextView != null && titleTextView.getVisibility() != GONE) {
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, !AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 18 : 20);
|
||||
titleTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(actionBarHeight, MeasureSpec.AT_MOST));
|
||||
|
||||
}
|
||||
if (subTitleTextView != null && subTitleTextView.getVisibility() != GONE) {
|
||||
subTitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, !AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 14 : 16);
|
||||
subTitleTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(actionBarHeight, MeasureSpec.AT_MOST));
|
||||
}
|
||||
}
|
||||
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child.getVisibility() == GONE || child == titleTextView || child == subTitleTextView || child == menu || child == backButtonImageView) {
|
||||
continue;
|
||||
}
|
||||
measureChildWithMargins(child, widthMeasureSpec, 0, MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
int additionalTop = occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
|
||||
|
||||
int textLeft;
|
||||
if (backButtonImageView != null && backButtonImageView.getVisibility() != GONE) {
|
||||
backButtonImageView.layout(0, additionalTop, backButtonImageView.getMeasuredWidth(), additionalTop + backButtonImageView.getMeasuredHeight());
|
||||
textLeft = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 80 : 72);
|
||||
} else {
|
||||
textLeft = AndroidUtilities.dp(AndroidUtilities.isTablet() ? 26 : 18);
|
||||
}
|
||||
|
||||
if (menu != null && menu.getVisibility() != GONE) {
|
||||
int menuLeft = isSearchFieldVisible ? AndroidUtilities.dp(AndroidUtilities.isTablet() ? 74 : 66) : (right - left) - menu.getMeasuredWidth();
|
||||
menu.layout(menuLeft, additionalTop, menuLeft + menu.getMeasuredWidth(), additionalTop + menu.getMeasuredHeight());
|
||||
}
|
||||
|
||||
int offset = AndroidUtilities.dp(!AndroidUtilities.isTablet() && getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? 1 : 2);
|
||||
if (titleTextView != null && titleTextView.getVisibility() != GONE) {
|
||||
int textTop;
|
||||
if (subTitleTextView != null && subTitleTextView.getVisibility() != GONE) {
|
||||
textTop = (getCurrentActionBarHeight() / 2 - titleTextView.getMeasuredHeight()) / 2 + offset;
|
||||
} else {
|
||||
textTop = (getCurrentActionBarHeight() - titleTextView.getMeasuredHeight()) / 2 - AndroidUtilities.dp(1);
|
||||
}
|
||||
titleTextView.layout(textLeft, additionalTop + textTop, textLeft + titleTextView.getMeasuredWidth(), additionalTop + textTop + titleTextView.getMeasuredHeight());
|
||||
}
|
||||
if (subTitleTextView != null && subTitleTextView.getVisibility() != GONE) {
|
||||
int textTop = getCurrentActionBarHeight() / 2 + (getCurrentActionBarHeight() / 2 - subTitleTextView.getMeasuredHeight()) / 2 - offset;
|
||||
subTitleTextView.layout(textLeft, additionalTop + textTop, textLeft + subTitleTextView.getMeasuredWidth(), additionalTop + textTop + subTitleTextView.getMeasuredHeight());
|
||||
}
|
||||
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child.getVisibility() == GONE || child == titleTextView || child == subTitleTextView || child == menu || child == backButtonImageView) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
|
||||
int width = child.getMeasuredWidth();
|
||||
int height = child.getMeasuredHeight();
|
||||
int childLeft;
|
||||
int childTop;
|
||||
|
||||
int gravity = lp.gravity;
|
||||
if (gravity == -1) {
|
||||
gravity = Gravity.TOP | Gravity.LEFT;
|
||||
}
|
||||
|
||||
final int absoluteGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
|
||||
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
|
||||
|
||||
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
|
||||
case Gravity.CENTER_HORIZONTAL:
|
||||
childLeft = (right - left - width) / 2 + lp.leftMargin - lp.rightMargin;
|
||||
break;
|
||||
case Gravity.RIGHT:
|
||||
childLeft = right - width - lp.rightMargin;
|
||||
break;
|
||||
case Gravity.LEFT:
|
||||
default:
|
||||
childLeft = lp.leftMargin;
|
||||
}
|
||||
|
||||
switch (verticalGravity) {
|
||||
case Gravity.TOP:
|
||||
childTop = lp.topMargin;
|
||||
break;
|
||||
case Gravity.CENTER_VERTICAL:
|
||||
childTop = (bottom - top - height) / 2 + lp.topMargin - lp.bottomMargin;
|
||||
break;
|
||||
case Gravity.BOTTOM:
|
||||
childTop = (bottom - top) - height - lp.bottomMargin;
|
||||
break;
|
||||
default:
|
||||
childTop = lp.topMargin;
|
||||
}
|
||||
child.layout(childLeft, childTop, childLeft + width, childTop + height);
|
||||
}
|
||||
}
|
||||
|
||||
public void onMenuButtonPressed() {
|
||||
|
@ -474,10 +467,13 @@ public class ActionBar extends FrameLayout {
|
|||
if (titleTextView != null) {
|
||||
titleTextView.setVisibility(textToSet != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
||||
titleTextView.setText(textToSet);
|
||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSearchFieldVisible() {
|
||||
return isSearchFieldVisible;
|
||||
}
|
||||
|
||||
public void setExtraHeight(int value, boolean layout) {
|
||||
extraHeight = value;
|
||||
if (layout) {
|
||||
|
@ -520,4 +516,14 @@ public class ActionBar extends FrameLayout {
|
|||
super.onTouchEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int getCurrentActionBarHeight() {
|
||||
if (AndroidUtilities.isTablet()) {
|
||||
return AndroidUtilities.dp(64);
|
||||
} else if (ApplicationLoader.applicationContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
return AndroidUtilities.dp(48);
|
||||
} else {
|
||||
return AndroidUtilities.dp(56);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ public class ActionBarLayout extends FrameLayout {
|
|||
if (child instanceof ActionBar) {
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
} else {
|
||||
boolean wasActionBar = false;
|
||||
//boolean wasActionBar = false;
|
||||
int actionBarHeight = 0;
|
||||
int childCount = getChildCount();
|
||||
for (int a = 0; a < childCount; a++) {
|
||||
|
@ -339,7 +339,7 @@ public class ActionBarLayout extends FrameLayout {
|
|||
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 2);
|
||||
View fragmentView = lastFragment.fragmentView;
|
||||
if (fragmentView == null) {
|
||||
fragmentView = lastFragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
||||
fragmentView = lastFragment.createView(parentActivity);
|
||||
} else {
|
||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||
if (parent != null) {
|
||||
|
@ -625,7 +625,7 @@ public class ActionBarLayout extends FrameLayout {
|
|||
fragment.setParentLayout(this);
|
||||
View fragmentView = fragment.fragmentView;
|
||||
if (fragmentView == null) {
|
||||
fragmentView = fragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
||||
fragmentView = fragment.createView(parentActivity);
|
||||
} else {
|
||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||
if (parent != null) {
|
||||
|
@ -829,7 +829,7 @@ public class ActionBarLayout extends FrameLayout {
|
|||
previousFragment.setParentLayout(this);
|
||||
View fragmentView = previousFragment.fragmentView;
|
||||
if (fragmentView == null) {
|
||||
fragmentView = previousFragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
||||
fragmentView = previousFragment.createView(parentActivity);
|
||||
} else {
|
||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||
if (parent != null) {
|
||||
|
@ -972,7 +972,7 @@ public class ActionBarLayout extends FrameLayout {
|
|||
previousFragment.setParentLayout(this);
|
||||
View fragmentView = previousFragment.fragmentView;
|
||||
if (fragmentView == null) {
|
||||
fragmentView = previousFragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
||||
fragmentView = previousFragment.createView(parentActivity);
|
||||
} else {
|
||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||
if (parent != null) {
|
||||
|
|
|
@ -36,7 +36,7 @@ public class ActionBarMenu extends LinearLayout {
|
|||
View view = li.inflate(resourceId, null);
|
||||
view.setTag(id);
|
||||
addView(view);
|
||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)view.getLayoutParams();
|
||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) view.getLayoutParams();
|
||||
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
||||
view.setBackgroundResource(parentActionBar.itemsBackgroundResourceId);
|
||||
view.setLayoutParams(layoutParams);
|
||||
|
@ -74,14 +74,14 @@ public class ActionBarMenu extends LinearLayout {
|
|||
menuItem.iconView.setImageResource(icon);
|
||||
}
|
||||
addView(menuItem);
|
||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)menuItem.getLayoutParams();
|
||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) menuItem.getLayoutParams();
|
||||
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams.width = width;
|
||||
menuItem.setLayoutParams(layoutParams);
|
||||
menuItem.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
||||
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||
if (item.hasSubMenu()) {
|
||||
if (parentActionBar.actionBarMenuOnItemClick.canOpenMenu()) {
|
||||
item.toggleSubMenu();
|
||||
|
@ -89,7 +89,7 @@ public class ActionBarMenu extends LinearLayout {
|
|||
} else if (item.isSearchField()) {
|
||||
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
||||
} else {
|
||||
onItemClick((Integer)view.getTag());
|
||||
onItemClick((Integer) view.getTag());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -100,7 +100,7 @@ public class ActionBarMenu extends LinearLayout {
|
|||
for (int a = 0; a < getChildCount(); a++) {
|
||||
View view = getChildAt(a);
|
||||
if (view instanceof ActionBarMenuItem) {
|
||||
((ActionBarMenuItem)view).closeSubMenu();
|
||||
((ActionBarMenuItem) view).closeSubMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,10 +122,16 @@ public class ActionBarMenu extends LinearLayout {
|
|||
for (int a = 0; a < getChildCount(); a++) {
|
||||
View view = getChildAt(a);
|
||||
if (view instanceof ActionBarMenuItem) {
|
||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
||||
if (item.hasSubMenu() && item.getVisibility() == VISIBLE) {
|
||||
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||
if (item.getVisibility() != VISIBLE) {
|
||||
continue;
|
||||
}
|
||||
if (item.hasSubMenu()) {
|
||||
item.toggleSubMenu();
|
||||
break;
|
||||
} else if (item.overrideMenuClick) {
|
||||
onItemClick((Integer) item.getTag());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,7 +141,7 @@ public class ActionBarMenu extends LinearLayout {
|
|||
for (int a = 0; a < getChildCount(); a++) {
|
||||
View view = getChildAt(a);
|
||||
if (view instanceof ActionBarMenuItem) {
|
||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
||||
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||
if (item.isSearchField()) {
|
||||
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
||||
break;
|
||||
|
@ -148,7 +154,7 @@ public class ActionBarMenu extends LinearLayout {
|
|||
for (int a = 0; a < getChildCount(); a++) {
|
||||
View view = getChildAt(a);
|
||||
if (view instanceof ActionBarMenuItem) {
|
||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
||||
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||
if (item.isSearchField()) {
|
||||
if (toggle) {
|
||||
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
||||
|
@ -164,7 +170,7 @@ public class ActionBarMenu extends LinearLayout {
|
|||
public ActionBarMenuItem getItem(int id) {
|
||||
View v = findViewWithTag(id);
|
||||
if (v instanceof ActionBarMenuItem) {
|
||||
return (ActionBarMenuItem)v;
|
||||
return (ActionBarMenuItem) v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
private int subMenuOpenSide = 0;
|
||||
private ActionBarMenuItemDelegate delegate;
|
||||
private boolean allowCloseAnimation = true;
|
||||
protected boolean overrideMenuClick;
|
||||
private boolean processedPopupClick;
|
||||
|
||||
public ActionBarMenuItem(Context context, ActionBarMenu menu, int background) {
|
||||
super(context);
|
||||
|
@ -129,21 +131,23 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
x -= location[0];
|
||||
y -= location[1];
|
||||
selectedMenuView = null;
|
||||
for (int a = 0; a < popupLayout.getChildCount(); a++) {
|
||||
View child = popupLayout.getChildAt(a);
|
||||
for (int a = 0; a < popupLayout.getItemsCount(); a++) {
|
||||
View child = popupLayout.getItemAt(a);
|
||||
child.getHitRect(rect);
|
||||
if ((Integer) child.getTag() < 100) {
|
||||
if (!rect.contains((int) x, (int) y)) {
|
||||
child.setPressed(false);
|
||||
child.setSelected(false);
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
if (Build.VERSION.SDK_INT == 21) {
|
||||
child.getBackground().setVisible(false, false);
|
||||
}
|
||||
} else {
|
||||
child.setPressed(true);
|
||||
child.setSelected(true);
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
child.getBackground().setVisible(true, false);
|
||||
if (Build.VERSION.SDK_INT == 21) {
|
||||
child.getBackground().setVisible(true, false);
|
||||
}
|
||||
child.drawableHotspotChanged(x, y - child.getTop());
|
||||
}
|
||||
selectedMenuView = child;
|
||||
|
@ -192,9 +196,6 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
rect = new Rect();
|
||||
location = new int[2];
|
||||
popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getContext());
|
||||
popupLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
popupLayout.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8));
|
||||
//popupLayout.setBackgroundResource(R.drawable.popup_fixed);
|
||||
popupLayout.setOnTouchListener(new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
|
@ -252,6 +253,10 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
@Override
|
||||
public void onClick(View view) {
|
||||
if (popupWindow != null && popupWindow.isShowing()) {
|
||||
if (processedPopupClick) {
|
||||
return;
|
||||
}
|
||||
processedPopupClick = true;
|
||||
popupWindow.dismiss(allowCloseAnimation);
|
||||
}
|
||||
if (parentMenu != null) {
|
||||
|
@ -306,6 +311,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
}
|
||||
});
|
||||
}
|
||||
processedPopupClick = false;
|
||||
popupWindow.setFocusable(true);
|
||||
if (popupLayout.getMeasuredWidth() == 0) {
|
||||
updateOrShowPopup(true, true);
|
||||
|
@ -367,6 +373,11 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
return setIsSearchField(value, true);
|
||||
}
|
||||
|
||||
public ActionBarMenuItem setOverrideMenuClick(boolean value) {
|
||||
overrideMenuClick = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ActionBarMenuItem setIsSearchField(boolean value, boolean needClearButton) {
|
||||
if (parentMenu == null) {
|
||||
return this;
|
||||
|
@ -389,7 +400,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
searchField.setSingleLine(true);
|
||||
searchField.setBackgroundResource(0);
|
||||
searchField.setPadding(0, 0, 0, 0);
|
||||
searchField.setInputType(EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
int inputType = searchField.getInputType() | EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
||||
searchField.setInputType(inputType);
|
||||
if (android.os.Build.VERSION.SDK_INT < 11) {
|
||||
searchField.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
|
||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||
|
@ -467,7 +479,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
layoutParams2.width = LayoutHelper.MATCH_PARENT;
|
||||
layoutParams2.gravity = Gravity.CENTER_VERTICAL;
|
||||
layoutParams2.height = AndroidUtilities.dp(36);
|
||||
layoutParams2.rightMargin = AndroidUtilities.dp(48);
|
||||
layoutParams2.rightMargin = needClearButton ? AndroidUtilities.dp(48) : 0;
|
||||
searchField.setLayoutParams(layoutParams2);
|
||||
|
||||
if (needClearButton) {
|
||||
|
@ -532,6 +544,10 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
}
|
||||
}
|
||||
|
||||
if (show) {
|
||||
popupLayout.scrollToTop();
|
||||
}
|
||||
|
||||
if (subMenuOpenSide == 0) {
|
||||
if (showFromBottom) {
|
||||
if (show) {
|
||||
|
@ -574,10 +590,6 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
if (view != null) {
|
||||
view.setVisibility(GONE);
|
||||
}
|
||||
view = popupLayout.findViewWithTag(100 + id);
|
||||
if (view != null) {
|
||||
view.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void showSubItem(int id) {
|
||||
|
@ -585,9 +597,5 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
|||
if (view != null) {
|
||||
view.setVisibility(VISIBLE);
|
||||
}
|
||||
view = popupLayout.findViewWithTag(100 + id);
|
||||
if (view != null) {
|
||||
view.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,14 +19,18 @@ import android.graphics.drawable.Drawable;
|
|||
import android.os.Build;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
|
@ -62,7 +66,7 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
void onDispatchKeyEvent(KeyEvent keyEvent);
|
||||
}
|
||||
|
||||
public static class ActionBarPopupWindowLayout extends LinearLayout {
|
||||
public static class ActionBarPopupWindowLayout extends FrameLayout {
|
||||
|
||||
private OnDispatchKeyEventListener mOnDispatchKeyEventListener;
|
||||
protected static Drawable backgroundDrawable;
|
||||
|
@ -73,13 +77,26 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
private boolean showedFromBotton;
|
||||
private HashMap<View, Integer> positions = new HashMap<>();
|
||||
|
||||
private ScrollView scrollView;
|
||||
private LinearLayout linearLayout;
|
||||
|
||||
public ActionBarPopupWindowLayout(Context context) {
|
||||
super(context);
|
||||
setWillNotDraw(false);
|
||||
|
||||
if (backgroundDrawable == null) {
|
||||
backgroundDrawable = getResources().getDrawable(R.drawable.popup_fixed);
|
||||
}
|
||||
|
||||
setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8));
|
||||
setWillNotDraw(false);
|
||||
|
||||
scrollView = new ScrollView(context);
|
||||
scrollView.setVerticalScrollBarEnabled(false);
|
||||
addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
|
||||
|
||||
linearLayout = new LinearLayout(context);
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
scrollView.addView(linearLayout, new ScrollView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
|
||||
public void setShowedFromBotton(boolean value) {
|
||||
|
@ -106,15 +123,15 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
public void setBackScaleY(float value) {
|
||||
backScaleY = value;
|
||||
if (animationEnabled) {
|
||||
int count = getChildCount();
|
||||
int count = getItemsCount();
|
||||
int visibleCount = 0;
|
||||
for (int a = 0; a < count; a++) {
|
||||
visibleCount += getChildAt(a).getVisibility() == VISIBLE ? 1 : 0;
|
||||
visibleCount += getItemAt(a).getVisibility() == VISIBLE ? 1 : 0;
|
||||
}
|
||||
int height = getMeasuredHeight() - AndroidUtilities.dp(16);
|
||||
if (showedFromBotton) {
|
||||
for (int a = lastStartedChild; a >= 0; a--) {
|
||||
View child = getChildAt(a);
|
||||
View child = getItemAt(a);
|
||||
if (child.getVisibility() != VISIBLE) {
|
||||
continue;
|
||||
}
|
||||
|
@ -127,7 +144,7 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
}
|
||||
} else {
|
||||
for (int a = lastStartedChild; a < count; a++) {
|
||||
View child = getChildAt(a);
|
||||
View child = getItemAt(a);
|
||||
if (child.getVisibility() != VISIBLE) {
|
||||
continue;
|
||||
}
|
||||
|
@ -155,6 +172,11 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addView(View child) {
|
||||
linearLayout.addView(child);
|
||||
}
|
||||
|
||||
public float getBackScaleX() {
|
||||
return backScaleX;
|
||||
}
|
||||
|
@ -183,6 +205,18 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
backgroundDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
public int getItemsCount() {
|
||||
return linearLayout.getChildCount();
|
||||
}
|
||||
|
||||
public View getItemAt(int index) {
|
||||
return linearLayout.getChildAt(index);
|
||||
}
|
||||
|
||||
public void scrollToTop() {
|
||||
scrollView.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public ActionBarPopupWindow() {
|
||||
|
@ -269,11 +303,11 @@ public class ActionBarPopupWindow extends PopupWindow {
|
|||
content.setAlpha(1.0f);
|
||||
content.setPivotX(content.getMeasuredWidth());
|
||||
content.setPivotY(0);
|
||||
int count = content.getChildCount();
|
||||
int count = content.getItemsCount();
|
||||
content.positions.clear();
|
||||
int visibleCount = 0;
|
||||
for (int a = 0; a < count; a++) {
|
||||
View child = content.getChildAt(a);
|
||||
View child = content.getItemAt(a);
|
||||
if (child.getVisibility() != View.VISIBLE) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
|
@ -44,7 +43,7 @@ public class BaseFragment {
|
|||
classGuid = ConnectionsManager.getInstance().generateClassGuid();
|
||||
}
|
||||
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,10 +9,18 @@
|
|||
package org.telegram.ui.ActionBar;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
@ -45,7 +53,7 @@ import java.util.ArrayList;
|
|||
|
||||
public class BottomSheet extends Dialog {
|
||||
|
||||
private LinearLayout linearLayout;
|
||||
private LinearLayout containerView;
|
||||
private FrameLayout container;
|
||||
|
||||
private boolean dismissed;
|
||||
|
@ -57,24 +65,73 @@ public class BottomSheet extends Dialog {
|
|||
private int[] itemIcons;
|
||||
private View customView;
|
||||
private CharSequence title;
|
||||
private boolean overrideTabletWidth = true;
|
||||
private boolean fullWidth;
|
||||
private boolean isGrid;
|
||||
private ColorDrawable backgroundDrawable = new ColorDrawable(0xff000000);
|
||||
private static Drawable shadowDrawable;
|
||||
|
||||
private Paint ciclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
private static int backgroundPaddingTop;
|
||||
private static int backgroundPaddingLeft;
|
||||
|
||||
private boolean useRevealAnimation;
|
||||
private float revealRadius;
|
||||
private int revealX;
|
||||
private int revealY;
|
||||
private boolean useRevealAnimation;
|
||||
private boolean applyTopPaddings = true;
|
||||
|
||||
private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
|
||||
private AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
|
||||
|
||||
private ArrayList<BottomSheetCell> itemViews = new ArrayList<>();
|
||||
|
||||
private BottomSheetDelegate delegate;
|
||||
private BottomSheetDelegateInterface delegate;
|
||||
|
||||
public interface BottomSheetDelegate {
|
||||
public interface BottomSheetDelegateInterface {
|
||||
void onOpenAnimationStart();
|
||||
|
||||
void onOpenAnimationEnd();
|
||||
|
||||
void onRevealAnimationStart(boolean open);
|
||||
|
||||
void onRevealAnimationEnd(boolean open);
|
||||
|
||||
void onRevealAnimationProgress(boolean open, float radius, int x, int y);
|
||||
|
||||
View getRevealView();
|
||||
}
|
||||
|
||||
public static class BottomSheetDelegate implements BottomSheetDelegateInterface {
|
||||
@Override
|
||||
public void onOpenAnimationStart() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpenAnimationEnd() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealAnimationStart(boolean open) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealAnimationEnd(boolean open) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealAnimationProgress(boolean open, float radius, int x, int y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getRevealView() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class BottomSheetCell extends FrameLayout {
|
||||
|
@ -139,7 +196,40 @@ public class BottomSheet extends Dialog {
|
|||
public BottomSheet(Context context) {
|
||||
super(context);
|
||||
|
||||
container = new FrameLayout(getContext());
|
||||
container = new FrameLayout(getContext()) {
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
setMeasuredDimension(width, height);
|
||||
boolean isPortrait = width < height;
|
||||
|
||||
if (containerView != null) {
|
||||
int left = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingLeft;
|
||||
if (!fullWidth) {
|
||||
if (AndroidUtilities.isTablet()) {
|
||||
int side = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.8f);
|
||||
containerView.measure(MeasureSpec.makeMeasureSpec(side + left * 2, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
|
||||
} else {
|
||||
int maxWidth = Math.min(AndroidUtilities.dp(480), width);
|
||||
containerView.measure(isPortrait ? MeasureSpec.makeMeasureSpec(width + left * 2, MeasureSpec.EXACTLY) : MeasureSpec.makeMeasureSpec((int) Math.max(width * 0.8f, maxWidth) + left * 2, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
|
||||
}
|
||||
} else {
|
||||
containerView.measure(MeasureSpec.makeMeasureSpec(width + left * 2, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
if (containerView != null) {
|
||||
int l = ((right - left) - containerView.getMeasuredWidth()) / 2;
|
||||
int t = (bottom - top) - containerView.getMeasuredHeight();
|
||||
containerView.layout(l, t, l + containerView.getMeasuredWidth(), t + getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
};
|
||||
container.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
|
@ -159,25 +249,37 @@ public class BottomSheet extends Dialog {
|
|||
window.requestFeature(Window.FEATURE_NO_TITLE);
|
||||
window.setWindowAnimations(R.style.DialogNoAnimation);
|
||||
|
||||
setContentView(container);
|
||||
|
||||
linearLayout = new LinearLayout(getContext());
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
if (AndroidUtilities.isTablet() && !overrideTabletWidth) {
|
||||
container.addView(linearLayout, 0, LayoutHelper.createFrame(320, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL));
|
||||
} else {
|
||||
container.addView(linearLayout, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM));
|
||||
if (shadowDrawable == null) {
|
||||
Rect padding = new Rect();
|
||||
shadowDrawable = getContext().getResources().getDrawable(R.drawable.sheet_shadow);
|
||||
shadowDrawable.getPadding(padding);
|
||||
backgroundPaddingLeft = padding.left;
|
||||
backgroundPaddingTop = padding.top;
|
||||
}
|
||||
|
||||
View shadow = new View(getContext());
|
||||
shadow.setBackgroundResource(R.drawable.header_shadow_reverse);
|
||||
linearLayout.addView(shadow, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 3));
|
||||
setContentView(container, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
LinearLayout containerView = new LinearLayout(getContext());
|
||||
containerView.setBackgroundColor(0xffffffff);
|
||||
ciclePaint.setColor(0xffffffff);
|
||||
|
||||
containerView = new LinearLayout(getContext()) {
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (useRevealAnimation && Build.VERSION.SDK_INT <= 19) {
|
||||
canvas.drawCircle(revealX, revealY, revealRadius, ciclePaint);
|
||||
//shadowDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
//shadowDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
};
|
||||
containerView.setWillNotDraw(false);
|
||||
containerView.setOrientation(LinearLayout.VERTICAL);
|
||||
containerView.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(isGrid ? 16 : 8));
|
||||
linearLayout.addView(containerView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
|
||||
container.addView(containerView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM));
|
||||
|
||||
if (title != null) {
|
||||
TextView titleView = new TextView(getContext());
|
||||
|
@ -273,104 +375,179 @@ public class BottomSheet extends Dialog {
|
|||
setOnShowListener(new OnShowListener() {
|
||||
@Override
|
||||
public void onShow(DialogInterface dialog) {
|
||||
if (useRevealAnimation) {
|
||||
int finalRadius = Math.max(AndroidUtilities.displaySize.x, container.getHeight());
|
||||
Animator anim = ViewAnimationUtils.createCircularReveal(container, revealX, revealY, 0, finalRadius);
|
||||
anim.setDuration(400);
|
||||
anim.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
if (delegate != null) {
|
||||
delegate.onOpenAnimationStart();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (delegate != null) {
|
||||
delegate.onOpenAnimationEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
|
||||
}
|
||||
});
|
||||
anim.start();
|
||||
} else {
|
||||
//startLayoutAnimation(true, true);
|
||||
ViewProxy.setTranslationY(linearLayout, linearLayout.getHeight());
|
||||
backgroundDrawable.setAlpha(0);
|
||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||
animatorSetProxy.playTogether(
|
||||
ObjectAnimatorProxy.ofFloat(linearLayout, "translationY", 0),
|
||||
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 51));
|
||||
animatorSetProxy.setDuration(200);
|
||||
animatorSetProxy.setStartDelay(20);
|
||||
animatorSetProxy.setInterpolator(new DecelerateInterpolator());
|
||||
animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() {
|
||||
@Override
|
||||
public void onAnimationEnd(Object animation) {
|
||||
if (delegate != null) {
|
||||
delegate.onOpenAnimationEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
animatorSetProxy.start();
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
startOpenAnimation();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private float animationProgress;
|
||||
private long lastFrameTime;
|
||||
private void startLayoutAnimation(final boolean open, final boolean first) {
|
||||
if (first) {
|
||||
animationProgress = 0.0f;
|
||||
lastFrameTime = System.nanoTime() / 1000000;
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
container.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
dismissed = false;
|
||||
if (Build.VERSION.SDK_INT >= 21 || !useRevealAnimation) {
|
||||
containerView.setBackgroundDrawable(shadowDrawable);
|
||||
} else {
|
||||
containerView.setBackgroundDrawable(null);
|
||||
}
|
||||
int left = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingLeft;
|
||||
int top = useRevealAnimation && Build.VERSION.SDK_INT <= 19 ? 0 : backgroundPaddingTop;
|
||||
containerView.setPadding(left, (applyTopPaddings ? AndroidUtilities.dp(8) : 0) + top, left, (applyTopPaddings ? AndroidUtilities.dp(isGrid ? 16 : 8) : 0));
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
startOpenAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setRevealRadius(float radius) {
|
||||
revealRadius = radius;
|
||||
delegate.onRevealAnimationProgress(!dismissed, radius, revealX, revealY);
|
||||
if (Build.VERSION.SDK_INT <= 19) {
|
||||
containerView.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
protected float getRevealRadius() {
|
||||
return revealRadius;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private void startRevealAnimation(final boolean open) {
|
||||
|
||||
if (open) {
|
||||
backgroundDrawable.setAlpha(0);
|
||||
containerView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
backgroundDrawable.setAlpha(51);
|
||||
}
|
||||
ViewProxy.setTranslationY(containerView, 0);
|
||||
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
|
||||
View view = delegate.getRevealView();
|
||||
if (view.getVisibility() == View.VISIBLE && ((ViewGroup) view.getParent()).getVisibility() == View.VISIBLE) {
|
||||
final int coords[] = new int[2];
|
||||
view.getLocationInWindow(coords);
|
||||
float top;
|
||||
if (Build.VERSION.SDK_INT <= 19) {
|
||||
top = AndroidUtilities.displaySize.y - containerView.getMeasuredHeight() - AndroidUtilities.statusBarHeight;
|
||||
} else {
|
||||
top = containerView.getY();
|
||||
}
|
||||
revealX = coords[0] + view.getMeasuredWidth() / 2;
|
||||
revealY = (int) (coords[1] + view.getMeasuredHeight() / 2 - top);
|
||||
if (Build.VERSION.SDK_INT <= 19) {
|
||||
revealY -= AndroidUtilities.statusBarHeight;
|
||||
}
|
||||
} else {
|
||||
revealX = AndroidUtilities.displaySize.x / 2 + backgroundPaddingLeft;
|
||||
revealY = (int) (AndroidUtilities.displaySize.y - containerView.getY());
|
||||
}
|
||||
|
||||
int corners[][] = new int[][]{
|
||||
{0, 0},
|
||||
{0, containerView.getMeasuredHeight()},
|
||||
{containerView.getMeasuredWidth(), 0},
|
||||
{containerView.getMeasuredWidth(), containerView.getMeasuredHeight()}
|
||||
};
|
||||
int finalRevealRadius = 0;
|
||||
for (int a = 0; a < 4; a++) {
|
||||
finalRevealRadius = Math.max(finalRevealRadius, (int) Math.ceil(Math.sqrt((revealX - corners[a][0]) * (revealX - corners[a][0]) + (revealY - corners[a][1]) * (revealY - corners[a][1]))));
|
||||
}
|
||||
|
||||
ArrayList<Animator> animators = new ArrayList<>(3);
|
||||
animators.add(ObjectAnimator.ofFloat(this, "revealRadius", open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0));
|
||||
animators.add(ObjectAnimator.ofInt(backgroundDrawable, "alpha", open ? 51 : 0));
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
containerView.setElevation(AndroidUtilities.dp(10));
|
||||
animators.add(ViewAnimationUtils.createCircularReveal(containerView, revealX <= containerView.getMeasuredWidth() ? revealX : containerView.getMeasuredWidth(), revealY, open ? 0 : finalRevealRadius, open ? finalRevealRadius : 0));
|
||||
animatorSet.setDuration(300);
|
||||
} else {
|
||||
if (!open) {
|
||||
animatorSet.setDuration(200);
|
||||
containerView.setPivotX(revealX <= containerView.getMeasuredWidth() ? revealX : containerView.getMeasuredWidth());
|
||||
containerView.setPivotY(revealY);
|
||||
animators.add(ObjectAnimator.ofFloat(containerView, "scaleX", 0.0f));
|
||||
animators.add(ObjectAnimator.ofFloat(containerView, "scaleY", 0.0f));
|
||||
animators.add(ObjectAnimator.ofFloat(containerView, "alpha", 0.0f));
|
||||
} else {
|
||||
animatorSet.setDuration(250);
|
||||
containerView.setScaleX(1);
|
||||
containerView.setScaleY(1);
|
||||
containerView.setAlpha(1);
|
||||
if (Build.VERSION.SDK_INT <= 19) {
|
||||
animatorSet.setStartDelay(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
animatorSet.playTogether(animators);
|
||||
animatorSet.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void run() {
|
||||
long newTime = System.nanoTime() / 1000000;
|
||||
long dt = newTime - lastFrameTime;
|
||||
FileLog.e("tmessages", "dt = " + dt);
|
||||
if (dt > 16) {
|
||||
dt = 16;
|
||||
}
|
||||
lastFrameTime = newTime;
|
||||
animationProgress += dt / 200.0f;
|
||||
if (animationProgress > 1.0f) {
|
||||
animationProgress = 1.0f;
|
||||
public void onAnimationStart(Animator animation) {
|
||||
if (delegate != null) {
|
||||
delegate.onRevealAnimationStart(open);
|
||||
}
|
||||
}
|
||||
|
||||
if (open) {
|
||||
float interpolated = decelerateInterpolator.getInterpolation(animationProgress);
|
||||
ViewProxy.setTranslationY(linearLayout, linearLayout.getHeight() * (1.0f - interpolated));
|
||||
backgroundDrawable.setAlpha((int) (51 * interpolated));
|
||||
} else {
|
||||
float interpolated = accelerateInterpolator.getInterpolation(animationProgress);
|
||||
ViewProxy.setTranslationY(linearLayout, linearLayout.getHeight() * interpolated);
|
||||
backgroundDrawable.setAlpha((int) (51 * (1.0f - interpolated)));
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (delegate != null) {
|
||||
delegate.onRevealAnimationEnd(open);
|
||||
}
|
||||
if (animationProgress < 1) {
|
||||
startLayoutAnimation(open, false);
|
||||
} else {
|
||||
if (open && delegate != null) {
|
||||
delegate.onOpenAnimationEnd();
|
||||
containerView.invalidate();
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
container.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
if (!open) {
|
||||
containerView.setVisibility(View.INVISIBLE);
|
||||
try {
|
||||
BottomSheet.super.dismiss();
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
onAnimationEnd(animation);
|
||||
}
|
||||
});
|
||||
animatorSet.start();
|
||||
}
|
||||
|
||||
private void startOpenAnimation() {
|
||||
if (Build.VERSION.SDK_INT >= 20) {
|
||||
container.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
if (containerView.getMeasuredHeight() == 0) {
|
||||
containerView.measure(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.x, View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(AndroidUtilities.displaySize.y, View.MeasureSpec.AT_MOST));
|
||||
}
|
||||
if (useRevealAnimation) {
|
||||
startRevealAnimation(true);
|
||||
} else {
|
||||
ViewProxy.setTranslationY(containerView, containerView.getMeasuredHeight());
|
||||
backgroundDrawable.setAlpha(0);
|
||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||
animatorSetProxy.playTogether(
|
||||
ObjectAnimatorProxy.ofFloat(containerView, "translationY", 0),
|
||||
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 51));
|
||||
animatorSetProxy.setDuration(200);
|
||||
animatorSetProxy.setStartDelay(20);
|
||||
animatorSetProxy.setInterpolator(new DecelerateInterpolator());
|
||||
animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() {
|
||||
@Override
|
||||
public void onAnimationEnd(Object animation) {
|
||||
if (delegate != null) {
|
||||
delegate.onOpenAnimationEnd();
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
container.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
}
|
||||
}
|
||||
});
|
||||
animatorSetProxy.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void setDelegate(BottomSheetDelegate delegate) {
|
||||
|
@ -382,7 +559,7 @@ public class BottomSheet extends Dialog {
|
|||
}
|
||||
|
||||
public LinearLayout getSheetContainer() {
|
||||
return linearLayout;
|
||||
return containerView;
|
||||
}
|
||||
|
||||
public int getTag() {
|
||||
|
@ -397,13 +574,14 @@ public class BottomSheet extends Dialog {
|
|||
cell.textView.setText(text);
|
||||
}
|
||||
|
||||
private void dismissWithButtonClick(final int item) {
|
||||
public void dismissWithButtonClick(final int item) {
|
||||
if (dismissed) {
|
||||
return;
|
||||
}
|
||||
dismissed = true;
|
||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||
animatorSetProxy.playTogether(
|
||||
ObjectAnimatorProxy.ofFloat(linearLayout, "translationY", linearLayout.getHeight() + AndroidUtilities.dp(10)),
|
||||
ObjectAnimatorProxy.ofFloat(containerView, "translationY", containerView.getMeasuredHeight() + AndroidUtilities.dp(10)),
|
||||
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0)
|
||||
);
|
||||
animatorSetProxy.setDuration(180);
|
||||
|
@ -440,34 +618,38 @@ public class BottomSheet extends Dialog {
|
|||
return;
|
||||
}
|
||||
dismissed = true;
|
||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||
animatorSetProxy.playTogether(
|
||||
ObjectAnimatorProxy.ofFloat(linearLayout, "translationY", linearLayout.getHeight() + AndroidUtilities.dp(10)),
|
||||
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0)
|
||||
);
|
||||
animatorSetProxy.setDuration(180);
|
||||
animatorSetProxy.setInterpolator(new AccelerateInterpolator());
|
||||
animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() {
|
||||
@Override
|
||||
public void onAnimationEnd(Object animation) {
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
BottomSheet.super.dismiss();
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
if (useRevealAnimation) {
|
||||
startRevealAnimation(false);
|
||||
} else {
|
||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||
animatorSetProxy.playTogether(
|
||||
ObjectAnimatorProxy.ofFloat(containerView, "translationY", containerView.getMeasuredHeight() + AndroidUtilities.dp(10)),
|
||||
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0)
|
||||
);
|
||||
animatorSetProxy.setDuration(180);
|
||||
animatorSetProxy.setInterpolator(new AccelerateInterpolator());
|
||||
animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() {
|
||||
@Override
|
||||
public void onAnimationEnd(Object animation) {
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
BottomSheet.super.dismiss();
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Object animation) {
|
||||
onAnimationEnd(animation);
|
||||
}
|
||||
});
|
||||
animatorSetProxy.start();
|
||||
@Override
|
||||
public void onAnimationCancel(Object animation) {
|
||||
onAnimationEnd(animation);
|
||||
}
|
||||
});
|
||||
animatorSetProxy.start();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
@ -515,10 +697,10 @@ public class BottomSheet extends Dialog {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setRevealAnimation(int x, int y) {
|
||||
bottomSheet.revealX = x;
|
||||
bottomSheet.revealY = y;
|
||||
bottomSheet.useRevealAnimation = true;
|
||||
public Builder setUseRevealAnimation() {
|
||||
if (Build.VERSION.SDK_INT >= 18 && !AndroidUtilities.isTablet()) {
|
||||
bottomSheet.useRevealAnimation = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -532,8 +714,13 @@ public class BottomSheet extends Dialog {
|
|||
return this;
|
||||
}
|
||||
|
||||
public BottomSheet setOverrideTabletWidth(boolean value) {
|
||||
bottomSheet.overrideTabletWidth = value;
|
||||
public Builder setApplyTopPaddings(boolean value) {
|
||||
bottomSheet.applyTopPaddings = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BottomSheet setUseFullWidth(boolean value) {
|
||||
bottomSheet.fullWidth = value;
|
||||
return bottomSheet;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -383,7 +383,6 @@ public class DrawerLayoutContainer extends FrameLayout {
|
|||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
inLayout = true;
|
||||
final int width = r - l;
|
||||
final int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
final View child = getChildAt(i);
|
||||
|
@ -416,8 +415,6 @@ public class DrawerLayoutContainer extends FrameLayout {
|
|||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@ public class CountrySearchAdapter extends BaseFragmentAdapter {
|
|||
updateSearchResults(new ArrayList<Country>());
|
||||
return;
|
||||
}
|
||||
long time = System.currentTimeMillis();
|
||||
ArrayList<Country> resultArray = new ArrayList<>();
|
||||
|
||||
String n = query.substring(0, 1);
|
||||
|
|
|
@ -19,7 +19,7 @@ import org.telegram.ui.Cells.PhotoAttachPhotoCell;
|
|||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class PhotoAttachAdapter extends RecyclerView.Adapter implements NotificationCenter.NotificationCenterDelegate {
|
||||
public class PhotoAttachAdapter extends RecyclerView.Adapter {
|
||||
|
||||
private Context mContext;
|
||||
private PhotoAttachAdapterDelegate delegate;
|
||||
|
@ -38,14 +38,6 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter implements Notifica
|
|||
|
||||
public PhotoAttachAdapter(Context context) {
|
||||
mContext = context;
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.albumsDidLoaded);
|
||||
if (MediaController.allPhotosAlbumEntry == null) {
|
||||
MediaController.loadGalleryPhotosAlbums(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.albumsDidLoaded);
|
||||
}
|
||||
|
||||
public void clearSelectedPhotos() {
|
||||
|
@ -64,13 +56,6 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter implements Notifica
|
|||
delegate = photoAttachAdapterDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didReceivedNotification(int id, Object... args) {
|
||||
if (id == NotificationCenter.albumsDidLoaded) {
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||
//if (position != 0) {
|
||||
|
|
|
@ -87,7 +87,7 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio
|
|||
}
|
||||
|
||||
public void loadStikersForEmoji(CharSequence emoji) {
|
||||
boolean search = emoji != null && emoji.length() != 0 && emoji.length() <= 2;
|
||||
boolean search = emoji != null && emoji.length() > 0 && emoji.length() <= 4;
|
||||
if (search) {
|
||||
lastSticker = emoji.toString();
|
||||
HashMap<String, ArrayList<TLRPC.Document>> allStickers = StickersQuery.getAllStickers();
|
||||
|
|
|
@ -0,0 +1,464 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 2.x.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-2015.
|
||||
*/
|
||||
|
||||
package org.telegram.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.android.ImageLoader;
|
||||
import org.telegram.android.MediaController;
|
||||
import org.telegram.android.MessageObject;
|
||||
import org.telegram.android.NotificationCenter;
|
||||
import org.telegram.android.audioinfo.AudioInfo;
|
||||
import org.telegram.messenger.FileLoader;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.messenger.TLRPC;
|
||||
import org.telegram.ui.ActionBar.ActionBar;
|
||||
import org.telegram.ui.ActionBar.BaseFragment;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
import org.telegram.ui.Components.LineProgressView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class AudioPlayerActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, MediaController.FileDownloadProgressListener {
|
||||
|
||||
private MessageObject lastMessageObject;
|
||||
private ImageView placeholder;
|
||||
private ImageView playButton;
|
||||
private ImageView nextButton;
|
||||
private ImageView prevButton;
|
||||
private ImageView shuffleButton;
|
||||
private LineProgressView progressView;
|
||||
private ImageView repeatButton;
|
||||
private ImageView[] buttons = new ImageView[5];
|
||||
private TextView durationTextView;
|
||||
private TextView timeTextView;
|
||||
private SeekBarView seekBarView;
|
||||
|
||||
private int TAG;
|
||||
|
||||
private String lastTimeString;
|
||||
|
||||
private class SeekBarView extends FrameLayout {
|
||||
|
||||
private Paint innerPaint1;
|
||||
private Paint outerPaint1;
|
||||
private int thumbWidth;
|
||||
private int thumbHeight;
|
||||
public int thumbX = 0;
|
||||
public int thumbDX = 0;
|
||||
private boolean pressed = false;
|
||||
|
||||
public SeekBarView(Context context) {
|
||||
super(context);
|
||||
setWillNotDraw(false);
|
||||
innerPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
innerPaint1.setColor(0x19000000);
|
||||
|
||||
outerPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
outerPaint1.setColor(0xff23afef);
|
||||
|
||||
thumbWidth = AndroidUtilities.dp(24);
|
||||
thumbHeight = AndroidUtilities.dp(24);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return onTouch(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return onTouch(event);
|
||||
}
|
||||
|
||||
boolean onTouch(MotionEvent ev) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
int additionWidth = (getMeasuredHeight() - thumbWidth) / 2;
|
||||
if (thumbX - additionWidth <= ev.getX() && ev.getX() <= thumbX + thumbWidth + additionWidth && ev.getY() >= 0 && ev.getY() <= getMeasuredHeight()) {
|
||||
pressed = true;
|
||||
thumbDX = (int)(ev.getX() - thumbX);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
} else if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
|
||||
if (pressed) {
|
||||
if (ev.getAction() == MotionEvent.ACTION_UP) {
|
||||
onSeekBarDrag((float) thumbX / (float) (getMeasuredWidth() - thumbWidth));
|
||||
}
|
||||
pressed = false;
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
if (pressed) {
|
||||
thumbX = (int)(ev.getX() - thumbDX);
|
||||
if (thumbX < 0) {
|
||||
thumbX = 0;
|
||||
} else if (thumbX > getMeasuredWidth() - thumbWidth) {
|
||||
thumbX = getMeasuredWidth() - thumbWidth;
|
||||
}
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setProgress(float progress) {
|
||||
int newThumbX = (int)Math.ceil((getMeasuredWidth() - thumbWidth) * progress);
|
||||
if (thumbX != newThumbX) {
|
||||
thumbX = newThumbX;
|
||||
if (thumbX < 0) {
|
||||
thumbX = 0;
|
||||
} else if (thumbX > getMeasuredWidth() - thumbWidth) {
|
||||
thumbX = getMeasuredWidth() - thumbWidth;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDragging() {
|
||||
return pressed;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
int y = (getMeasuredHeight() - thumbHeight) / 2;
|
||||
canvas.drawRect(thumbWidth / 2, getMeasuredHeight() / 2 - AndroidUtilities.dp(1), getMeasuredWidth() - thumbWidth / 2, getMeasuredHeight() / 2 + AndroidUtilities.dp(1), innerPaint1);
|
||||
canvas.drawRect(thumbWidth / 2, getMeasuredHeight() / 2 - AndroidUtilities.dp(1), thumbWidth / 2 + thumbX, getMeasuredHeight() / 2 + AndroidUtilities.dp(1), outerPaint1);
|
||||
canvas.drawCircle(thumbX + thumbWidth / 2, y + thumbHeight / 2, AndroidUtilities.dp(pressed ? 8 : 6), outerPaint1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFragmentCreate() {
|
||||
TAG = MediaController.getInstance().generateObserverTag();
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidStarted);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioProgressDidChanged);
|
||||
return super.onFragmentCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentDestroy() {
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioDidReset);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioDidStarted);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioProgressDidChanged);
|
||||
MediaController.getInstance().removeLoadingFileObserver(this);
|
||||
super.onFragmentDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context) {
|
||||
FrameLayout frameLayout = new FrameLayout(context);
|
||||
frameLayout.setBackgroundColor(0xfff0f0f0);
|
||||
frameLayout.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
fragmentView = frameLayout;
|
||||
|
||||
actionBar.setBackgroundColor(0xffffffff);
|
||||
actionBar.setBackButtonImage(R.drawable.pl_back);
|
||||
actionBar.setItemsBackground(R.drawable.bar_selector_audio);
|
||||
if (!AndroidUtilities.isTablet()) {
|
||||
actionBar.showActionModeTop();
|
||||
}
|
||||
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
|
||||
@Override
|
||||
public void onItemClick(int id) {
|
||||
if (id == -1) {
|
||||
finishFragment();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
placeholder = new ImageView(context);
|
||||
frameLayout.addView(placeholder, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 66));
|
||||
|
||||
View shadow = new View(context);
|
||||
shadow.setBackgroundResource(R.drawable.header_shadow_reverse);
|
||||
frameLayout.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 3, Gravity.BOTTOM | Gravity.LEFT, 0, 0, 0, 96));
|
||||
|
||||
FrameLayout seekBarContainer = new FrameLayout(context);
|
||||
seekBarContainer.setBackgroundColor(0xe5ffffff);
|
||||
frameLayout.addView(seekBarContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 30, Gravity.BOTTOM | Gravity.LEFT, 0, 0, 0, 66));
|
||||
|
||||
timeTextView = new TextView(context);
|
||||
timeTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
|
||||
timeTextView.setTextColor(0xff19a7e8);
|
||||
timeTextView.setGravity(Gravity.CENTER);
|
||||
timeTextView.setText("0:00");
|
||||
seekBarContainer.addView(timeTextView, LayoutHelper.createFrame(44, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT));
|
||||
|
||||
durationTextView = new TextView(context);
|
||||
durationTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
|
||||
durationTextView.setTextColor(0xff8a8a8a);
|
||||
durationTextView.setGravity(Gravity.CENTER);
|
||||
durationTextView.setText("3:00");
|
||||
seekBarContainer.addView(durationTextView, LayoutHelper.createFrame(44, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.RIGHT));
|
||||
|
||||
seekBarView = new SeekBarView(context);
|
||||
seekBarContainer.addView(seekBarView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 32, 0, 32, 0));
|
||||
|
||||
progressView = new LineProgressView(context);
|
||||
progressView.setVisibility(View.INVISIBLE);
|
||||
progressView.setBackgroundColor(0x19000000);
|
||||
progressView.setProgressColor(0xff23afef);
|
||||
seekBarContainer.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 2, Gravity.CENTER_VERTICAL | Gravity.LEFT, 44, 0, 44, 0));
|
||||
|
||||
FrameLayout bottomView = new FrameLayout(context) {
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
int dist = ((right - left) - AndroidUtilities.dp(30 + 48 * 5)) / 4;
|
||||
for (int a = 0; a < 5; a++) {
|
||||
int l = AndroidUtilities.dp(15 + 48 * a) + dist * a;
|
||||
int t = AndroidUtilities.dp(9);
|
||||
buttons[a].layout(l, t, l + buttons[a].getMeasuredWidth(), t + buttons[a].getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
};
|
||||
bottomView.setBackgroundColor(0xffffffff);
|
||||
frameLayout.addView(bottomView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 66, Gravity.BOTTOM | Gravity.LEFT));
|
||||
|
||||
buttons[0] = repeatButton = new ImageView(context);
|
||||
repeatButton.setScaleType(ImageView.ScaleType.CENTER);
|
||||
bottomView.addView(repeatButton, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.TOP));
|
||||
repeatButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MediaController.getInstance().toggleRepeatMode();
|
||||
updateRepeatButton();
|
||||
}
|
||||
});
|
||||
|
||||
buttons[1] = prevButton = new ImageView(context);
|
||||
prevButton.setScaleType(ImageView.ScaleType.CENTER);
|
||||
prevButton.setImageResource(R.drawable.player_prev_states);
|
||||
bottomView.addView(prevButton, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.TOP));
|
||||
prevButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MediaController.getInstance().playPreviousMessage();
|
||||
}
|
||||
});
|
||||
|
||||
buttons[2] = playButton = new ImageView(context);
|
||||
playButton.setScaleType(ImageView.ScaleType.CENTER);
|
||||
playButton.setImageResource(R.drawable.player_play_states);
|
||||
bottomView.addView(playButton, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.TOP));
|
||||
playButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (MediaController.getInstance().isDownloadingCurrentMessage()) {
|
||||
return;
|
||||
}
|
||||
if (MediaController.getInstance().isAudioPaused()) {
|
||||
MediaController.getInstance().playAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
} else {
|
||||
MediaController.getInstance().pauseAudio(MediaController.getInstance().getPlayingMessageObject());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
buttons[3] = nextButton = new ImageView(context);
|
||||
nextButton.setScaleType(ImageView.ScaleType.CENTER);
|
||||
nextButton.setImageResource(R.drawable.player_next_states);
|
||||
bottomView.addView(nextButton, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.TOP));
|
||||
nextButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MediaController.getInstance().playNextMessage();
|
||||
}
|
||||
});
|
||||
|
||||
buttons[4] = shuffleButton = new ImageView(context);
|
||||
shuffleButton.setScaleType(ImageView.ScaleType.CENTER);
|
||||
bottomView.addView(shuffleButton, LayoutHelper.createFrame(48, 48, Gravity.LEFT | Gravity.TOP));
|
||||
shuffleButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MediaController.getInstance().toggleShuffleMusic();
|
||||
updateShuffleButton();
|
||||
}
|
||||
});
|
||||
|
||||
updateTitle(false);
|
||||
updateRepeatButton();
|
||||
updateShuffleButton();
|
||||
|
||||
return frameLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didReceivedNotification(int id, Object... args) {
|
||||
if (id == NotificationCenter.audioDidStarted || id == NotificationCenter.audioPlayStateChanged || id == NotificationCenter.audioDidReset) {
|
||||
updateTitle(id == NotificationCenter.audioDidReset && (Boolean) args[1]);
|
||||
} else if (id == NotificationCenter.audioProgressDidChanged) {
|
||||
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
|
||||
|
||||
if (messageObject.isMusic()) {
|
||||
updateProgress(messageObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailedDownload(String fileName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccessDownload(String fileName) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressDownload(String fileName, float progress) {
|
||||
progressView.setProgress(progress, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getObserverTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
private void onSeekBarDrag(float progress) {
|
||||
MediaController.getInstance().seekToProgress(MediaController.getInstance().getPlayingMessageObject(), progress);
|
||||
}
|
||||
|
||||
private void updateShuffleButton() {
|
||||
if (MediaController.getInstance().isShuffleMusic()) {
|
||||
shuffleButton.setImageResource(R.drawable.pl_shuffle_active);
|
||||
} else {
|
||||
shuffleButton.setImageResource(R.drawable.pl_shuffle);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRepeatButton() {
|
||||
int mode = MediaController.getInstance().getRepeatMode();
|
||||
if (mode == 0) {
|
||||
repeatButton.setImageResource(R.drawable.pl_repeat);
|
||||
} else if (mode == 1) {
|
||||
repeatButton.setImageResource(R.drawable.pl_repeat_active);
|
||||
} else if (mode == 2) {
|
||||
repeatButton.setImageResource(R.drawable.pl_repeat1_active);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateProgress(MessageObject messageObject) {
|
||||
if (seekBarView != null) {
|
||||
if (!seekBarView.isDragging()) {
|
||||
seekBarView.setProgress(messageObject.audioProgress);
|
||||
}
|
||||
String timeString = String.format("%d:%02d", messageObject.audioProgressSec / 60, messageObject.audioProgressSec % 60);
|
||||
if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) {
|
||||
lastTimeString = timeString;
|
||||
timeTextView.setText(timeString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfMusicDownloaded(MessageObject messageObject) {
|
||||
File cacheFile = null;
|
||||
if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() > 0) {
|
||||
cacheFile = new File(messageObject.messageOwner.attachPath);
|
||||
if(!cacheFile.exists()) {
|
||||
cacheFile = null;
|
||||
}
|
||||
}
|
||||
if (cacheFile == null) {
|
||||
cacheFile = FileLoader.getPathToMessage(messageObject.messageOwner);
|
||||
}
|
||||
if (!cacheFile.exists()) {
|
||||
String fileName = messageObject.getFileName();
|
||||
MediaController.getInstance().addLoadingFileObserver(fileName, this);
|
||||
Float progress = ImageLoader.getInstance().getFileProgress(fileName);
|
||||
progressView.setProgress(progress != null ? progress : 0, false);
|
||||
progressView.setVisibility(View.VISIBLE);
|
||||
seekBarView.setVisibility(View.INVISIBLE);
|
||||
playButton.setEnabled(false);
|
||||
} else {
|
||||
MediaController.getInstance().removeLoadingFileObserver(this);
|
||||
progressView.setVisibility(View.INVISIBLE);
|
||||
seekBarView.setVisibility(View.VISIBLE);
|
||||
playButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTitle(boolean shutdown) {
|
||||
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
|
||||
if (messageObject == null && shutdown || messageObject != null && !messageObject.isMusic()) {
|
||||
if (!parentLayout.fragmentsStack.isEmpty() && parentLayout.fragmentsStack.get(parentLayout.fragmentsStack.size() - 1) == this) {
|
||||
finishFragment();
|
||||
} else {
|
||||
removeSelfFromStack();
|
||||
}
|
||||
} else {
|
||||
if (messageObject == null) {
|
||||
return;
|
||||
}
|
||||
checkIfMusicDownloaded(messageObject);
|
||||
updateProgress(messageObject);
|
||||
|
||||
if (MediaController.getInstance().isAudioPaused()) {
|
||||
playButton.setImageResource(R.drawable.player_play_states);
|
||||
} else {
|
||||
playButton.setImageResource(R.drawable.player_pause_states);
|
||||
}
|
||||
if (actionBar != null) {
|
||||
actionBar.setTitle(messageObject.getMusicTitle());
|
||||
actionBar.getTitleTextView().setTextColor(0xff212121);
|
||||
actionBar.setSubtitle(messageObject.getMusicAuthor());
|
||||
actionBar.getSubTitleTextView().setTextColor(0xff8a8a8a);
|
||||
}
|
||||
AudioInfo audioInfo = MediaController.getInstance().getAudioInfo();
|
||||
if (audioInfo != null && audioInfo.getCover() != null) {
|
||||
placeholder.setImageBitmap(audioInfo.getCover());
|
||||
placeholder.setPadding(0, 0, 0, 0);
|
||||
placeholder.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
} else {
|
||||
placeholder.setImageResource(R.drawable.nocover);
|
||||
placeholder.setPadding(0, 0, 0, AndroidUtilities.dp(30));
|
||||
placeholder.setScaleType(ImageView.ScaleType.CENTER);
|
||||
}
|
||||
|
||||
if (durationTextView != null) {
|
||||
int duration = 0;
|
||||
for (TLRPC.DocumentAttribute attribute : messageObject.messageOwner.media.document.attributes) {
|
||||
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
|
||||
duration = attribute.duration;
|
||||
break;
|
||||
}
|
||||
}
|
||||
durationTextView.setText(duration != 0 ? String.format("%d:%02d", duration / 60, duration % 60) : "-:--");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 2.x.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-2015.
|
||||
*/
|
||||
|
||||
package org.telegram.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ListView;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.android.LocaleController;
|
||||
import org.telegram.android.MediaController;
|
||||
import org.telegram.android.MessageObject;
|
||||
import org.telegram.android.NotificationCenter;
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.FileLoader;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.messenger.TLRPC;
|
||||
import org.telegram.messenger.UserConfig;
|
||||
import org.telegram.messenger.Utilities;
|
||||
import org.telegram.ui.ActionBar.ActionBar;
|
||||
import org.telegram.ui.ActionBar.BaseFragment;
|
||||
import org.telegram.ui.Adapters.BaseFragmentAdapter;
|
||||
import org.telegram.ui.Cells.AudioCell;
|
||||
import org.telegram.ui.Components.EmptyTextProgressView;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
import org.telegram.ui.Components.PickerBottomLayout;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class AudioSelectActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate {
|
||||
|
||||
private ListAdapter listViewAdapter;
|
||||
private EmptyTextProgressView progressView;
|
||||
private PickerBottomLayout bottomLayout;
|
||||
|
||||
private boolean loadingAudio;
|
||||
|
||||
private ArrayList<MediaController.AudioEntry> audioEntries = new ArrayList<>();
|
||||
private HashMap<Long, MediaController.AudioEntry> selectedAudios = new HashMap<>();
|
||||
|
||||
private AudioSelectActivityDelegate delegate;
|
||||
|
||||
private MessageObject playingAudio;
|
||||
|
||||
public interface AudioSelectActivityDelegate {
|
||||
void didSelectAudio(ArrayList<MessageObject> audios);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFragmentCreate() {
|
||||
super.onFragmentCreate();
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.closeChats);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset);
|
||||
loadAudio();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFragmentDestroy() {
|
||||
super.onFragmentDestroy();
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.closeChats);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioDidReset);
|
||||
if (playingAudio != null && MediaController.getInstance().isPlayingAudio(playingAudio)) {
|
||||
MediaController.getInstance().clenupPlayer(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context) {
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
actionBar.setAllowOverlayTitle(true);
|
||||
actionBar.setTitle(LocaleController.getString("AttachAudio", R.string.AttachAudio));
|
||||
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
|
||||
@Override
|
||||
public void onItemClick(int id) {
|
||||
if (id == -1) {
|
||||
finishFragment();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
fragmentView = new FrameLayout(context);
|
||||
FrameLayout frameLayout = (FrameLayout) fragmentView;
|
||||
|
||||
progressView = new EmptyTextProgressView(context);
|
||||
progressView.setText(LocaleController.getString("NoAudio", R.string.NoAudio));
|
||||
frameLayout.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
|
||||
|
||||
ListView listView = new ListView(context);
|
||||
listView.setEmptyView(progressView);
|
||||
listView.setVerticalScrollBarEnabled(false);
|
||||
listView.setDivider(null);
|
||||
listView.setDividerHeight(0);
|
||||
listView.setAdapter(listViewAdapter = new ListAdapter(context));
|
||||
if (Build.VERSION.SDK_INT >= 11) {
|
||||
listView.setVerticalScrollbarPosition(LocaleController.isRTL ? ListView.SCROLLBAR_POSITION_LEFT : ListView.SCROLLBAR_POSITION_RIGHT);
|
||||
}
|
||||
frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 48));
|
||||
|
||||
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
|
||||
AudioCell audioCell = (AudioCell) view;
|
||||
MediaController.AudioEntry audioEntry = audioCell.getAudioEntry();
|
||||
if (selectedAudios.containsKey(audioEntry.id)) {
|
||||
selectedAudios.remove(audioEntry.id);
|
||||
audioCell.setChecked(false);
|
||||
} else {
|
||||
selectedAudios.put(audioEntry.id, audioEntry);
|
||||
audioCell.setChecked(true);
|
||||
}
|
||||
updateBottomLayoutCount();
|
||||
}
|
||||
});
|
||||
|
||||
bottomLayout = new PickerBottomLayout(context, false);
|
||||
frameLayout.addView(bottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM));
|
||||
bottomLayout.cancelButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
finishFragment();
|
||||
}
|
||||
});
|
||||
bottomLayout.doneButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (delegate != null) {
|
||||
ArrayList<MessageObject> audios = new ArrayList<>();
|
||||
for (HashMap.Entry<Long, MediaController.AudioEntry> entry : selectedAudios.entrySet()) {
|
||||
audios.add(entry.getValue().messageObject);
|
||||
}
|
||||
delegate.didSelectAudio(audios);
|
||||
}
|
||||
finishFragment();
|
||||
}
|
||||
});
|
||||
|
||||
View shadow = new View(context);
|
||||
shadow.setBackgroundResource(R.drawable.header_shadow_reverse);
|
||||
frameLayout.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 3, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 48));
|
||||
|
||||
if (loadingAudio) {
|
||||
progressView.showProgress();
|
||||
} else {
|
||||
progressView.showTextView();
|
||||
}
|
||||
updateBottomLayoutCount();
|
||||
return fragmentView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didReceivedNotification(int id, Object... args) {
|
||||
if (id == NotificationCenter.closeChats) {
|
||||
removeSelfFromStack();
|
||||
} else if (id == NotificationCenter.audioDidReset) {
|
||||
if (listViewAdapter != null) {
|
||||
listViewAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBottomLayoutCount() {
|
||||
bottomLayout.updateSelectedCount(selectedAudios.size(), true);
|
||||
}
|
||||
|
||||
public void setDelegate(AudioSelectActivityDelegate audioSelectActivityDelegate) {
|
||||
delegate = audioSelectActivityDelegate;
|
||||
}
|
||||
|
||||
private void loadAudio() {
|
||||
loadingAudio = true;
|
||||
if (progressView != null) {
|
||||
progressView.showProgress();
|
||||
}
|
||||
Utilities.globalQueue.postRunnable(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String[] projection = {
|
||||
MediaStore.Audio.Media._ID,
|
||||
MediaStore.Audio.Media.ARTIST,
|
||||
MediaStore.Audio.Media.TITLE,
|
||||
MediaStore.Audio.Media.DATA,
|
||||
MediaStore.Audio.Media.DURATION,
|
||||
MediaStore.Audio.Media.ALBUM
|
||||
};
|
||||
|
||||
final ArrayList<MediaController.AudioEntry> newAudioEntries = new ArrayList<>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = ApplicationLoader.applicationContext.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, MediaStore.Audio.Media.IS_MUSIC + " != 0", null, null);
|
||||
int id = -2000000000;
|
||||
while (cursor.moveToNext()) {
|
||||
MediaController.AudioEntry audioEntry = new MediaController.AudioEntry();
|
||||
audioEntry.id = cursor.getInt(0);
|
||||
audioEntry.author = cursor.getString(1);
|
||||
audioEntry.title = cursor.getString(2);
|
||||
audioEntry.path = cursor.getString(3);
|
||||
audioEntry.duration = (int) (cursor.getLong(4) / 1000);
|
||||
audioEntry.genre = cursor.getString(5);
|
||||
|
||||
File file = new File(audioEntry.path);
|
||||
|
||||
TLRPC.TL_message message = new TLRPC.TL_message();
|
||||
message.flags = TLRPC.MESSAGE_FLAG_OUT;
|
||||
message.id = id;
|
||||
message.to_id = new TLRPC.TL_peerUser();
|
||||
message.to_id.user_id = message.from_id = UserConfig.getClientUserId();
|
||||
message.date = (int) (System.currentTimeMillis() / 1000);
|
||||
message.message = "-1";
|
||||
message.attachPath = audioEntry.path;
|
||||
message.media = new TLRPC.TL_messageMediaDocument();
|
||||
message.media.document = new TLRPC.TL_document();
|
||||
|
||||
String ext = FileLoader.getFileExtension(file);
|
||||
|
||||
message.media.document.id = 0;
|
||||
message.media.document.access_hash = 0;
|
||||
message.media.document.date = message.date;
|
||||
message.media.document.mime_type = "audio/" + (ext.length() > 0 ? ext : "mp3");
|
||||
message.media.document.size = (int) file.length();
|
||||
message.media.document.thumb = new TLRPC.TL_photoSizeEmpty();
|
||||
message.media.document.thumb.type = "s";
|
||||
message.media.document.dc_id = 0;
|
||||
|
||||
TLRPC.TL_documentAttributeAudio attributeAudio = new TLRPC.TL_documentAttributeAudio();
|
||||
attributeAudio.duration = audioEntry.duration;
|
||||
attributeAudio.title = audioEntry.title;
|
||||
attributeAudio.performer = audioEntry.author;
|
||||
message.media.document.attributes.add(attributeAudio);
|
||||
|
||||
TLRPC.TL_documentAttributeFilename fileName = new TLRPC.TL_documentAttributeFilename();
|
||||
fileName.file_name = file.getName();
|
||||
message.media.document.attributes.add(fileName);
|
||||
|
||||
audioEntry.messageObject = new MessageObject(message, null, false);
|
||||
|
||||
newAudioEntries.add(audioEntry);
|
||||
id--;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
audioEntries = newAudioEntries;
|
||||
progressView.showTextView();
|
||||
listViewAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class ListAdapter extends BaseFragmentAdapter {
|
||||
private Context mContext;
|
||||
|
||||
public ListAdapter(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areAllItemsEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int i) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return audioEntries.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int i) {
|
||||
return audioEntries.get(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int i, View view, ViewGroup viewGroup) {
|
||||
int type = getItemViewType(i);
|
||||
if (view == null) {
|
||||
view = new AudioCell(mContext);
|
||||
((AudioCell) view).setDelegate(new AudioCell.AudioCellDelegate() {
|
||||
@Override
|
||||
public void startedPlayingAudio(MessageObject messageObject) {
|
||||
playingAudio = messageObject;
|
||||
}
|
||||
});
|
||||
}
|
||||
MediaController.AudioEntry audioEntry = audioEntries.get(i);
|
||||
((AudioCell) view).setAudio(audioEntries.get(i), i != audioEntries.size() - 1, selectedAudios.containsKey(audioEntry.id));
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return audioEntries.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,6 @@ import android.content.DialogInterface;
|
|||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -65,7 +64,7 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe
|
|||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
actionBar.setAllowOverlayTitle(true);
|
||||
actionBar.setTitle(LocaleController.getString("BlockedUsers", R.string.BlockedUsers));
|
||||
|
|
169
TMessagesProj/src/main/java/org/telegram/ui/Cells/AudioCell.java
Normal file
169
TMessagesProj/src/main/java/org/telegram/ui/Cells/AudioCell.java
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 2.x.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-2015.
|
||||
*/
|
||||
|
||||
package org.telegram.ui.Cells;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.android.LocaleController;
|
||||
import org.telegram.android.MediaController;
|
||||
import org.telegram.android.MessageObject;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.Components.CheckBox;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class AudioCell extends FrameLayout {
|
||||
|
||||
private ImageView playButton;
|
||||
private TextView titleTextView;
|
||||
private TextView authorTextView;
|
||||
private TextView genreTextView;
|
||||
private TextView timeTextView;
|
||||
private CheckBox checkBox;
|
||||
|
||||
private MediaController.AudioEntry audioEntry;
|
||||
private boolean needDivider;
|
||||
private static Paint paint;
|
||||
|
||||
private AudioCellDelegate delegate;
|
||||
|
||||
public interface AudioCellDelegate {
|
||||
void startedPlayingAudio(MessageObject messageObject);
|
||||
}
|
||||
|
||||
public AudioCell(Context context) {
|
||||
super(context);
|
||||
|
||||
if (paint == null) {
|
||||
paint = new Paint();
|
||||
paint.setColor(0xffd9d9d9);
|
||||
paint.setStrokeWidth(1);
|
||||
}
|
||||
|
||||
playButton = new ImageView(context);
|
||||
playButton.setScaleType(ImageView.ScaleType.CENTER);
|
||||
addView(playButton, LayoutHelper.createFrame(46, 46, ((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP), LocaleController.isRTL ? 0 : 13, 13, LocaleController.isRTL ? 13 : 0, 0));
|
||||
playButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (audioEntry != null) {
|
||||
if (MediaController.getInstance().isPlayingAudio(audioEntry.messageObject) && !MediaController.getInstance().isAudioPaused()) {
|
||||
MediaController.getInstance().pauseAudio(audioEntry.messageObject);
|
||||
playButton.setImageResource(R.drawable.audiosend_play);
|
||||
} else {
|
||||
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
||||
arrayList.add(audioEntry.messageObject);
|
||||
if (MediaController.getInstance().setPlaylist(arrayList, audioEntry.messageObject)) {
|
||||
playButton.setImageResource(R.drawable.audiosend_pause);
|
||||
if (delegate != null) {
|
||||
delegate.startedPlayingAudio(audioEntry.messageObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
titleTextView = new TextView(context);
|
||||
titleTextView.setTextColor(0xff212121);
|
||||
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
titleTextView.setLines(1);
|
||||
titleTextView.setMaxLines(1);
|
||||
titleTextView.setSingleLine(true);
|
||||
titleTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
titleTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
|
||||
addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 50 : 72, 7, LocaleController.isRTL ? 72 : 50, 0));
|
||||
|
||||
genreTextView = new TextView(context);
|
||||
genreTextView.setTextColor(0xff8a8a8a);
|
||||
genreTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
genreTextView.setLines(1);
|
||||
genreTextView.setMaxLines(1);
|
||||
genreTextView.setSingleLine(true);
|
||||
genreTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
genreTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
|
||||
addView(genreTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 50 : 72, 28, LocaleController.isRTL ? 72 : 50, 0));
|
||||
|
||||
authorTextView = new TextView(context);
|
||||
authorTextView.setTextColor(0xff8a8a8a);
|
||||
authorTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
authorTextView.setLines(1);
|
||||
authorTextView.setMaxLines(1);
|
||||
authorTextView.setSingleLine(true);
|
||||
authorTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
authorTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
|
||||
addView(authorTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 50 : 72, 44, LocaleController.isRTL ? 72 : 50, 0));
|
||||
|
||||
timeTextView = new TextView(context);
|
||||
timeTextView.setTextColor(0xff999999);
|
||||
timeTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
|
||||
timeTextView.setLines(1);
|
||||
timeTextView.setMaxLines(1);
|
||||
timeTextView.setSingleLine(true);
|
||||
timeTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
timeTextView.setGravity((LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP);
|
||||
addView(timeTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, LocaleController.isRTL ? 18 : 0, 11, LocaleController.isRTL ? 0 : 18, 0));
|
||||
|
||||
checkBox = new CheckBox(context, R.drawable.round_check2);
|
||||
checkBox.setVisibility(VISIBLE);
|
||||
checkBox.setColor(0xff29b6f7);
|
||||
addView(checkBox, LayoutHelper.createFrame(22, 22, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, LocaleController.isRTL ? 18 : 0, 39, LocaleController.isRTL ? 0 : 18, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(72) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY));
|
||||
}
|
||||
|
||||
public void setAudio(MediaController.AudioEntry entry, boolean divider, boolean checked) {
|
||||
audioEntry = entry;
|
||||
|
||||
titleTextView.setText(audioEntry.title);
|
||||
genreTextView.setText(audioEntry.genre);
|
||||
authorTextView.setText(audioEntry.author);
|
||||
timeTextView.setText(String.format("%d:%02d", audioEntry.duration / 60, audioEntry.duration % 60));
|
||||
playButton.setImageResource(MediaController.getInstance().isPlayingAudio(audioEntry.messageObject) && !MediaController.getInstance().isAudioPaused() ? R.drawable.audiosend_pause : R.drawable.audiosend_play);
|
||||
|
||||
needDivider = divider;
|
||||
setWillNotDraw(!divider);
|
||||
|
||||
checkBox.setChecked(checked, false);
|
||||
}
|
||||
|
||||
public void setChecked(boolean value) {
|
||||
checkBox.setChecked(value, true);
|
||||
}
|
||||
|
||||
public void setDelegate(AudioCellDelegate audioCellDelegate) {
|
||||
delegate = audioCellDelegate;
|
||||
}
|
||||
|
||||
public MediaController.AudioEntry getAudioEntry() {
|
||||
return audioEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (needDivider) {
|
||||
canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth(), getHeight() - 1, paint);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -42,6 +42,7 @@ public class BotHelpCell extends View {
|
|||
private int height;
|
||||
private int textX;
|
||||
private int textY;
|
||||
private int textXOffset;
|
||||
|
||||
private ClickableSpan pressedLink;
|
||||
private LinkPath urlPath = new LinkPath();
|
||||
|
@ -101,7 +102,9 @@ public class BotHelpCell extends View {
|
|||
width = 0;
|
||||
height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18);
|
||||
int count = textLayout.getLineCount();
|
||||
textXOffset = Integer.MAX_VALUE;
|
||||
for (int a = 0; a < count; a++) {
|
||||
textXOffset = (int) Math.ceil(Math.min(textXOffset, textLayout.getLineLeft(a)));
|
||||
width = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) - textLayout.getLineLeft(a)));
|
||||
}
|
||||
width += AndroidUtilities.dp(4 + 18);
|
||||
|
@ -113,7 +116,6 @@ public class BotHelpCell extends View {
|
|||
float y = event.getY();
|
||||
|
||||
boolean result = false;
|
||||
int side = AndroidUtilities.dp(48);
|
||||
if (textLayout != null) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
|
@ -186,7 +188,7 @@ public class BotHelpCell extends View {
|
|||
ResourceLoader.backgroundMediaDrawableIn.setBounds(x, y, width + x, height + y);
|
||||
ResourceLoader.backgroundMediaDrawableIn.draw(canvas);
|
||||
canvas.save();
|
||||
canvas.translate(textX = AndroidUtilities.dp(2 + 9) + x, textY = AndroidUtilities.dp(2 + 9) + y);
|
||||
canvas.translate(textX = AndroidUtilities.dp(2 + 9) + x - textXOffset, textY = AndroidUtilities.dp(2 + 9) + y);
|
||||
if (pressedLink != null) {
|
||||
canvas.drawPath(urlPath, urlPaint);
|
||||
}
|
||||
|
|
|
@ -174,8 +174,8 @@ public class ChatActionCell extends BaseCell {
|
|||
final int line = textLayout.getLineForVertical((int)y);
|
||||
final int off = textLayout.getOffsetForHorizontal(line, x);
|
||||
final float left = textLayout.getLineLeft(line);
|
||||
if (left <= x && left + textLayout.getLineWidth(line) >= x) {
|
||||
Spannable buffer = (Spannable)currentMessageObject.messageText;
|
||||
if (left <= x && left + textLayout.getLineWidth(line) >= x && currentMessageObject.messageText instanceof Spannable) {
|
||||
Spannable buffer = (Spannable) currentMessageObject.messageText;
|
||||
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
|
||||
|
||||
if (link.length != 0) {
|
||||
|
@ -226,7 +226,6 @@ public class ChatActionCell extends BaseCell {
|
|||
int linesCount = textLayout.getLineCount();
|
||||
for (int a = 0; a < linesCount; a++) {
|
||||
float lineWidth;
|
||||
float lineLeft = 0;
|
||||
try {
|
||||
lineWidth = textLayout.getLineWidth(a);
|
||||
textHeight = (int)Math.max(textHeight, Math.ceil(textLayout.getLineBottom(a)));
|
||||
|
|
|
@ -21,10 +21,11 @@ import android.view.SoundEffectConstants;
|
|||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.android.ImageLoader;
|
||||
import org.telegram.android.MessagesController;
|
||||
import org.telegram.android.SendMessagesHelper;
|
||||
import org.telegram.messenger.FileLoader;
|
||||
import org.telegram.android.MediaController;
|
||||
import org.telegram.android.MessageObject;
|
||||
import org.telegram.ui.Components.ProgressView;
|
||||
import org.telegram.ui.Components.RadialProgress;
|
||||
import org.telegram.ui.Components.ResourceLoader;
|
||||
import org.telegram.ui.Components.SeekBar;
|
||||
|
||||
|
@ -36,10 +37,10 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
private static Paint circlePaint;
|
||||
|
||||
private SeekBar seekBar;
|
||||
private ProgressView progressView;
|
||||
private int seekBarX;
|
||||
private int seekBarY;
|
||||
|
||||
private RadialProgress radialProgress;
|
||||
private int buttonState = 0;
|
||||
private int buttonX;
|
||||
private int buttonY;
|
||||
|
@ -58,7 +59,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
|
||||
seekBar = new SeekBar(context);
|
||||
seekBar.delegate = this;
|
||||
progressView = new ProgressView();
|
||||
radialProgress = new RadialProgress(this);
|
||||
drawForwardedName = true;
|
||||
|
||||
if (timePaint == null) {
|
||||
|
@ -78,7 +79,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
updateButtonState();
|
||||
updateButtonState(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -131,21 +132,25 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
}
|
||||
if (result) {
|
||||
buttonState = 1;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||
invalidate();
|
||||
}
|
||||
} else if (buttonState == 1) {
|
||||
boolean result = MediaController.getInstance().pauseAudio(currentMessageObject);
|
||||
if (result) {
|
||||
buttonState = 0;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||
invalidate();
|
||||
}
|
||||
} else if (buttonState == 2) {
|
||||
FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.audio, true);
|
||||
buttonState = 3;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), true, false);
|
||||
invalidate();
|
||||
} else if (buttonState == 3) {
|
||||
FileLoader.getInstance().cancelLoadFile(currentMessageObject.messageOwner.media.audio);
|
||||
buttonState = 2;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||
invalidate();
|
||||
} else if (buttonState == 4) {
|
||||
if (currentMessageObject.isOut() && currentMessageObject.isSending()) {
|
||||
|
@ -173,6 +178,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
}
|
||||
String timeString = String.format("%02d:%02d", duration / 60, duration % 60);
|
||||
if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) {
|
||||
lastTimeString = timeString;
|
||||
timeWidth = (int)Math.ceil(timePaint.measureText(timeString));
|
||||
timeLayout = new StaticLayout(timeString, timePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||
}
|
||||
|
@ -183,16 +189,23 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
if (buttonState == 2) {
|
||||
FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.audio, true);
|
||||
buttonState = 3;
|
||||
invalidate();
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateButtonState() {
|
||||
public void updateButtonState(boolean animated) {
|
||||
if (currentMessageObject == null) {
|
||||
return;
|
||||
}
|
||||
if (currentMessageObject.isOut() && currentMessageObject.isSending()) {
|
||||
MediaController.getInstance().addLoadingFileObserver(currentMessageObject.messageOwner.attachPath, this);
|
||||
buttonState = 4;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), true, animated);
|
||||
Float progress = ImageLoader.getInstance().getFileProgress(currentMessageObject.messageOwner.attachPath);
|
||||
if (progress == null && SendMessagesHelper.getInstance().isSendingMessage(currentMessageObject.getId())) {
|
||||
progress = 1.0f;
|
||||
}
|
||||
radialProgress.setProgress(progress != null ? progress : 0, false);
|
||||
} else {
|
||||
File cacheFile = null;
|
||||
if (currentMessageObject.messageOwner.attachPath != null && currentMessageObject.messageOwner.attachPath.length() > 0) {
|
||||
|
@ -212,21 +225,24 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
} else {
|
||||
buttonState = 1;
|
||||
}
|
||||
progressView.setProgress(0);
|
||||
radialProgress.setProgress(0, animated);
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
|
||||
} else {
|
||||
String fileName = currentMessageObject.getFileName();
|
||||
MediaController.getInstance().addLoadingFileObserver(fileName, this);
|
||||
if (!FileLoader.getInstance().isLoadingFile(fileName)) {
|
||||
buttonState = 2;
|
||||
progressView.setProgress(0);
|
||||
radialProgress.setProgress(0, animated);
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
|
||||
} else {
|
||||
buttonState = 3;
|
||||
Float progress = ImageLoader.getInstance().getFileProgress(fileName);
|
||||
if (progress != null) {
|
||||
progressView.setProgress(progress);
|
||||
radialProgress.setProgress(progress, animated);
|
||||
} else {
|
||||
progressView.setProgress(0);
|
||||
radialProgress.setProgress(0, animated);
|
||||
}
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), true, animated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -235,26 +251,25 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
|
||||
@Override
|
||||
public void onFailedDownload(String fileName) {
|
||||
updateButtonState();
|
||||
updateButtonState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccessDownload(String fileName) {
|
||||
updateButtonState();
|
||||
updateButtonState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressDownload(String fileName, float progress) {
|
||||
progressView.setProgress(progress);
|
||||
radialProgress.setProgress(progress, true);
|
||||
if (buttonState != 3) {
|
||||
updateButtonState();
|
||||
updateButtonState(false);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
|
||||
|
||||
radialProgress.setProgress(progress, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -299,17 +314,17 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
|
||||
seekBar.width = backgroundWidth - AndroidUtilities.dp(70);
|
||||
seekBar.height = AndroidUtilities.dp(30);
|
||||
progressView.width = backgroundWidth - AndroidUtilities.dp(94);
|
||||
progressView.height = AndroidUtilities.dp(30);
|
||||
seekBarY = AndroidUtilities.dp(11) + namesOffset;
|
||||
buttonY = AndroidUtilities.dp(13) + namesOffset;
|
||||
radialProgress.setProgressRect(buttonX, buttonY, buttonX + AndroidUtilities.dp(40), buttonY + AndroidUtilities.dp(40));
|
||||
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageObject(MessageObject messageObject) {
|
||||
if (currentMessageObject != messageObject || isUserDataChanged()) {
|
||||
boolean dataChanged = currentMessageObject == messageObject && isUserDataChanged();
|
||||
if (currentMessageObject != messageObject || dataChanged) {
|
||||
if (AndroidUtilities.isTablet()) {
|
||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(isChat ? 102 : 50), AndroidUtilities.dp(300));
|
||||
} else {
|
||||
|
@ -318,15 +333,20 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
|
||||
if (messageObject.isOut()) {
|
||||
seekBar.type = 0;
|
||||
progressView.setProgressColors(0xffb4e396, 0xff6ac453);
|
||||
radialProgress.setProgressColor(0xff87bf78);
|
||||
} else {
|
||||
seekBar.type = 1;
|
||||
progressView.setProgressColors(0xffd9e2eb, 0xff86c5f8);
|
||||
radialProgress.setProgressColor(0xffa2b5c7);
|
||||
}
|
||||
|
||||
super.setMessageObject(messageObject);
|
||||
}
|
||||
updateButtonState();
|
||||
updateButtonState(dataChanged);
|
||||
}
|
||||
|
||||
private Drawable getDrawableForCurrentState() {
|
||||
return ResourceLoader.audioStatesDrawable[currentMessageObject.isOut() ? buttonState : buttonState + 5][0];
|
||||
//buttonPressed ? 1 :
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -338,27 +358,18 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
|||
}
|
||||
|
||||
canvas.save();
|
||||
if (buttonState == 0 || buttonState == 1) {
|
||||
canvas.translate(seekBarX, seekBarY);
|
||||
seekBar.draw(canvas);
|
||||
} else {
|
||||
canvas.translate(seekBarX + AndroidUtilities.dp(12), seekBarY);
|
||||
progressView.draw(canvas);
|
||||
}
|
||||
canvas.translate(seekBarX, seekBarY);
|
||||
seekBar.draw(canvas);
|
||||
canvas.restore();
|
||||
|
||||
int state = buttonState;
|
||||
if (currentMessageObject.isOut()) {
|
||||
timePaint.setColor(0xff70b15c);
|
||||
circlePaint.setColor(0xff87bf78);
|
||||
} else {
|
||||
state += 5;
|
||||
timePaint.setColor(0xffa1aab3);
|
||||
circlePaint.setColor(0xff4195e5);
|
||||
}
|
||||
Drawable buttonDrawable = ResourceLoader.audioStatesDrawable[state][buttonPressed ? 1 : 0];
|
||||
setDrawableBounds(buttonDrawable, buttonX, buttonY);
|
||||
buttonDrawable.draw(canvas);
|
||||
radialProgress.onDraw(canvas);
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset);
|
||||
|
|
|
@ -445,7 +445,7 @@ public class ChatBaseCell extends BaseCell {
|
|||
mess = mess.substring(0, 150);
|
||||
}
|
||||
mess = mess.replace("\n", " ");
|
||||
stringFinalText = Emoji.replaceEmoji(mess, replyTextPaint.getFontMetricsInt(), AndroidUtilities.dp(14));
|
||||
stringFinalText = Emoji.replaceEmoji(mess, replyTextPaint.getFontMetricsInt(), AndroidUtilities.dp(14), false);
|
||||
stringFinalText = TextUtils.ellipsize(stringFinalText, replyTextPaint, maxWidth - AndroidUtilities.dp(8), TextUtils.TruncateAt.END);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,7 +108,6 @@ public class ChatContactCell extends ChatBaseCell {
|
|||
float y = event.getY();
|
||||
|
||||
boolean result = false;
|
||||
int side = AndroidUtilities.dp(36);
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if (x >= avatarImage.getImageX() && x <= avatarImage.getImageX() + namesWidth + AndroidUtilities.dp(42) && y >= avatarImage.getImageY() && y <= avatarImage.getImageY() + avatarImage.getImageHeight()) {
|
||||
avatarPressed = true;
|
||||
|
|
|
@ -379,7 +379,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
|
|||
|
||||
private Drawable getDrawableForCurrentState() {
|
||||
if (buttonState >= 0 && buttonState < 4) {
|
||||
Drawable currentButtonDrawable = null;
|
||||
if (currentMessageObject.type == 9 && gifDrawable == null) {
|
||||
if (buttonState == 1 && !currentMessageObject.isSending()) {
|
||||
return ResourceLoader.buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0];
|
||||
|
|
|
@ -193,7 +193,7 @@ public class ChatMessageCell extends ChatBaseCell {
|
|||
pressedLink.onClick(this);
|
||||
} else {
|
||||
TLRPC.WebPage webPage = currentMessageObject.messageOwner.media.webpage;
|
||||
if (Build.VERSION.SDK_INT >= 16 && webPage.embed_url != null && webPage.embed_url.length() != 0) {
|
||||
if (Build.VERSION.SDK_INT >= 19 && webPage.embed_url != null && webPage.embed_url.length() != 0) {
|
||||
delegate.needOpenWebView(webPage.embed_url, webPage.site_name, webPage.url, webPage.embed_width, webPage.embed_height);
|
||||
} else {
|
||||
Uri uri = Uri.parse(webPage.url);
|
||||
|
@ -536,7 +536,7 @@ public class ChatMessageCell extends ChatBaseCell {
|
|||
|
||||
if (webPage.photo != null) {
|
||||
boolean smallImage = webPage.type != null && (webPage.type.equals("app") || webPage.type.equals("profile") || webPage.type.equals("article"));
|
||||
if (smallImage && descriptionLayout != null && descriptionLayout.getLineCount() == 1) {
|
||||
if (smallImage && (descriptionLayout == null || descriptionLayout != null && descriptionLayout.getLineCount() == 1)) {
|
||||
smallImage = false;
|
||||
isSmallImage = false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* This is the source code of Telegram for Android v. 2.x.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-2015.
|
||||
*/
|
||||
|
||||
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 android.text.TextUtils;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SoundEffectConstants;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.android.ImageLoader;
|
||||
import org.telegram.android.MediaController;
|
||||
import org.telegram.android.MessageObject;
|
||||
import org.telegram.android.SendMessagesHelper;
|
||||
import org.telegram.messenger.FileLoader;
|
||||
import org.telegram.messenger.TLRPC;
|
||||
import org.telegram.ui.Components.RadialProgress;
|
||||
import org.telegram.ui.Components.ResourceLoader;
|
||||
import org.telegram.ui.Components.SeekBar;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ChatMusicCell extends ChatBaseCell implements SeekBar.SeekBarDelegate, MediaController.FileDownloadProgressListener {
|
||||
|
||||
public interface ChatMusicCellDelegate {
|
||||
boolean needPlayMusic(MessageObject messageObject);
|
||||
}
|
||||
|
||||
private static TextPaint timePaint;
|
||||
private static TextPaint titlePaint;
|
||||
private static TextPaint authorPaint;
|
||||
|
||||
private SeekBar seekBar;
|
||||
private int seekBarX;
|
||||
private int seekBarY;
|
||||
|
||||
private RadialProgress radialProgress;
|
||||
private int buttonState = 0;
|
||||
private int buttonX;
|
||||
private int buttonY;
|
||||
private boolean buttonPressed = false;
|
||||
|
||||
private StaticLayout timeLayout;
|
||||
private int timeX;
|
||||
private String lastTimeString = null;
|
||||
|
||||
private StaticLayout titleLayout;
|
||||
private int titleX;
|
||||
|
||||
private StaticLayout authorLayout;
|
||||
private int authorX;
|
||||
|
||||
private int TAG;
|
||||
|
||||
private ChatMusicCellDelegate musicDelegate;
|
||||
|
||||
public ChatMusicCell(Context context) {
|
||||
super(context);
|
||||
TAG = MediaController.getInstance().generateObserverTag();
|
||||
|
||||
seekBar = new SeekBar(context);
|
||||
seekBar.delegate = this;
|
||||
radialProgress = new RadialProgress(this);
|
||||
drawForwardedName = false;
|
||||
|
||||
if (timePaint == null) {
|
||||
timePaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
|
||||
timePaint.setTextSize(AndroidUtilities.dp(13));
|
||||
|
||||
titlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||
titlePaint.setTextSize(AndroidUtilities.dp(16));
|
||||
titlePaint.setColor(0xff212121);
|
||||
titlePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
|
||||
authorPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||
authorPaint.setTextSize(AndroidUtilities.dp(15));
|
||||
authorPaint.setColor(0xff212121);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
MediaController.getInstance().removeLoadingFileObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
updateButtonState(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
boolean result = seekBar.onTouch(event.getAction(), event.getX() - seekBarX, event.getY() - seekBarY);
|
||||
if (result) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}
|
||||
invalidate();
|
||||
} else {
|
||||
int side = AndroidUtilities.dp(36);
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if (x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side) {
|
||||
buttonPressed = true;
|
||||
invalidate();
|
||||
result = true;
|
||||
}
|
||||
} else if (buttonPressed) {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
buttonPressed = false;
|
||||
playSoundEffect(SoundEffectConstants.CLICK);
|
||||
didPressedButton();
|
||||
invalidate();
|
||||
} else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
|
||||
buttonPressed = false;
|
||||
invalidate();
|
||||
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
if (!(x >= buttonX && x <= buttonX + side && y >= buttonY && y <= buttonY + side)) {
|
||||
buttonPressed = false;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
result = super.onTouchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void didPressedButton() {
|
||||
if (buttonState == 0) {
|
||||
if (musicDelegate != null) {
|
||||
if (musicDelegate.needPlayMusic(currentMessageObject)) {
|
||||
buttonState = 1;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
} else if (buttonState == 1) {
|
||||
boolean result = MediaController.getInstance().pauseAudio(currentMessageObject);
|
||||
if (result) {
|
||||
buttonState = 0;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||
invalidate();
|
||||
}
|
||||
} else if (buttonState == 2) {
|
||||
FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.document, true, false);
|
||||
buttonState = 3;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), true, false);
|
||||
invalidate();
|
||||
} else if (buttonState == 3) {
|
||||
FileLoader.getInstance().cancelLoadFile(currentMessageObject.messageOwner.media.document);
|
||||
buttonState = 2;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||
invalidate();
|
||||
} else if (buttonState == 4) {
|
||||
if (currentMessageObject.isOut() && currentMessageObject.isSending()) {
|
||||
if (delegate != null) {
|
||||
delegate.didPressedCancelSendButton(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setMusicDelegate(ChatMusicCellDelegate delegate) {
|
||||
musicDelegate = delegate;
|
||||
}
|
||||
|
||||
public void updateProgress() {
|
||||
if (currentMessageObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!seekBar.isDragging()) {
|
||||
seekBar.setProgress(currentMessageObject.audioProgress);
|
||||
}
|
||||
|
||||
int duration = 0;
|
||||
int currentProgress = 0;
|
||||
for (TLRPC.DocumentAttribute attribute : currentMessageObject.messageOwner.media.document.attributes) {
|
||||
if (attribute instanceof TLRPC.TL_documentAttributeAudio) {
|
||||
duration = attribute.duration;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (MediaController.getInstance().isPlayingAudio(currentMessageObject)) {
|
||||
currentProgress = currentMessageObject.audioProgressSec;
|
||||
}
|
||||
String timeString = String.format("%d:%02d / %d:%02d", currentProgress / 60, currentProgress % 60, duration / 60, duration % 60);
|
||||
if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) {
|
||||
lastTimeString = timeString;
|
||||
int timeWidth = (int) Math.ceil(timePaint.measureText(timeString));
|
||||
timeLayout = new StaticLayout(timeString, timePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void downloadAudioIfNeed() {
|
||||
//if (buttonState == 2) {
|
||||
//FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.document, true, false);
|
||||
// buttonState = 3;
|
||||
// invalidate();
|
||||
//}
|
||||
}
|
||||
|
||||
public void updateButtonState(boolean animated) {
|
||||
if (currentMessageObject == null) {
|
||||
return;
|
||||
}
|
||||
if (currentMessageObject.isOut() && currentMessageObject.isSending()) {
|
||||
MediaController.getInstance().addLoadingFileObserver(currentMessageObject.messageOwner.attachPath, this);
|
||||
buttonState = 4;
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), true, animated);
|
||||
Float progress = ImageLoader.getInstance().getFileProgress(currentMessageObject.messageOwner.attachPath);
|
||||
if (progress == null && SendMessagesHelper.getInstance().isSendingMessage(currentMessageObject.getId())) {
|
||||
progress = 1.0f;
|
||||
}
|
||||
radialProgress.setProgress(progress != null ? progress : 0, false);
|
||||
} else {
|
||||
File cacheFile = null;
|
||||
if (currentMessageObject.messageOwner.attachPath != null && currentMessageObject.messageOwner.attachPath.length() > 0) {
|
||||
cacheFile = new File(currentMessageObject.messageOwner.attachPath);
|
||||
if(!cacheFile.exists()) {
|
||||
cacheFile = null;
|
||||
}
|
||||
}
|
||||
if (cacheFile == null) {
|
||||
cacheFile = FileLoader.getPathToMessage(currentMessageObject.messageOwner);
|
||||
}
|
||||
if (cacheFile.exists()) {
|
||||
MediaController.getInstance().removeLoadingFileObserver(this);
|
||||
boolean playing = MediaController.getInstance().isPlayingAudio(currentMessageObject);
|
||||
if (!playing || playing && MediaController.getInstance().isAudioPaused()) {
|
||||
buttonState = 0;
|
||||
} else {
|
||||
buttonState = 1;
|
||||
}
|
||||
radialProgress.setProgress(0, animated);
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
|
||||
} else {
|
||||
String fileName = currentMessageObject.getFileName();
|
||||
MediaController.getInstance().addLoadingFileObserver(fileName, this);
|
||||
if (!FileLoader.getInstance().isLoadingFile(fileName)) {
|
||||
buttonState = 2;
|
||||
radialProgress.setProgress(0, animated);
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
|
||||
} else {
|
||||
buttonState = 3;
|
||||
Float progress = ImageLoader.getInstance().getFileProgress(fileName);
|
||||
if (progress != null) {
|
||||
radialProgress.setProgress(progress, animated);
|
||||
} else {
|
||||
radialProgress.setProgress(0, animated);
|
||||
}
|
||||
radialProgress.setBackground(getDrawableForCurrentState(), true, animated);
|
||||
}
|
||||
}
|
||||
}
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailedDownload(String fileName) {
|
||||
updateButtonState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccessDownload(String fileName) {
|
||||
updateButtonState(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressDownload(String fileName, float progress) {
|
||||
radialProgress.setProgress(progress, true);
|
||||
if (buttonState != 3) {
|
||||
updateButtonState(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
|
||||
radialProgress.setProgress(progress, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getObserverTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSeekBarDrag(float progress) {
|
||||
if (currentMessageObject == null) {
|
||||
return;
|
||||
}
|
||||
currentMessageObject.audioProgress = progress;
|
||||
MediaController.getInstance().seekToProgress(currentMessageObject, progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
setMeasuredDimension(width, AndroidUtilities.dp(78) + namesOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
|
||||
if (currentMessageObject.isOut()) {
|
||||
seekBarX = layoutWidth - backgroundWidth + AndroidUtilities.dp(52);
|
||||
buttonX = layoutWidth - backgroundWidth + AndroidUtilities.dp(13);
|
||||
timeX = layoutWidth - backgroundWidth + AndroidUtilities.dp(63);
|
||||
} else {
|
||||
if (isChat) {
|
||||
seekBarX = AndroidUtilities.dp(113);
|
||||
buttonX = AndroidUtilities.dp(74);
|
||||
timeX = AndroidUtilities.dp(124);
|
||||
} else {
|
||||
seekBarX = AndroidUtilities.dp(61);
|
||||
buttonX = AndroidUtilities.dp(22);
|
||||
timeX = AndroidUtilities.dp(72);
|
||||
}
|
||||
}
|
||||
|
||||
seekBar.width = backgroundWidth - AndroidUtilities.dp(67);
|
||||
seekBar.height = AndroidUtilities.dp(30);
|
||||
seekBarY = AndroidUtilities.dp(26) + namesOffset;
|
||||
buttonY = AndroidUtilities.dp(13) + namesOffset;
|
||||
radialProgress.setProgressRect(buttonX, buttonY, buttonX + AndroidUtilities.dp(40), buttonY + AndroidUtilities.dp(40));
|
||||
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageObject(MessageObject messageObject) {
|
||||
boolean dataChanged = currentMessageObject == messageObject && isUserDataChanged();
|
||||
if (currentMessageObject != messageObject || dataChanged) {
|
||||
if (AndroidUtilities.isTablet()) {
|
||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(isChat ? 102 : 50), AndroidUtilities.dp(300));
|
||||
} else {
|
||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(isChat ? 102 : 50), AndroidUtilities.dp(300));
|
||||
}
|
||||
|
||||
if (messageObject.isOut()) {
|
||||
seekBar.type = 0;
|
||||
radialProgress.setProgressColor(0xff87bf78);
|
||||
} else {
|
||||
seekBar.type = 1;
|
||||
radialProgress.setProgressColor(0xffa2b5c7);
|
||||
}
|
||||
|
||||
int maxWidth = backgroundWidth - AndroidUtilities.dp(86);
|
||||
|
||||
CharSequence stringFinal = TextUtils.ellipsize(messageObject.getMusicTitle().replace("\n", " "), titlePaint, maxWidth, TextUtils.TruncateAt.END);
|
||||
titleLayout = new StaticLayout(stringFinal, titlePaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||
if (titleLayout.getLineCount() > 0) {
|
||||
titleX = (int) Math.ceil(titleLayout.getLineLeft(0));
|
||||
}
|
||||
|
||||
stringFinal = TextUtils.ellipsize(messageObject.getMusicAuthor().replace("\n", " "), authorPaint, maxWidth, TextUtils.TruncateAt.END);
|
||||
authorLayout = new StaticLayout(stringFinal, authorPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||
if (authorLayout.getLineCount() > 0) {
|
||||
authorX = (int) Math.ceil(authorLayout.getLineLeft(0));
|
||||
}
|
||||
|
||||
super.setMessageObject(messageObject);
|
||||
}
|
||||
updateButtonState(dataChanged);
|
||||
}
|
||||
|
||||
private Drawable getDrawableForCurrentState() {
|
||||
return ResourceLoader.audioStatesDrawable[currentMessageObject.isOut() ? buttonState : buttonState + 5][0];
|
||||
//buttonPressed ? 1 :
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
if (currentMessageObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentMessageObject.isOut()) {
|
||||
timePaint.setColor(0xff70b15c);
|
||||
} else {
|
||||
timePaint.setColor(0xffa1aab3);
|
||||
}
|
||||
radialProgress.onDraw(canvas);
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(timeX + titleX, AndroidUtilities.dp(12) + namesOffset);
|
||||
titleLayout.draw(canvas);
|
||||
canvas.restore();
|
||||
|
||||
canvas.save();
|
||||
if (MediaController.getInstance().isPlayingAudio(currentMessageObject)) {
|
||||
canvas.translate(seekBarX, seekBarY);
|
||||
seekBar.draw(canvas);
|
||||
} else {
|
||||
canvas.translate(timeX + authorX, AndroidUtilities.dp(32) + namesOffset);
|
||||
authorLayout.draw(canvas);
|
||||
}
|
||||
canvas.restore();
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(timeX, AndroidUtilities.dp(52) + namesOffset);
|
||||
timeLayout.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
|
@ -356,15 +356,7 @@ public class DialogCell extends BaseCell {
|
|||
if (message.isOut()) {
|
||||
name = LocaleController.getString("FromYou", R.string.FromYou);
|
||||
} else {
|
||||
if (UserObject.isDeleted(fromUser)) {
|
||||
name = "Deleted";
|
||||
} else {
|
||||
if (fromUser.first_name != null && fromUser.first_name.length() > 0) {
|
||||
name = fromUser.first_name;
|
||||
} else {
|
||||
name = fromUser.last_name;
|
||||
}
|
||||
}
|
||||
name = UserObject.getFirstName(fromUser);
|
||||
}
|
||||
checkMessage = false;
|
||||
if (message.caption != null) {
|
||||
|
@ -373,11 +365,11 @@ public class DialogCell extends BaseCell {
|
|||
mess = mess.substring(0, 150);
|
||||
}
|
||||
mess = mess.replace("\n", " ");
|
||||
messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("<c#ff4d83b3>%s:</c> <c#ff808080>%s</c>", name, mess), AndroidUtilities.FLAG_TAG_COLOR), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20));
|
||||
messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("<c#ff4d83b3>%s:</c> <c#ff808080>%s</c>", name, mess), AndroidUtilities.FLAG_TAG_COLOR), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||
} else {
|
||||
if (message.messageOwner.media != null && !message.isMediaEmpty()) {
|
||||
currentMessagePaint = messagePrintingPaint;
|
||||
messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("<c#ff4d83b3>%s:</c> <c#ff4d83b3>%s</c>", name, message.messageText), AndroidUtilities.FLAG_TAG_COLOR), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20));
|
||||
messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("<c#ff4d83b3>%s:</c> <c#ff4d83b3>%s</c>", name, message.messageText), AndroidUtilities.FLAG_TAG_COLOR), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||
} else {
|
||||
if (message.messageOwner.message != null) {
|
||||
String mess = message.messageOwner.message;
|
||||
|
@ -385,7 +377,7 @@ public class DialogCell extends BaseCell {
|
|||
mess = mess.substring(0, 150);
|
||||
}
|
||||
mess = mess.replace("\n", " ");
|
||||
messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("<c#ff4d83b3>%s:</c> <c#ff808080>%s</c>", name, mess), AndroidUtilities.FLAG_TAG_COLOR), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20));
|
||||
messageString = Emoji.replaceEmoji(AndroidUtilities.replaceTags(String.format("<c#ff4d83b3>%s:</c> <c#ff808080>%s</c>", name, mess), AndroidUtilities.FLAG_TAG_COLOR), messagePaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -580,7 +572,7 @@ public class DialogCell extends BaseCell {
|
|||
mess = mess.substring(0, 150);
|
||||
}
|
||||
mess = mess.replace("\n", " ");
|
||||
messageString = Emoji.replaceEmoji(mess, messagePaint.getFontMetricsInt(), AndroidUtilities.dp(17));
|
||||
messageString = Emoji.replaceEmoji(mess, messagePaint.getFontMetricsInt(), AndroidUtilities.dp(17), false);
|
||||
}
|
||||
messageWidth = Math.max(AndroidUtilities.dp(12), messageWidth);
|
||||
CharSequence messageStringFinal = TextUtils.ellipsize(messageString, currentMessagePaint, messageWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END);
|
||||
|
|
|
@ -13,7 +13,6 @@ import android.widget.FrameLayout;
|
|||
import android.widget.ImageView;
|
||||
|
||||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
|
||||
public class PhotoAttachCameraCell extends FrameLayout {
|
||||
|
@ -23,7 +22,7 @@ public class PhotoAttachCameraCell extends FrameLayout {
|
|||
|
||||
ImageView imageView = new ImageView(context);
|
||||
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||
imageView.setImageResource(R.drawable.ic_attach_photobig);
|
||||
//imageView.setImageResource(R.drawable.ic_attach_photobig);
|
||||
imageView.setBackgroundColor(0xff777777);
|
||||
addView(imageView, LayoutHelper.createFrame(80, 80));
|
||||
}
|
||||
|
|
|
@ -81,5 +81,6 @@ public class PhotoAttachPhotoCell extends FrameLayout {
|
|||
|
||||
public void setOnCheckClickLisnener(OnClickListener onCheckClickLisnener) {
|
||||
checkFrame.setOnClickListener(onCheckClickLisnener);
|
||||
imageView.setOnClickListener(onCheckClickLisnener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F
|
|||
});
|
||||
|
||||
nameTextView = new TextView(context);
|
||||
nameTextView.setTextColor(0xff222222);
|
||||
nameTextView.setTextColor(0xff212121);
|
||||
nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
nameTextView.setLines(1);
|
||||
|
@ -318,6 +318,9 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F
|
|||
|
||||
@Override
|
||||
public void onProgressDownload(String fileName, float progress) {
|
||||
if (progressView.getVisibility() != VISIBLE) {
|
||||
updateFileExistIcon();
|
||||
}
|
||||
progressView.setProgress(progress, true);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public class SharedMediaSectionCell extends FrameLayout {
|
|||
textView = new TextView(getContext());
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
textView.setTextColor(0xff222222);
|
||||
textView.setTextColor(0xff212121);
|
||||
textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
|
||||
addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 13, 0, 13, 0));
|
||||
}
|
||||
|
|
|
@ -63,14 +63,14 @@ public class StickerEmojiCell extends FrameLayout {
|
|||
for (TLRPC.DocumentAttribute attribute : document.attributes) {
|
||||
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
|
||||
if (attribute.alt != null && attribute.alt.length() > 0) {
|
||||
emojiTextView.setText(Emoji.replaceEmoji(attribute.alt, emojiTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16)));
|
||||
emojiTextView.setText(Emoji.replaceEmoji(attribute.alt, emojiTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16), false));
|
||||
set = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!set) {
|
||||
emojiTextView.setText(Emoji.replaceEmoji(StickersQuery.getEmojiForSticker(sticker.id), emojiTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16)));
|
||||
emojiTextView.setText(Emoji.replaceEmoji(StickersQuery.getEmojiForSticker(sticker.id), emojiTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16), false));
|
||||
}
|
||||
emojiTextView.setVisibility(VISIBLE);
|
||||
} else {
|
||||
|
|
|
@ -36,7 +36,7 @@ public class TextDetailCell extends FrameLayout {
|
|||
textView.setMaxLines(1);
|
||||
textView.setSingleLine(true);
|
||||
textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
|
||||
addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 16 : 71, 10, LocaleController.isRTL ? 16 : 71, 0));
|
||||
addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 16 : 71, 10, LocaleController.isRTL ? 71 : 16, 0));
|
||||
|
||||
valueTextView = new TextView(context);
|
||||
valueTextView.setTextColor(0xff8a8a8a);
|
||||
|
@ -45,7 +45,7 @@ public class TextDetailCell extends FrameLayout {
|
|||
valueTextView.setMaxLines(1);
|
||||
valueTextView.setSingleLine(true);
|
||||
valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
|
||||
addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 16 : 71, 35, LocaleController.isRTL ? 16 : 71, 0));
|
||||
addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT, LocaleController.isRTL ? 16 : 71, 35, LocaleController.isRTL ? 71 : 16, 0));
|
||||
|
||||
imageView = new ImageView(context);
|
||||
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||
|
|
|
@ -16,7 +16,6 @@ import android.text.InputType;
|
|||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -57,7 +56,7 @@ public class ChangeChatNameActivity extends BaseFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
actionBar.setAllowOverlayTitle(true);
|
||||
actionBar.setTitle(LocaleController.getString("EditName", R.string.EditName));
|
||||
|
|
|
@ -15,7 +15,6 @@ import android.text.InputType;
|
|||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -50,7 +49,7 @@ public class ChangeNameActivity extends BaseFragment {
|
|||
private final static int done_button = 1;
|
||||
|
||||
@Override
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
actionBar.setAllowOverlayTitle(true);
|
||||
actionBar.setTitle(LocaleController.getString("EditName", R.string.EditName));
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.text.TextWatcher;
|
|||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
|
@ -102,7 +101,7 @@ public class ChangePhoneActivity extends BaseFragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
actionBar.setTitle(LocaleController.getString("AppName", R.string.AppName));
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
|
||||
|
|
|
@ -13,7 +13,6 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
@ -36,7 +35,7 @@ import org.telegram.ui.Components.LayoutHelper;
|
|||
public class ChangePhoneHelpActivity extends BaseFragment {
|
||||
|
||||
@Override
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
actionBar.setAllowOverlayTitle(true);
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import android.text.TextWatcher;
|
|||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -62,7 +61,7 @@ public class ChangeUsernameActivity extends BaseFragment {
|
|||
private final static int done_button = 1;
|
||||
|
||||
@Override
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||
actionBar.setAllowOverlayTitle(true);
|
||||
actionBar.setTitle(LocaleController.getString("Username", R.string.Username));
|
||||
|
|
|
@ -14,7 +14,6 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
|
@ -32,7 +31,6 @@ import android.util.Base64;
|
|||
import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -40,6 +38,7 @@ import android.view.ViewTreeObserver;
|
|||
import android.view.WindowManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
@ -96,6 +95,7 @@ import org.telegram.ui.ActionBar.ActionBar;
|
|||
import org.telegram.ui.ActionBar.ActionBarMenu;
|
||||
import org.telegram.ui.ActionBar.ActionBarMenuItem;
|
||||
import org.telegram.ui.Cells.ChatMessageCell;
|
||||
import org.telegram.ui.Cells.ChatMusicCell;
|
||||
import org.telegram.ui.Cells.ChatUnreadCell;
|
||||
import org.telegram.ui.Components.AlertsCreator;
|
||||
import org.telegram.ui.Components.AvatarDrawable;
|
||||
|
@ -123,7 +123,7 @@ import java.util.Collections;
|
|||
import java.util.HashMap;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
public class ChatActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, MessagesActivity.MessagesActivityDelegate,
|
||||
public class ChatActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, DialogsActivity.MessagesActivityDelegate,
|
||||
PhotoViewer.PhotoViewerProvider {
|
||||
|
||||
protected TLRPC.Chat currentChat;
|
||||
|
@ -177,6 +177,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
private ListView mentionListView;
|
||||
private AnimatorSetProxy mentionListAnimation;
|
||||
private ChatAttachView chatAttachView;
|
||||
private BottomSheet chatAttachViewSheet;
|
||||
|
||||
private boolean allowStickersPanel;
|
||||
private AnimatorSetProxy runningAnimation;
|
||||
|
@ -455,6 +456,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.removeAllMessagesFromDialog);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioProgressDidChanged);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.screenshotTook);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.blockedUsersDidLoaded);
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileNewChunkAvailable);
|
||||
|
@ -558,6 +560,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.botInfoDidLoaded);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.botKeyboardDidLoaded);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatSearchResultsAvailable);
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||
|
||||
if (AndroidUtilities.isTablet()) {
|
||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.openedChatChanged, dialog_id, true);
|
||||
|
@ -578,11 +581,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
chatAttachView.onDestroy();
|
||||
}
|
||||
AndroidUtilities.unlockOrientation(getParentActivity());
|
||||
MediaController.getInstance().stopAudio();
|
||||
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
|
||||
if (messageObject != null && !messageObject.isMusic()) {
|
||||
MediaController.getInstance().stopAudio();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context, LayoutInflater inflater) {
|
||||
public View createView(Context context) {
|
||||
|
||||
for (int a = 0; a < 8; a++) {
|
||||
chatMessageCellsCache.add(new ChatMessageCell(context));
|
||||
|
@ -595,6 +601,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
lastStatus = null;
|
||||
hasOwnBackground = true;
|
||||
chatAttachView = null;
|
||||
chatAttachViewSheet = null;
|
||||
|
||||
ResourceLoader.loadRecources(context);
|
||||
|
||||
|
@ -673,7 +680,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
Bundle args = new Bundle();
|
||||
args.putBoolean("onlySelect", true);
|
||||
args.putInt("dialogsType", 1);
|
||||
MessagesActivity fragment = new MessagesActivity(args);
|
||||
DialogsActivity fragment = new DialogsActivity(args);
|
||||
fragment.setDelegate(ChatActivity.this);
|
||||
presentFragment(fragment);
|
||||
} else if (id == chat_enc_timer) {
|
||||
|
@ -770,20 +777,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
selectedMessagesCanCopyIds.clear();
|
||||
actionBar.hideActionMode();
|
||||
updateVisibleRows();
|
||||
}/* else if (id == chat_menu_attach) {
|
||||
} else if (id == chat_menu_attach) {
|
||||
if (getParentActivity() == null) {
|
||||
return;
|
||||
}
|
||||
BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity());
|
||||
|
||||
if (chatAttachView == null) {
|
||||
BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity());
|
||||
chatAttachView = new ChatAttachView(getParentActivity());
|
||||
chatAttachView.setDelegate(new ChatAttachView.ChatAttachViewDelegate() {
|
||||
@Override
|
||||
public void didPressedButton(int button) {
|
||||
if (visibleDialog != null) {
|
||||
visibleDialog.dismiss();
|
||||
}
|
||||
if (button == 7) {
|
||||
chatAttachViewSheet.dismiss();
|
||||
HashMap<Integer, MediaController.PhotoEntry> selectedPhotos = chatAttachView.getSelectedPhotos();
|
||||
if (!selectedPhotos.isEmpty()) {
|
||||
ArrayList<String> photos = new ArrayList<>();
|
||||
|
@ -796,31 +802,50 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
showReplyPanel(false, null, null, null, false, true);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
chatAttachViewSheet.dismissWithButtonClick(button);
|
||||
}
|
||||
processSelectedAttach(button);
|
||||
}
|
||||
});
|
||||
builder.setDelegate(new BottomSheet.BottomSheetDelegate() {
|
||||
|
||||
@Override
|
||||
public void onRevealAnimationStart(boolean open) {
|
||||
chatAttachView.onRevealAnimationStart(open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealAnimationProgress(boolean open, float radius, int x, int y) {
|
||||
chatAttachView.onRevealAnimationProgress(open, radius, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRevealAnimationEnd(boolean open) {
|
||||
chatAttachView.onRevealAnimationEnd(open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpenAnimationEnd() {
|
||||
chatAttachView.onRevealAnimationEnd(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getRevealView() {
|
||||
return menuItem;
|
||||
}
|
||||
});
|
||||
builder.setApplyTopPaddings(false);
|
||||
builder.setUseRevealAnimation();
|
||||
builder.setCustomView(chatAttachView);
|
||||
chatAttachViewSheet = builder.create();
|
||||
}
|
||||
builder.setCustomView(chatAttachView);
|
||||
final int coords[] = new int[2];
|
||||
menuItem.getLocationInWindow(coords);
|
||||
builder.setRevealAnimation(coords[0] + menuItem.getWidth() / 2, coords[1] + menuItem.getHeight() / 2);
|
||||
builder.setDelegate(new BottomSheet.BottomSheetDelegate() {
|
||||
@Override
|
||||
public void onOpenAnimationStart() {
|
||||
chatAttachView.startAnimations(coords[1] > AndroidUtilities.displaySize.y - AndroidUtilities.dp(100));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpenAnimationEnd() {
|
||||
|
||||
}
|
||||
});
|
||||
chatAttachView.init(ChatActivity.this);
|
||||
showDialog(builder.create());
|
||||
}*/ else if (id == attach_gallery || id == attach_video || id == attach_document || id == attach_location || id == attach_photo || id == attach_audio || id == attach_contact) {
|
||||
showDialog(chatAttachViewSheet);
|
||||
}/* else if (id == attach_gallery || id == attach_video || id == attach_document || id == attach_location || id == attach_photo || id == attach_audio || id == attach_contact) {
|
||||
processSelectedAttach(id);
|
||||
} else if (id == bot_help) {
|
||||
} */else if (id == bot_help) {
|
||||
SendMessagesHelper.getInstance().sendMessage("/help", dialog_id, null, null, false);
|
||||
} else if (id == bot_settings) {
|
||||
SendMessagesHelper.getInstance().sendMessage("/settings", dialog_id, null, null, false);
|
||||
|
@ -922,12 +947,26 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
ActionBarMenu menu = actionBar.createMenu();
|
||||
|
||||
if (currentEncryptedChat == null && !isBroadcast) {
|
||||
/*searchItem = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true, false).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() {
|
||||
searchItem = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true, false).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() {
|
||||
|
||||
@Override
|
||||
public void onSearchCollapse() {
|
||||
avatarContainer.setVisibility(View.VISIBLE);
|
||||
headerItem.setVisibility(View.VISIBLE);
|
||||
if (chatActivityEnterView.hasText()) {
|
||||
if (headerItem != null) {
|
||||
headerItem.setVisibility(View.GONE);
|
||||
}
|
||||
if (attachItem != null) {
|
||||
attachItem.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
if (headerItem != null) {
|
||||
headerItem.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (attachItem != null) {
|
||||
attachItem.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
searchItem.setVisibility(View.GONE);
|
||||
//chatActivityEnterView.setVisibility(View.VISIBLE);
|
||||
searchUpItem.clearAnimation();
|
||||
|
@ -945,7 +984,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
searchItem.getSearchField().requestFocus();
|
||||
AndroidUtilities.showKeyboard(searchItem.getSearchField());
|
||||
}
|
||||
}, 200); //TODO find a better way to open keyboard
|
||||
}, 300); //TODO find a better way to open keyboard
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -960,7 +999,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
searchUpItem = menu.addItem(search_up, R.drawable.search_up);
|
||||
searchUpItem.setVisibility(View.GONE);
|
||||
searchDownItem = menu.addItem(search_down, R.drawable.search_down);
|
||||
searchDownItem.setVisibility(View.GONE);*/
|
||||
searchDownItem.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
headerItem = menu.addItem(0, R.drawable.ic_ab_other);
|
||||
|
@ -990,22 +1029,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
updateSubtitle();
|
||||
updateTitleIcons();
|
||||
|
||||
attachItem = menu.addItem(chat_menu_attach, R.drawable.ic_ab_other).setAllowCloseAnimation(false);
|
||||
attachItem.addSubItem(attach_photo, LocaleController.getString("ChatTakePhoto", R.string.ChatTakePhoto), R.drawable.ic_attach_photo);
|
||||
attachItem.addSubItem(attach_gallery, LocaleController.getString("ChatGallery", R.string.ChatGallery), R.drawable.ic_attach_gallery);
|
||||
attachItem.addSubItem(attach_video, LocaleController.getString("ChatVideo", R.string.ChatVideo), R.drawable.ic_attach_video);
|
||||
attachItem.addSubItem(attach_document, LocaleController.getString("ChatDocument", R.string.ChatDocument), R.drawable.ic_ab_doc);
|
||||
attachItem.addSubItem(attach_location, LocaleController.getString("ChatLocation", R.string.ChatLocation), R.drawable.ic_attach_location);
|
||||
attachItem = menu.addItem(chat_menu_attach, R.drawable.ic_ab_other).setOverrideMenuClick(true).setAllowCloseAnimation(false);
|
||||
attachItem.setVisibility(View.GONE);
|
||||
|
||||
menuItem = menu.addItem(chat_menu_attach, R.drawable.ic_ab_attach).setAllowCloseAnimation(false);
|
||||
menuItem.addSubItem(attach_photo, LocaleController.getString("ChatTakePhoto", R.string.ChatTakePhoto), R.drawable.ic_attach_photo);
|
||||
menuItem.addSubItem(attach_gallery, LocaleController.getString("ChatGallery", R.string.ChatGallery), R.drawable.ic_attach_gallery);
|
||||
menuItem.addSubItem(attach_video, LocaleController.getString("ChatVideo", R.string.ChatVideo), R.drawable.ic_attach_video);
|
||||
menuItem.addSubItem(attach_document, LocaleController.getString("ChatDocument", R.string.ChatDocument), R.drawable.ic_ab_doc);
|
||||
menuItem.addSubItem(attach_location, LocaleController.getString("ChatLocation", R.string.ChatLocation), R.drawable.ic_attach_location);
|
||||
menuItem.setShowFromBottom(true);
|
||||
|
||||
menuItem.setBackgroundDrawable(null);
|
||||
|
||||
actionModeViews.clear();
|
||||
|
@ -1053,8 +1079,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
|
@ -1067,14 +1091,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
}
|
||||
|
||||
int childCount = getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child == chatActivityEnterView) {
|
||||
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||
inputFieldHeight = child.getMeasuredHeight();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
measureChildWithMargins(chatActivityEnterView, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||
inputFieldHeight = chatActivityEnterView.getMeasuredHeight();
|
||||
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = getChildAt(i);
|
||||
if (child.getVisibility() == GONE || child == chatActivityEnterView) {
|
||||
|
@ -1660,6 +1680,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
@Override
|
||||
public void onAttachButtonHidden() {
|
||||
if (actionBar.isSearchFieldVisible()) {
|
||||
return;
|
||||
}
|
||||
if (attachItem != null) {
|
||||
attachItem.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
@ -1670,6 +1693,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
@Override
|
||||
public void onAttachButtonShow() {
|
||||
if (actionBar.isSearchFieldVisible()) {
|
||||
return;
|
||||
}
|
||||
if (attachItem != null) {
|
||||
attachItem.setVisibility(View.GONE);
|
||||
}
|
||||
|
@ -1680,7 +1706,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
@Override
|
||||
public void onWindowSizeChanged(int size) {
|
||||
if (size < AndroidUtilities.dp(72) + AndroidUtilities.getCurrentActionBarHeight()) {
|
||||
if (size < AndroidUtilities.dp(72) + ActionBar.getCurrentActionBarHeight()) {
|
||||
allowStickersPanel = false;
|
||||
if (stickersPanel.getVisibility() == View.VISIBLE) {
|
||||
stickersPanel.clearAnimation();
|
||||
|
@ -2089,12 +2115,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
});
|
||||
presentFragment(fragment);
|
||||
} else if (which == attach_audio) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
|
||||
startActivityForResult(intent, 32);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
}
|
||||
AudioSelectActivity fragment = new AudioSelectActivity();
|
||||
fragment.setDelegate(new AudioSelectActivity.AudioSelectActivityDelegate() {
|
||||
@Override
|
||||
public void didSelectAudio(ArrayList<MessageObject> audios) {
|
||||
SendMessagesHelper.prepareSendingAudioDocuments(audios, dialog_id, replyingMessageObject);
|
||||
showReplyPanel(false, null, null, null, false, true);
|
||||
}
|
||||
});
|
||||
presentFragment(fragment);
|
||||
} else if (which == attach_contact) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
|
||||
|
@ -2216,7 +2245,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
mess = mess.substring(0, 150);
|
||||
}
|
||||
mess = mess.replace("\n", " ");
|
||||
replyObjectTextView.setText(Emoji.replaceEmoji(mess, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14)));
|
||||
replyObjectTextView.setText(Emoji.replaceEmoji(mess, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false));
|
||||
}
|
||||
} else if (messageObjects != null) {
|
||||
if (messageObjects.isEmpty()) {
|
||||
|
@ -2277,7 +2306,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
mess = mess.substring(0, 150);
|
||||
}
|
||||
mess = mess.replace("\n", " ");
|
||||
replyObjectTextView.setText(Emoji.replaceEmoji(mess, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14)));
|
||||
replyObjectTextView.setText(Emoji.replaceEmoji(mess, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false));
|
||||
} else {
|
||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedMessage", messageObjects.size()));
|
||||
}
|
||||
|
@ -2296,7 +2325,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
}
|
||||
} else if (type == 12) {
|
||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedContact", messageObjects.size()));
|
||||
} else if (type == 2) {
|
||||
} else if (type == 2 || type == 14) {
|
||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedAudio", messageObjects.size()));
|
||||
} else if (type == 13) {
|
||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedSticker", messageObjects.size()));
|
||||
|
@ -3221,22 +3250,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
FileLog.e("tmessages", e);
|
||||
}
|
||||
}
|
||||
} else if (requestCode == 32) {
|
||||
if (data == null || data.getData() == null) {
|
||||
showAttachmentError();
|
||||
return;
|
||||
}
|
||||
Uri uri = data.getData();
|
||||
String path = AndroidUtilities.getPath(uri);
|
||||
if (path != null) {
|
||||
TLRPC.TL_audio audio = new TLRPC.TL_audio();
|
||||
audio.dc_id = Integer.MIN_VALUE;
|
||||
audio.id = Integer.MIN_VALUE;
|
||||
audio.user_id = UserConfig.getClientUserId();
|
||||
audio.mime_type = "audio/mp3";
|
||||
SendMessagesHelper.getInstance().sendMessage(audio, path, dialog_id, replyingMessageObject);
|
||||
showReplyPanel(false, null, null, null, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3587,7 +3600,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
if (currentEncryptedChat != null && obj.isOut() && obj.messageOwner.action != null && obj.messageOwner.action instanceof TLRPC.TL_messageEncryptedAction &&
|
||||
obj.messageOwner.action.encryptedAction instanceof TLRPC.TL_decryptedMessageActionSetMessageTTL && getParentActivity() != null) {
|
||||
TLRPC.TL_decryptedMessageActionSetMessageTTL action = (TLRPC.TL_decryptedMessageActionSetMessageTTL) obj.messageOwner.action.encryptedAction;
|
||||
if (AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) < 17 && currentEncryptedChat.ttl > 0 && currentEncryptedChat.ttl <= 60) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
||||
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
|
||||
|
@ -4005,7 +4017,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
int encId = (Integer) args[0];
|
||||
if (currentEncryptedChat != null && currentEncryptedChat.id == encId) {
|
||||
int date = (Integer) args[1];
|
||||
boolean started = false;
|
||||
for (MessageObject obj : messages) {
|
||||
if (!obj.isOut()) {
|
||||
continue;
|
||||
|
@ -4018,7 +4029,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
}
|
||||
updateVisibleRows();
|
||||
}
|
||||
} else if (id == NotificationCenter.audioDidReset) {
|
||||
} else if (id == NotificationCenter.audioDidReset || id == NotificationCenter.audioPlayStateChanged) {
|
||||
Integer mid = (Integer) args[0];
|
||||
if (chatListView != null) {
|
||||
int count = chatListView.getChildCount();
|
||||
|
@ -4027,7 +4038,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
if (view instanceof ChatAudioCell) {
|
||||
ChatAudioCell cell = (ChatAudioCell) view;
|
||||
if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) {
|
||||
cell.updateButtonState();
|
||||
cell.updateButtonState(false);
|
||||
break;
|
||||
}
|
||||
} else if (view instanceof ChatMusicCell) {
|
||||
ChatMusicCell cell = (ChatMusicCell) view;
|
||||
if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) {
|
||||
cell.updateButtonState(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -4045,6 +4062,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
cell.updateProgress();
|
||||
break;
|
||||
}
|
||||
} else if (view instanceof ChatMusicCell) {
|
||||
ChatMusicCell cell = (ChatMusicCell) view;
|
||||
if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) {
|
||||
MessageObject playing = cell.getMessageObject();
|
||||
MessageObject player = MediaController.getInstance().getPlayingMessageObject();
|
||||
playing.audioProgress = player.audioProgress;
|
||||
playing.audioProgressSec = player.audioProgressSec;
|
||||
cell.updateProgress();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4121,6 +4148,27 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
} else if (id == NotificationCenter.audioDidStarted) {
|
||||
MessageObject messageObject = (MessageObject) args[0];
|
||||
sendSecretMessageRead(messageObject);
|
||||
|
||||
int mid = messageObject.getId();
|
||||
if (chatListView != null) {
|
||||
int count = chatListView.getChildCount();
|
||||
for (int a = 0; a < count; a++) {
|
||||
View view = chatListView.getChildAt(a);
|
||||
if (view instanceof ChatAudioCell) {
|
||||
ChatAudioCell cell = (ChatAudioCell) view;
|
||||
if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) {
|
||||
cell.updateButtonState(false);
|
||||
break;
|
||||
}
|
||||
} else if (view instanceof ChatMusicCell) {
|
||||
ChatMusicCell cell = (ChatMusicCell) view;
|
||||
if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) {
|
||||
cell.updateButtonState(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (id == NotificationCenter.updateMessageMedia) {
|
||||
MessageObject messageObject = (MessageObject) args[0];
|
||||
MessageObject existMessageObject = messagesDict.get(messageObject.getId());
|
||||
|
@ -4555,7 +4603,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
actionBar.setBackButtonImage(R.drawable.ic_close_white);
|
||||
}
|
||||
}
|
||||
int padding = (AndroidUtilities.getCurrentActionBarHeight() - AndroidUtilities.dp(48)) / 2;
|
||||
int padding = (ActionBar.getCurrentActionBarHeight() - AndroidUtilities.dp(48)) / 2;
|
||||
avatarContainer.setPadding(avatarContainer.getPaddingLeft(), padding, avatarContainer.getPaddingRight(), padding);
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) avatarContainer.getLayoutParams();
|
||||
layoutParams.topMargin = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
|
||||
|
@ -4629,7 +4677,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
options = new int[]{8, 2, 3, 1};
|
||||
} else if (type == 4) {
|
||||
if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
|
||||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
String saveString;
|
||||
if (selectedObject.isMusic()) {
|
||||
saveString = LocaleController.getString("SaveToMusic", R.string.SaveToMusic);
|
||||
} else {
|
||||
saveString = LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads);
|
||||
}
|
||||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), saveString, LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
options = new int[]{8, 10, 4, 2, 1};
|
||||
} else {
|
||||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
|
@ -4639,7 +4693,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
options = new int[]{8, 5, 4, 2, 1};
|
||||
} else if (type == 6) {
|
||||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
String saveString;
|
||||
if (selectedObject.isMusic()) {
|
||||
saveString = LocaleController.getString("SaveToMusic", R.string.SaveToMusic);
|
||||
} else {
|
||||
saveString = LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads);
|
||||
}
|
||||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("SaveToGallery", R.string.SaveToGallery), saveString, LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
options = new int[]{8, 7, 10, 6, 2, 1};
|
||||
} else if (type == 7) {
|
||||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("AddToStickers", R.string.AddToStickers), LocaleController.getString("Delete", R.string.Delete)};
|
||||
|
@ -4654,7 +4714,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
options = new int[]{2, 3, 1};
|
||||
} else if (type == 4) {
|
||||
if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
|
||||
items = new CharSequence[]{LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
String saveString;
|
||||
if (selectedObject.isMusic()) {
|
||||
saveString = LocaleController.getString("SaveToMusic", R.string.SaveToMusic);
|
||||
} else {
|
||||
saveString = LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads);
|
||||
}
|
||||
items = new CharSequence[]{saveString, LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
options = new int[]{10, 4, 2, 1};
|
||||
} else {
|
||||
items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
|
@ -4664,7 +4730,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
items = new CharSequence[]{LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
options = new int[]{5, 4, 2, 1};
|
||||
} else if (type == 6) {
|
||||
items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
String saveString;
|
||||
if (selectedObject.isMusic()) {
|
||||
saveString = LocaleController.getString("SaveToMusic", R.string.SaveToMusic);
|
||||
} else {
|
||||
saveString = LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads);
|
||||
}
|
||||
items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), saveString, LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
||||
options = new int[]{7, 10, 6, 2, 1};
|
||||
} else if (type == 7) {
|
||||
items = new CharSequence[]{LocaleController.getString("Reply", R.string.Reply), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("AddToStickers", R.string.AddToStickers), LocaleController.getString("Delete", R.string.Delete)};
|
||||
|
@ -4680,7 +4752,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
options = new int[]{3, 1};
|
||||
} else if (type == 4) {
|
||||
if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
|
||||
items = new CharSequence[]{LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads), LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Delete", R.string.Delete)};
|
||||
String saveString;
|
||||
if (selectedObject.isMusic()) {
|
||||
saveString = LocaleController.getString("SaveToMusic", R.string.SaveToMusic);
|
||||
} else {
|
||||
saveString = LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads);
|
||||
}
|
||||
items = new CharSequence[]{saveString, LocaleController.getString("ShareFile", R.string.ShareFile), LocaleController.getString("Delete", R.string.Delete)};
|
||||
options = new int[]{10, 4, 1};
|
||||
} else {
|
||||
items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Delete", R.string.Delete)};
|
||||
|
@ -4770,7 +4848,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
Bundle args = new Bundle();
|
||||
args.putBoolean("onlySelect", true);
|
||||
args.putInt("dialogsType", 1);
|
||||
MessagesActivity fragment = new MessagesActivity(args);
|
||||
DialogsActivity fragment = new DialogsActivity(args);
|
||||
fragment.setDelegate(this);
|
||||
presentFragment(fragment);
|
||||
} else if (option == 3) {
|
||||
|
@ -4787,7 +4865,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
FileLog.e("tmessages", e);
|
||||
}
|
||||
} else if (option == 4) {
|
||||
String fileName = selectedObject.getFileName();
|
||||
String path = selectedObject.messageOwner.attachPath;
|
||||
if (path != null && path.length() > 0) {
|
||||
File temp = new File(path);
|
||||
|
@ -4802,7 +4879,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
MediaController.saveFile(path, getParentActivity(), 1, null);
|
||||
} else if (selectedObject.type == 1) {
|
||||
MediaController.saveFile(path, getParentActivity(), 0, null);
|
||||
} else if (selectedObject.type == 8 || selectedObject.type == 9) {
|
||||
} else if (selectedObject.type == 8 || selectedObject.type == 9 || selectedObject.type == 14) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType(selectedObject.messageOwner.media.document.mime_type);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path)));
|
||||
|
@ -4837,7 +4914,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
}
|
||||
}
|
||||
} else if (option == 6 || option == 7) {
|
||||
String fileName = selectedObject.getFileName();
|
||||
String path = selectedObject.messageOwner.attachPath;
|
||||
if (path != null && path.length() > 0) {
|
||||
File temp = new File(path);
|
||||
|
@ -4848,7 +4924,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
if (path == null || path.length() == 0) {
|
||||
path = FileLoader.getPathToMessage(selectedObject.messageOwner).toString();
|
||||
}
|
||||
if (selectedObject.type == 8 || selectedObject.type == 9) {
|
||||
if (selectedObject.type == 8 || selectedObject.type == 9 || selectedObject.type == 14) {
|
||||
if (option == 6) {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType(selectedObject.messageOwner.media.document.mime_type);
|
||||
|
@ -4877,13 +4953,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
if (path == null || path.length() == 0) {
|
||||
path = FileLoader.getPathToMessage(selectedObject.messageOwner).toString();
|
||||
}
|
||||
MediaController.saveFile(path, getParentActivity(), 2, fileName);
|
||||
MediaController.saveFile(path, getParentActivity(), selectedObject.isMusic() ? 3 : 2, fileName);
|
||||
}
|
||||
selectedObject = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didSelectDialog(MessagesActivity activity, long did, boolean param) {
|
||||
public void didSelectDialog(DialogsActivity activity, long did, boolean param) {
|
||||
if (dialog_id != 0 && (forwaringMessage != null || !selectedMessagesIds.isEmpty())) {
|
||||
ArrayList<MessageObject> fmessages = new ArrayList<>();
|
||||
if (forwaringMessage != null) {
|
||||
|
@ -4961,7 +5037,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
|
||||
public boolean isGoogleMapsInstalled() {
|
||||
try {
|
||||
ApplicationInfo info = ApplicationLoader.applicationContext.getPackageManager().getApplicationInfo("com.google.android.apps.maps", 0);
|
||||
ApplicationLoader.applicationContext.getPackageManager().getApplicationInfo("com.google.android.apps.maps", 0);
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
if (getParentActivity() == null) {
|
||||
|
@ -5213,7 +5289,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
if (url.startsWith("@")) {
|
||||
MessagesController.openByUserName(url.substring(1), ChatActivity.this, 0);
|
||||
} else if (url.startsWith("#")) {
|
||||
MessagesActivity fragment = new MessagesActivity(null);
|
||||
DialogsActivity fragment = new DialogsActivity(null);
|
||||
fragment.setSearchString(url);
|
||||
presentFragment(fragment);
|
||||
} else if (url.startsWith("/")) {
|
||||
|
@ -5221,6 +5297,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
}
|
||||
}
|
||||
});
|
||||
} else if (viewType == 8) {
|
||||
view = new ChatMusicCell(mContext);
|
||||
}
|
||||
|
||||
if (view instanceof ChatBaseCell) {
|
||||
|
@ -5261,7 +5339,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
if (url.startsWith("@")) {
|
||||
MessagesController.openByUserName(url.substring(1), ChatActivity.this, 0);
|
||||
} else if (url.startsWith("#")) {
|
||||
MessagesActivity fragment = new MessagesActivity(null);
|
||||
DialogsActivity fragment = new DialogsActivity(null);
|
||||
fragment.setSearchString(url);
|
||||
presentFragment(fragment);
|
||||
} else if (url.startsWith("/")) {
|
||||
|
@ -5273,7 +5351,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
public void needOpenWebView(String url, String title, String originalUrl, int w, int h) {
|
||||
BottomSheet.Builder builder = new BottomSheet.Builder(mContext);
|
||||
builder.setCustomView(new WebFrameLayout(mContext, builder.create(), title, originalUrl, url, w, h));
|
||||
builder.setOverrideTabletWidth(true);
|
||||
builder.setUseFullWidth(true);
|
||||
showDialog(builder.create());
|
||||
}
|
||||
|
||||
|
@ -5434,6 +5512,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
|||
showDialog(builder.create());
|
||||
}
|
||||
});
|
||||
} else if (view instanceof ChatMusicCell) {
|
||||
((ChatMusicCell) view).setMusicDelegate(new ChatMusicCell.ChatMusicCellDelegate() {
|
||||
@Override
|
||||
public boolean needPlayMusic(MessageObject messageObject) {
|
||||
return MediaController.getInstance().setPlaylist(messages, messageObject);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (view instanceof ChatActionCell) {
|
||||
((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() {
|
||||
|
|
|
@ -57,7 +57,7 @@ public class BotKeyboardView extends LinearLayout {
|
|||
|
||||
public void setPanelHeight(int height) {
|
||||
panelHeight = height;
|
||||
if (isFullSize && botButtons != null) {
|
||||
if (isFullSize && botButtons != null && botButtons.rows.size() != 0) {
|
||||
buttonHeight = !isFullSize ? 42 : (int) Math.max(42, (panelHeight - AndroidUtilities.dp(30) - (botButtons.rows.size() - 1) * AndroidUtilities.dp(10)) / botButtons.rows.size() / AndroidUtilities.density);
|
||||
int count = container.getChildCount();
|
||||
int newHeight = AndroidUtilities.dp(buttonHeight);
|
||||
|
@ -87,7 +87,7 @@ public class BotKeyboardView extends LinearLayout {
|
|||
container.removeAllViews();
|
||||
buttonViews.clear();
|
||||
|
||||
if (buttons != null) {
|
||||
if (buttons != null && botButtons.rows.size() != 0) {
|
||||
isFullSize = (buttons.flags & 1) == 0;
|
||||
buttonHeight = !isFullSize ? 42 : (int) Math.max(42, (panelHeight - AndroidUtilities.dp(30) - (botButtons.rows.size() - 1) * AndroidUtilities.dp(10)) / botButtons.rows.size() / AndroidUtilities.density);
|
||||
for (int a = 0; a < buttons.rows.size(); a++) {
|
||||
|
@ -106,7 +106,7 @@ public class BotKeyboardView extends LinearLayout {
|
|||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setBackgroundResource(R.drawable.bot_keyboard_states);
|
||||
textView.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0);
|
||||
textView.setText(Emoji.replaceEmoji(button.text, textView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16)));
|
||||
textView.setText(Emoji.replaceEmoji(button.text, textView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(16), false));
|
||||
layout.addView(textView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, weight, 0, 0, b != row.buttons.size() - 1 ? 10 : 0, 0));
|
||||
textView.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.telegram.android.NotificationCenter;
|
|||
import org.telegram.messenger.R;
|
||||
import org.telegram.messenger.TLRPC;
|
||||
import org.telegram.messenger.UserConfig;
|
||||
import org.telegram.ui.ActionBar.ActionBar;
|
||||
import org.telegram.ui.ActionBar.BaseFragment;
|
||||
import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy;
|
||||
import org.telegram.android.AnimationCompat.AnimatorSetProxy;
|
||||
|
@ -117,6 +118,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
private BaseFragment parentFragment;
|
||||
private long dialog_id;
|
||||
private boolean ignoreTextChange;
|
||||
private int innerTextChange;
|
||||
private MessageObject replyingMessageObject;
|
||||
private MessageObject botMessageObject;
|
||||
private TLRPC.WebPage messageWebPage;
|
||||
|
@ -187,7 +189,16 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
}
|
||||
});
|
||||
|
||||
messageEditText = new EditText(context);
|
||||
messageEditText = new EditText(context) {
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (isPopupShowing() && event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
showPopup(AndroidUtilities.usingHardwareInput ? 0 : 2, 0);
|
||||
openKeyboardInternal();
|
||||
}
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
};
|
||||
messageEditText.setHint(LocaleController.getString("TypeMessage", R.string.TypeMessage));
|
||||
messageEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||
messageEditText.setInputType(messageEditText.getInputType() | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE);
|
||||
|
@ -216,14 +227,6 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
return false;
|
||||
}
|
||||
});
|
||||
messageEditText.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (isPopupShowing()) {
|
||||
showPopup(AndroidUtilities.usingHardwareInput ? 0 : 2, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
|
||||
|
@ -240,6 +243,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
}
|
||||
});
|
||||
messageEditText.addTextChangedListener(new TextWatcher() {
|
||||
boolean processChange = false;
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||
|
||||
|
@ -247,16 +252,20 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
|
||||
String message = getTrimmedString(charSequence.toString());
|
||||
if (innerTextChange == 1) {
|
||||
return;
|
||||
}
|
||||
checkSendButton(true);
|
||||
|
||||
String message = getTrimmedString(charSequence.toString());
|
||||
if (delegate != null) {
|
||||
if (count > 2 || charSequence == null || charSequence.length() == 0) {
|
||||
messageWebPageSearch = true;
|
||||
}
|
||||
delegate.onTextChanged(charSequence, before > count + 1 || (count - before) > 2);
|
||||
}
|
||||
|
||||
if (innerTextChange != 2 && before != count && (count - before) > 1) {
|
||||
processChange = true;
|
||||
}
|
||||
if (message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) {
|
||||
int currentTime = ConnectionsManager.getInstance().getCurrentTime();
|
||||
TLRPC.User currentUser = null;
|
||||
|
@ -275,19 +284,19 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if (innerTextChange != 0) {
|
||||
return;
|
||||
}
|
||||
if (sendByEnter && editable.length() > 0 && editable.charAt(editable.length() - 1) == '\n') {
|
||||
sendMessage();
|
||||
}
|
||||
int i = 0;
|
||||
ImageSpan[] arrayOfImageSpan = editable.getSpans(0, editable.length(), ImageSpan.class);
|
||||
int j = arrayOfImageSpan.length;
|
||||
while (true) {
|
||||
if (i >= j) {
|
||||
Emoji.replaceEmoji(editable, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20));
|
||||
return;
|
||||
if (processChange) {
|
||||
ImageSpan[] spans = editable.getSpans(0, editable.length(), ImageSpan.class);
|
||||
for (int i = 0; i < spans.length; i++) {
|
||||
editable.removeSpan(spans[i]);
|
||||
}
|
||||
editable.removeSpan(arrayOfImageSpan[i]);
|
||||
i++;
|
||||
Emoji.replaceEmoji(editable, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||
processChange = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -590,7 +599,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
delegate.onWindowSizeChanged(size);
|
||||
}
|
||||
if (topView != null) {
|
||||
if (size < AndroidUtilities.dp(72) + AndroidUtilities.getCurrentActionBarHeight()) {
|
||||
if (size < AndroidUtilities.dp(72) + ActionBar.getCurrentActionBarHeight()) {
|
||||
if (allowShowTopView) {
|
||||
allowShowTopView = false;
|
||||
if (needShowTopView) {
|
||||
|
@ -773,11 +782,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
});
|
||||
runningAnimation2.start();
|
||||
|
||||
if (messageEditText != null) {
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(0);
|
||||
messageEditText.setLayoutParams(layoutParams);
|
||||
}
|
||||
updateFieldRight(0);
|
||||
|
||||
delegate.onAttachButtonHidden();
|
||||
}
|
||||
|
@ -823,9 +828,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
attachButton.setVisibility(View.GONE);
|
||||
attachButton.clearAnimation();
|
||||
delegate.onAttachButtonHidden();
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(0);
|
||||
messageEditText.setLayoutParams(layoutParams);
|
||||
updateFieldRight(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -854,11 +857,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
runningAnimation2.setDuration(100);
|
||||
runningAnimation2.start();
|
||||
|
||||
if (messageEditText != null) {
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(50);
|
||||
messageEditText.setLayoutParams(layoutParams);
|
||||
}
|
||||
updateFieldRight(1);
|
||||
|
||||
delegate.onAttachButtonShow();
|
||||
}
|
||||
|
@ -903,14 +902,37 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
if (attachButton != null) {
|
||||
delegate.onAttachButtonShow();
|
||||
attachButton.setVisibility(View.VISIBLE);
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(50);
|
||||
messageEditText.setLayoutParams(layoutParams);
|
||||
updateFieldRight(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateFieldRight(int attachVisible) {
|
||||
if (messageEditText == null) {
|
||||
return;
|
||||
}
|
||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
||||
if (attachVisible == 1) {
|
||||
if (botButton != null && botButton.getVisibility() == VISIBLE) {
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(98);
|
||||
} else {
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(50);
|
||||
}
|
||||
} else if (attachVisible == 2) {
|
||||
if (layoutParams.rightMargin != AndroidUtilities.dp(2)) {
|
||||
if (botButton != null && botButton.getVisibility() == VISIBLE) {
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(98);
|
||||
} else {
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(50);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
layoutParams.rightMargin = AndroidUtilities.dp(2);
|
||||
}
|
||||
messageEditText.setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
private void updateAudioRecordIntefrace() {
|
||||
if (recordingAudio) {
|
||||
if (audioInterfaceState == 1) {
|
||||
|
@ -1108,6 +1130,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
} else {
|
||||
botButton.setVisibility(GONE);
|
||||
}
|
||||
updateFieldRight(2);
|
||||
ViewProxy.setPivotX(attachButton, AndroidUtilities.dp(botButton.getVisibility() == GONE ? 48 : 96));
|
||||
attachButton.clearAnimation();
|
||||
}
|
||||
|
@ -1209,12 +1232,15 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
|||
i = 0;
|
||||
}
|
||||
try {
|
||||
CharSequence localCharSequence = Emoji.replaceEmoji(symbol/* + "\uFE0F"*/, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20));
|
||||
innerTextChange = 2;
|
||||
CharSequence localCharSequence = Emoji.replaceEmoji(symbol/* + "\uFE0F"*/, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||
messageEditText.setText(messageEditText.getText().insert(i, localCharSequence));
|
||||
int j = i + localCharSequence.length();
|
||||
messageEditText.setSelection(j, j);
|
||||
} catch (Exception e) {
|
||||
FileLog.e("tmessages", e);
|
||||
} finally {
|
||||
innerTextChange = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
|
||||
package org.telegram.ui.Components;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
@ -18,6 +21,7 @@ import android.view.Gravity;
|
|||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
@ -25,14 +29,16 @@ import android.widget.TextView;
|
|||
import org.telegram.android.AndroidUtilities;
|
||||
import org.telegram.android.LocaleController;
|
||||
import org.telegram.android.MediaController;
|
||||
import org.telegram.android.NotificationCenter;
|
||||
import org.telegram.android.support.widget.LinearLayoutManager;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.Adapters.PhotoAttachAdapter;
|
||||
import org.telegram.ui.ChatActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ChatAttachView extends FrameLayout {
|
||||
public class ChatAttachView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate {
|
||||
|
||||
public interface ChatAttachViewDelegate {
|
||||
void didPressedButton(int button);
|
||||
|
@ -42,7 +48,16 @@ public class ChatAttachView extends FrameLayout {
|
|||
private PhotoAttachAdapter photoAttachAdapter;
|
||||
private ChatActivity baseFragment;
|
||||
private AttachButton sendPhotosButton;
|
||||
private AttachButton buttons[] = new AttachButton[8];
|
||||
private View views[] = new View[20];
|
||||
private RecyclerListView attachPhotoRecyclerView;
|
||||
private View lineView;
|
||||
private EmptyTextProgressView progressView;
|
||||
|
||||
private float[] distCache = new float[20];
|
||||
|
||||
private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
|
||||
|
||||
private boolean loading;
|
||||
|
||||
private ChatAttachViewDelegate delegate;
|
||||
|
||||
|
@ -56,7 +71,6 @@ public class ChatAttachView extends FrameLayout {
|
|||
|
||||
imageView = new ImageView(context);
|
||||
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||
//imageView.setColorFilter(0x33000000);
|
||||
addView(imageView, LayoutHelper.createFrame(64, 64, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
|
||||
|
||||
textView = new TextView(context);
|
||||
|
@ -83,10 +97,15 @@ public class ChatAttachView extends FrameLayout {
|
|||
public ChatAttachView(Context context) {
|
||||
super(context);
|
||||
|
||||
RecyclerListView attachPhotoRecyclerView = new RecyclerListView(context);
|
||||
if (photoAttachAdapter != null) {
|
||||
photoAttachAdapter.onDestroy();
|
||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.albumsDidLoaded);
|
||||
if (MediaController.allPhotosAlbumEntry == null) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
MediaController.loadGalleryPhotosAlbums(0);
|
||||
}
|
||||
loading = true;
|
||||
}
|
||||
|
||||
views[8] = attachPhotoRecyclerView = new RecyclerListView(context);
|
||||
attachPhotoRecyclerView.setVerticalScrollBarEnabled(true);
|
||||
attachPhotoRecyclerView.setAdapter(photoAttachAdapter = new PhotoAttachAdapter(context));
|
||||
attachPhotoRecyclerView.setClipToPadding(false);
|
||||
|
@ -112,11 +131,14 @@ public class ChatAttachView extends FrameLayout {
|
|||
}
|
||||
});
|
||||
|
||||
View lineView = new View(getContext());
|
||||
views[9] = progressView = new EmptyTextProgressView(context);
|
||||
progressView.setText(LocaleController.getString("NoPhotos", R.string.NoPhotos));
|
||||
addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 80));
|
||||
attachPhotoRecyclerView.setEmptyView(progressView);
|
||||
|
||||
views[10] = lineView = new View(getContext());
|
||||
lineView.setBackgroundColor(0xffd2d2d2);
|
||||
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.TOP | Gravity.LEFT);
|
||||
layoutParams.topMargin = AndroidUtilities.dp(88);
|
||||
addView(lineView, layoutParams);
|
||||
addView(lineView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.TOP | Gravity.LEFT));
|
||||
CharSequence[] items = new CharSequence[]{
|
||||
LocaleController.getString("ChatCamera", R.string.ChatCamera),
|
||||
LocaleController.getString("ChatGallery", R.string.ChatGallery),
|
||||
|
@ -128,23 +150,21 @@ public class ChatAttachView extends FrameLayout {
|
|||
""
|
||||
};
|
||||
int itemIcons[] = new int[] {
|
||||
R.drawable.ic_attach_photo_big,
|
||||
R.drawable.ic_attach_gallery_big,
|
||||
R.drawable.ic_attach_video_big,
|
||||
R.drawable.ic_attach_music_big,
|
||||
R.drawable.ic_attach_file_big,
|
||||
R.drawable.ic_attach_contact_big,
|
||||
R.drawable.ic_attach_location_big,
|
||||
R.drawable.ic_attach_hide_big,
|
||||
R.drawable.attach_camera_states,
|
||||
R.drawable.attach_gallery_states,
|
||||
R.drawable.attach_video_states,
|
||||
R.drawable.attach_audio_states,
|
||||
R.drawable.attach_file_states,
|
||||
R.drawable.attach_contact_states,
|
||||
R.drawable.attach_location_states,
|
||||
R.drawable.attach_hide_states,
|
||||
};
|
||||
for (int a = 0; a < 8; a++) {
|
||||
AttachButton attachButton = new AttachButton(context);
|
||||
attachButton.setTextAndIcon(items[a], itemIcons[a]);
|
||||
int y = 97 + 95 * (a / 4);
|
||||
int x = 10 + (a % 4) * 85;
|
||||
addView(attachButton, LayoutHelper.createFrame(85, 90, Gravity.LEFT | Gravity.TOP, x, y, 0, 0));
|
||||
addView(attachButton, LayoutHelper.createFrame(85, 90, Gravity.LEFT | Gravity.TOP));
|
||||
attachButton.setTag(a);
|
||||
buttons[a] = attachButton;
|
||||
views[a] = attachButton;
|
||||
if (a == 7) {
|
||||
sendPhotosButton = attachButton;
|
||||
sendPhotosButton.imageView.setPadding(0, AndroidUtilities.dp(4), 0, 0);
|
||||
|
@ -164,24 +184,58 @@ public class ChatAttachView extends FrameLayout {
|
|||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
progressView.showProgress();
|
||||
} else {
|
||||
progressView.showTextView();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didReceivedNotification(int id, Object... args) {
|
||||
if (id == NotificationCenter.albumsDidLoaded) {
|
||||
if (photoAttachAdapter != null) {
|
||||
loading = false;
|
||||
progressView.showTextView();
|
||||
photoAttachAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthSpec, int heightSpec) {
|
||||
super.onMeasure(widthSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(278), MeasureSpec.EXACTLY));
|
||||
super.onMeasure(widthSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(294), MeasureSpec.EXACTLY));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
int width = right - left;
|
||||
|
||||
int t = AndroidUtilities.dp(8);
|
||||
attachPhotoRecyclerView.layout(0, t, width, t + attachPhotoRecyclerView.getMeasuredHeight());
|
||||
progressView.layout(0, t, width, t + progressView.getMeasuredHeight());
|
||||
lineView.layout(0, AndroidUtilities.dp(96), width, AndroidUtilities.dp(96) + lineView.getMeasuredHeight());
|
||||
|
||||
int diff = (width - AndroidUtilities.dp(85 * 4 + 20)) / 3;
|
||||
for (int a = 0; a < 8; a++) {
|
||||
int y = AndroidUtilities.dp(105 + 95 * (a / 4));
|
||||
int x = AndroidUtilities.dp(10) + (a % 4) * (AndroidUtilities.dp(85) + diff);
|
||||
views[a].layout(x, y, x + views[a].getMeasuredWidth(), y + views[a].getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePhotosButton() {
|
||||
int count = photoAttachAdapter.getSelectedPhotos().size();
|
||||
if (count == 0) {
|
||||
sendPhotosButton.imageView.setPadding(0, AndroidUtilities.dp(4), 0, 0);
|
||||
sendPhotosButton.imageView.setBackgroundResource(R.drawable.ic_attach_hide_big);
|
||||
sendPhotosButton.imageView.setImageResource(R.drawable.ic_attach_hide_big_icon);
|
||||
sendPhotosButton.imageView.setBackgroundResource(R.drawable.attach_hide_states);
|
||||
sendPhotosButton.imageView.setImageResource(R.drawable.attach_hide2);
|
||||
sendPhotosButton.textView.setText("");
|
||||
} else {
|
||||
sendPhotosButton.imageView.setPadding(AndroidUtilities.dp(2), 0, 0, 0);
|
||||
sendPhotosButton.imageView.setBackgroundResource(R.drawable.ic_attach_send_big);
|
||||
sendPhotosButton.imageView.setImageResource(R.drawable.ic_attach_send_big_icon);
|
||||
sendPhotosButton.imageView.setBackgroundResource(R.drawable.attach_send_states);
|
||||
sendPhotosButton.imageView.setImageResource(R.drawable.attach_send2);
|
||||
sendPhotosButton.textView.setText(LocaleController.formatString("SendItems", R.string.SendItems, String.format("(%d)", count)));
|
||||
}
|
||||
}
|
||||
|
@ -190,22 +244,83 @@ public class ChatAttachView extends FrameLayout {
|
|||
delegate = chatAttachViewDelegate;
|
||||
}
|
||||
|
||||
public void startAnimations(boolean up) {
|
||||
for (int a = 0; a < 4; a++) {
|
||||
//buttons[a].setTranslationY(AndroidUtilities.dp(up ? 20 : -20));
|
||||
//buttons[a + 4].setTranslationY(AndroidUtilities.dp(up ? 20 : -20));
|
||||
buttons[a].setScaleX(0.8f);
|
||||
buttons[a].setScaleY(0.8f);
|
||||
buttons[a + 4].setScaleX(0.8f);
|
||||
buttons[a + 4].setScaleY(0.8f);
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(ObjectAnimator.ofFloat(buttons[a], "scaleX", 1),
|
||||
ObjectAnimator.ofFloat(buttons[a + 4], "scaleX", 1),
|
||||
ObjectAnimator.ofFloat(buttons[a], "scaleY", 1),
|
||||
ObjectAnimator.ofFloat(buttons[a + 4], "scaleY", 1));
|
||||
animatorSet.setDuration(150);
|
||||
animatorSet.setStartDelay((3 - a) * 40);
|
||||
animatorSet.start();
|
||||
public void onRevealAnimationEnd(boolean open) {
|
||||
if (open && Build.VERSION.SDK_INT <= 19 && MediaController.allPhotosAlbumEntry == null) {
|
||||
MediaController.loadGalleryPhotosAlbums(0);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public void onRevealAnimationStart(boolean open) {
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
int count = Build.VERSION.SDK_INT <= 19 ? 11 : 8;
|
||||
for (int a = 0; a < count; a++) {
|
||||
if (Build.VERSION.SDK_INT <= 19) {
|
||||
if (a < 8) {
|
||||
views[a].setScaleX(0.1f);
|
||||
views[a].setScaleY(0.1f);
|
||||
}
|
||||
views[a].setAlpha(0.0f);
|
||||
} else {
|
||||
views[a].setScaleX(0.7f);
|
||||
views[a].setScaleY(0.7f);
|
||||
}
|
||||
views[a].setTag(R.string.AppName, null);
|
||||
distCache[a] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public void onRevealAnimationProgress(boolean open, float radius, int x, int y) {
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
int count = Build.VERSION.SDK_INT <= 19 ? 11 : 8;
|
||||
for (int a = 0; a < count; a++) {
|
||||
if (views[a].getTag(R.string.AppName) == null) {
|
||||
if (distCache[a] == 0) {
|
||||
int buttonX = views[a].getLeft() + views[a].getMeasuredWidth() / 2;
|
||||
int buttonY = views[a].getTop() + views[a].getMeasuredHeight() / 2;
|
||||
distCache[a] = (float) Math.sqrt((x - buttonX) * (x - buttonX) + (y - buttonY) * (y - buttonY));
|
||||
float vecX = (x - buttonX) / distCache[a];
|
||||
float vecY = (y - buttonY) / distCache[a];
|
||||
views[a].setPivotX(views[a].getMeasuredWidth() / 2 + vecX * AndroidUtilities.dp(20));
|
||||
views[a].setPivotY(views[a].getMeasuredHeight() / 2 + vecY * AndroidUtilities.dp(20));
|
||||
}
|
||||
if (distCache[a] > radius + AndroidUtilities.dp(27)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
views[a].setTag(R.string.AppName, 1);
|
||||
final ArrayList<Animator> animators = new ArrayList<>();
|
||||
final ArrayList<Animator> animators2 = new ArrayList<>();
|
||||
if (a < 8) {
|
||||
animators.add(ObjectAnimator.ofFloat(views[a], "scaleX", 0.7f, 1.05f));
|
||||
animators.add(ObjectAnimator.ofFloat(views[a], "scaleY", 0.7f, 1.05f));
|
||||
animators2.add(ObjectAnimator.ofFloat(views[a], "scaleX", 1.0f));
|
||||
animators2.add(ObjectAnimator.ofFloat(views[a], "scaleY", 1.0f));
|
||||
}
|
||||
if (Build.VERSION.SDK_INT <= 19) {
|
||||
animators.add(ObjectAnimator.ofFloat(views[a], "alpha", 1.0f));
|
||||
}
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(animators);
|
||||
animatorSet.setDuration(150);
|
||||
animatorSet.setInterpolator(decelerateInterpolator);
|
||||
animatorSet.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
AnimatorSet animatorSet = new AnimatorSet();
|
||||
animatorSet.playTogether(animators2);
|
||||
animatorSet.setDuration(100);
|
||||
animatorSet.setInterpolator(decelerateInterpolator);
|
||||
animatorSet.start();
|
||||
}
|
||||
});
|
||||
animatorSet.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,7 +336,7 @@ public class ChatAttachView extends FrameLayout {
|
|||
}
|
||||
|
||||
public void onDestroy() {
|
||||
photoAttachAdapter.onDestroy();
|
||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.albumsDidLoaded);
|
||||
baseFragment = null;
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue