mirror of
https://github.com/DrKLO/Telegram.git
synced 2024-12-22 14:35:03 +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 {
|
defaultConfig {
|
||||||
minSdkVersion 8
|
minSdkVersion 8
|
||||||
targetSdkVersion 22
|
targetSdkVersion 22
|
||||||
versionCode 572
|
versionCode 580
|
||||||
versionName "3.0.1"
|
versionName "3.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,8 +161,20 @@
|
||||||
|
|
||||||
<service android:name="org.telegram.android.NotificationsService" android:enabled="true"/>
|
<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.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.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">
|
<receiver android:name="org.telegram.android.AppStartReceiver" android:enabled="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
package org.telegram.SQLite;
|
package org.telegram.SQLite;
|
||||||
|
|
||||||
import org.telegram.messenger.BuildVars;
|
|
||||||
import org.telegram.messenger.FileLog;
|
import org.telegram.messenger.FileLog;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -30,7 +29,7 @@ public class SQLitePreparedStatement {
|
||||||
public SQLitePreparedStatement(SQLiteDatabase db, String sql, boolean finalize) throws SQLiteException {
|
public SQLitePreparedStatement(SQLiteDatabase db, String sql, boolean finalize) throws SQLiteException {
|
||||||
finalizeAfterQuery = finalize;
|
finalizeAfterQuery = finalize;
|
||||||
sqliteStatementHandle = prepare(db.getSQLiteHandle(), sql);
|
sqliteStatementHandle = prepare(db.getSQLiteHandle(), sql);
|
||||||
if (BuildVars.DEBUG_VERSION) {
|
/*if (BuildVars.DEBUG_VERSION) {
|
||||||
if (hashMap == null) {
|
if (hashMap == null) {
|
||||||
hashMap = new HashMap<>();
|
hashMap = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
@ -38,7 +37,7 @@ public class SQLitePreparedStatement {
|
||||||
for (HashMap.Entry<SQLitePreparedStatement, String> entry : hashMap.entrySet()) {
|
for (HashMap.Entry<SQLitePreparedStatement, String> entry : hashMap.entrySet()) {
|
||||||
FileLog.d("tmessages", "exist entry = " + entry.getValue());
|
FileLog.d("tmessages", "exist entry = " + entry.getValue());
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,9 +100,9 @@ public class SQLitePreparedStatement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (BuildVars.DEBUG_VERSION) {
|
/*if (BuildVars.DEBUG_VERSION) {
|
||||||
hashMap.remove(this);
|
hashMap.remove(this);
|
||||||
}
|
}*/
|
||||||
isFinalized = true;
|
isFinalized = true;
|
||||||
finalize(sqliteStatementHandle);
|
finalize(sqliteStatementHandle);
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteException e) {
|
||||||
|
|
|
@ -542,16 +542,6 @@ public class AndroidUtilities {
|
||||||
return 0;
|
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() {
|
public static Point getRealScreenSize() {
|
||||||
Point size = new Point();
|
Point size = new Point();
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -66,13 +66,13 @@ public class AnimatorSetProxy {
|
||||||
|
|
||||||
public void playTogether(ArrayList<Object> items) {
|
public void playTogether(ArrayList<Object> items) {
|
||||||
if (View10.NEED_PROXY) {
|
if (View10.NEED_PROXY) {
|
||||||
ArrayList<Animator10> animators = new ArrayList<Animator10>();
|
ArrayList<Animator10> animators = new ArrayList<>();
|
||||||
for (Object obj : items) {
|
for (Object obj : items) {
|
||||||
animators.add((Animator10)obj);
|
animators.add((Animator10)obj);
|
||||||
}
|
}
|
||||||
((AnimatorSet10) animatorSet).playTogether(animators);
|
((AnimatorSet10) animatorSet).playTogether(animators);
|
||||||
} else {
|
} else {
|
||||||
ArrayList<Animator> animators = new ArrayList<Animator>();
|
ArrayList<Animator> animators = new ArrayList<>();
|
||||||
for (Object obj : items) {
|
for (Object obj : items) {
|
||||||
animators.add((Animator)obj);
|
animators.add((Animator)obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
import android.provider.ContactsContract;
|
import android.provider.ContactsContract;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import org.telegram.PhoneFormat.PhoneFormat;
|
import org.telegram.PhoneFormat.PhoneFormat;
|
||||||
|
@ -330,7 +331,7 @@ public class ContactsController {
|
||||||
ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver();
|
ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver();
|
||||||
|
|
||||||
HashMap<String, Contact> shortContacts = new HashMap<>();
|
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);
|
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectionPhones, null, null, null);
|
||||||
if (pCur != null) {
|
if (pCur != null) {
|
||||||
if (pCur.getCount() > 0) {
|
if (pCur.getCount() > 0) {
|
||||||
|
@ -355,10 +356,9 @@ public class ContactsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer id = pCur.getInt(0);
|
Integer id = pCur.getInt(0);
|
||||||
if (ids.length() != 0) {
|
if (!idsArr.contains(id)) {
|
||||||
ids.append(",");
|
idsArr.add(id);
|
||||||
}
|
}
|
||||||
ids.append(id);
|
|
||||||
|
|
||||||
int type = pCur.getInt(2);
|
int type = pCur.getInt(2);
|
||||||
Contact contact = contactsMap.get(id);
|
Contact contact = contactsMap.get(id);
|
||||||
|
@ -392,8 +392,9 @@ public class ContactsController {
|
||||||
}
|
}
|
||||||
pCur.close();
|
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) {
|
if (pCur != null && pCur.getCount() > 0) {
|
||||||
while (pCur.moveToNext()) {
|
while (pCur.moveToNext()) {
|
||||||
int id = pCur.getInt(0);
|
int id = pCur.getInt(0);
|
||||||
|
@ -474,6 +475,23 @@ public class ContactsController {
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
contactsMap.clear();
|
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;
|
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) {
|
if (existing == null || nameChanged) {
|
||||||
for (int a = 0; a < value.phones.size(); a++) {
|
for (int a = 0; a < value.phones.size(); a++) {
|
||||||
String sphone = value.shortPhones.get(a);
|
String sphone = value.shortPhones.get(a);
|
||||||
|
@ -607,9 +625,13 @@ public class ContactsController {
|
||||||
int index = existing.shortPhones.indexOf(sphone);
|
int index = existing.shortPhones.indexOf(sphone);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
if (request) {
|
if (request) {
|
||||||
if (contactsByPhone.containsKey(sphone)) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
||||||
imp.client_id = value.id;
|
imp.client_id = value.id;
|
||||||
|
@ -702,9 +724,13 @@ public class ContactsController {
|
||||||
int id = pair.getKey();
|
int id = pair.getKey();
|
||||||
for (int a = 0; a < value.phones.size(); a++) {
|
for (int a = 0; a < value.phones.size(); a++) {
|
||||||
String phone = value.shortPhones.get(a);
|
String phone = value.shortPhones.get(a);
|
||||||
if (contactsByPhone.containsKey(phone)) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
|
||||||
imp.client_id = id;
|
imp.client_id = id;
|
||||||
imp.first_name = value.first_name;
|
imp.first_name = value.first_name;
|
||||||
|
@ -721,9 +747,9 @@ public class ContactsController {
|
||||||
if (!toImport.isEmpty()) {
|
if (!toImport.isEmpty()) {
|
||||||
if (BuildVars.DEBUG_VERSION) {
|
if (BuildVars.DEBUG_VERSION) {
|
||||||
FileLog.e("tmessages", "start import contacts");
|
FileLog.e("tmessages", "start import contacts");
|
||||||
// for (TLRPC.TL_inputPhoneContact contact : toImport) {
|
for (TLRPC.TL_inputPhoneContact contact : toImport) {
|
||||||
// FileLog.e("tmessages", "add contact " + contact.first_name + " " + contact.last_name + " " + contact.phone);
|
FileLog.e("tmessages", "add contact " + contact.first_name + " " + contact.last_name + " " + contact.phone);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
final int count = (int)Math.ceil(toImport.size() / 500.0f);
|
final int count = (int)Math.ceil(toImport.size() / 500.0f);
|
||||||
for (int a = 0; a < count; a++) {
|
for (int a = 0; a < count; a++) {
|
||||||
|
@ -743,9 +769,9 @@ public class ContactsController {
|
||||||
}
|
}
|
||||||
TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
|
TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
|
||||||
if (BuildVars.DEBUG_VERSION) {
|
if (BuildVars.DEBUG_VERSION) {
|
||||||
// for (TLRPC.User user : res.users) {
|
for (TLRPC.User user : res.users) {
|
||||||
// FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
||||||
ArrayList<TLRPC.TL_contact> cArr = new ArrayList<>();
|
ArrayList<TLRPC.TL_contact> cArr = new ArrayList<>();
|
||||||
|
@ -903,9 +929,9 @@ public class ContactsController {
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
usersDict.put(user.id, user);
|
usersDict.put(user.id, user);
|
||||||
|
|
||||||
// if (BuildVars.DEBUG_VERSION) {
|
if (BuildVars.DEBUG_VERSION) {
|
||||||
// FileLog.e("tmessages", "loaded user contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
FileLog.e("tmessages", "loaded user contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1524,9 +1550,9 @@ public class ContactsController {
|
||||||
contactsParams.add(c);
|
contactsParams.add(c);
|
||||||
req.contacts = contactsParams;
|
req.contacts = contactsParams;
|
||||||
req.replace = false;
|
req.replace = false;
|
||||||
// if (BuildVars.DEBUG_VERSION) {
|
if (BuildVars.DEBUG_VERSION) {
|
||||||
// FileLog.e("tmessages", "add contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
FileLog.e("tmessages", "add contact " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||||
// }
|
}
|
||||||
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
|
ConnectionsManager.getInstance().performRpc(req, new RPCRequest.RPCRequestDelegate() {
|
||||||
@Override
|
@Override
|
||||||
public void run(TLObject response, TLRPC.TL_error error) {
|
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;
|
final TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
|
||||||
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
MessagesStorage.getInstance().putUsersAndChats(res.users, null, true, true);
|
||||||
|
|
||||||
// if (BuildVars.DEBUG_VERSION) {
|
if (BuildVars.DEBUG_VERSION) {
|
||||||
// for (TLRPC.User user : res.users) {
|
for (TLRPC.User user : res.users) {
|
||||||
// FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
FileLog.e("tmessages", "received user " + user.first_name + " " + user.last_name + " " + user.phone);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
for (final TLRPC.User u : res.users) {
|
for (final TLRPC.User u : res.users) {
|
||||||
Utilities.phoneBookQueue.postRunnable(new Runnable() {
|
Utilities.phoneBookQueue.postRunnable(new Runnable() {
|
||||||
|
|
|
@ -64,7 +64,8 @@ public class Emoji {
|
||||||
new long[]
|
new long[]
|
||||||
{},
|
{},
|
||||||
new long[]//189
|
new long[]//189
|
||||||
{0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL,
|
{
|
||||||
|
0x00000000D83DDE04L, 0x00000000D83DDE03L, 0x00000000D83DDE00L, 0x00000000D83DDE0AL, 0x000000000000263AL, 0x00000000D83DDE09L, 0x00000000D83DDE0DL,
|
||||||
0x00000000D83DDE18L, 0x00000000D83DDE1AL, 0x00000000D83DDE17L, 0x00000000D83DDE19L, 0x00000000D83DDE1CL, 0x00000000D83DDE1DL, 0x00000000D83DDE1BL,
|
0x00000000D83DDE18L, 0x00000000D83DDE1AL, 0x00000000D83DDE17L, 0x00000000D83DDE19L, 0x00000000D83DDE1CL, 0x00000000D83DDE1DL, 0x00000000D83DDE1BL,
|
||||||
0x00000000D83DDE33L, 0x00000000D83DDE01L, 0x00000000D83DDE14L, 0x00000000D83DDE0CL, 0x00000000D83DDE12L, 0x00000000D83DDE1EL, 0x00000000D83DDE23L,
|
0x00000000D83DDE33L, 0x00000000D83DDE01L, 0x00000000D83DDE14L, 0x00000000D83DDE0CL, 0x00000000D83DDE12L, 0x00000000D83DDE1EL, 0x00000000D83DDE23L,
|
||||||
0x00000000D83DDE22L, 0x00000000D83DDE02L, 0x00000000D83DDE2DL, 0x00000000D83DDE2AL, 0x00000000D83DDE25L, 0x00000000D83DDE30L, 0x00000000D83DDE05L,
|
0x00000000D83DDE22L, 0x00000000D83DDE02L, 0x00000000D83DDE2DL, 0x00000000D83DDE2AL, 0x00000000D83DDE25L, 0x00000000D83DDE30L, 0x00000000D83DDE05L,
|
||||||
|
@ -92,7 +93,8 @@ public class Emoji {
|
||||||
0x00000000D83DDC93L, 0x00000000D83DDC95L, 0x00000000D83DDC96L, 0x00000000D83DDC9EL, 0x00000000D83DDC98L, 0x00000000D83DDC8CL, 0x00000000D83DDC8BL,
|
0x00000000D83DDC93L, 0x00000000D83DDC95L, 0x00000000D83DDC96L, 0x00000000D83DDC9EL, 0x00000000D83DDC98L, 0x00000000D83DDC8CL, 0x00000000D83DDC8BL,
|
||||||
0x00000000D83DDC8DL, 0x00000000D83DDC8EL, 0x00000000D83DDC64L, 0x00000000D83DDC65L, 0x00000000D83DDCACL, 0x00000000D83DDC63L, 0x00000000D83DDCADL},
|
0x00000000D83DDC8DL, 0x00000000D83DDC8EL, 0x00000000D83DDC64L, 0x00000000D83DDC65L, 0x00000000D83DDCACL, 0x00000000D83DDC63L, 0x00000000D83DDCADL},
|
||||||
new long[]//116
|
new long[]//116
|
||||||
{0x00000000D83DDC36L, 0x00000000D83DDC3AL, 0x00000000D83DDC31L, 0x00000000D83DDC2DL, 0x00000000D83DDC39L, 0x00000000D83DDC30L, 0x00000000D83DDC38L, 0x00000000D83DDC2FL,
|
{
|
||||||
|
0x00000000D83DDC36L, 0x00000000D83DDC3AL, 0x00000000D83DDC31L, 0x00000000D83DDC2DL, 0x00000000D83DDC39L, 0x00000000D83DDC30L, 0x00000000D83DDC38L, 0x00000000D83DDC2FL,
|
||||||
0x00000000D83DDC28L, 0x00000000D83DDC3BL, 0x00000000D83DDC37L, 0x00000000D83DDC3DL, 0x00000000D83DDC2EL, 0x00000000D83DDC17L, 0x00000000D83DDC35L,
|
0x00000000D83DDC28L, 0x00000000D83DDC3BL, 0x00000000D83DDC37L, 0x00000000D83DDC3DL, 0x00000000D83DDC2EL, 0x00000000D83DDC17L, 0x00000000D83DDC35L,
|
||||||
0x00000000D83DDC12L, 0x00000000D83DDC34L, 0x00000000D83DDC11L, 0x00000000D83DDC18L, 0x00000000D83DDC3CL, 0x00000000D83DDC27L, 0x00000000D83DDC26L,
|
0x00000000D83DDC12L, 0x00000000D83DDC34L, 0x00000000D83DDC11L, 0x00000000D83DDC18L, 0x00000000D83DDC3CL, 0x00000000D83DDC27L, 0x00000000D83DDC26L,
|
||||||
0x00000000D83DDC24L, 0x00000000D83DDC25L, 0x00000000D83DDC23L, 0x00000000D83DDC14L, 0x00000000D83DDC0DL, 0x00000000D83DDC22L, 0x00000000D83DDC1BL,
|
0x00000000D83DDC24L, 0x00000000D83DDC25L, 0x00000000D83DDC23L, 0x00000000D83DDC14L, 0x00000000D83DDC0DL, 0x00000000D83DDC22L, 0x00000000D83DDC1BL,
|
||||||
|
@ -110,7 +112,8 @@ public class Emoji {
|
||||||
0x00000000000026C5L, 0x0000000000002601L, 0x00000000000026A1L, 0x0000000000002614L, 0x0000000000002744L, 0x00000000000026C4L, 0x00000000D83CDF00L,
|
0x00000000000026C5L, 0x0000000000002601L, 0x00000000000026A1L, 0x0000000000002614L, 0x0000000000002744L, 0x00000000000026C4L, 0x00000000D83CDF00L,
|
||||||
0x00000000D83CDF01L, 0x00000000D83CDF08L, 0x00000000D83CDF0AL},
|
0x00000000D83CDF01L, 0x00000000D83CDF08L, 0x00000000D83CDF0AL},
|
||||||
new long[]//230
|
new long[]//230
|
||||||
{0x00000000D83CDF8DL, 0x00000000D83DDC9DL, 0x00000000D83CDF8EL, 0x00000000D83CDF92L, 0x00000000D83CDF93L, 0x00000000D83CDF8FL, 0x00000000D83CDF86L, 0x00000000D83CDF87L,
|
{
|
||||||
|
0x00000000D83CDF8DL, 0x00000000D83DDC9DL, 0x00000000D83CDF8EL, 0x00000000D83CDF92L, 0x00000000D83CDF93L, 0x00000000D83CDF8FL, 0x00000000D83CDF86L, 0x00000000D83CDF87L,
|
||||||
0x00000000D83CDF90L, 0x00000000D83CDF91L, 0x00000000D83CDF83L, 0x00000000D83DDC7BL, 0x00000000D83CDF85L, 0x00000000D83CDF84L, 0x00000000D83CDF81L,
|
0x00000000D83CDF90L, 0x00000000D83CDF91L, 0x00000000D83CDF83L, 0x00000000D83DDC7BL, 0x00000000D83CDF85L, 0x00000000D83CDF84L, 0x00000000D83CDF81L,
|
||||||
0x00000000D83CDF8BL, 0x00000000D83CDF89L, 0x00000000D83CDF8AL, 0x00000000D83CDF88L, 0x00000000D83CDF8CL, 0x00000000D83DDD2EL, 0x00000000D83CDFA5L,
|
0x00000000D83CDF8BL, 0x00000000D83CDF89L, 0x00000000D83CDF8AL, 0x00000000D83CDF88L, 0x00000000D83CDF8CL, 0x00000000D83DDD2EL, 0x00000000D83CDFA5L,
|
||||||
0x00000000D83DDCF7L, 0x00000000D83DDCF9L, 0x00000000D83DDCFCL, 0x00000000D83DDCBFL, 0x00000000D83DDCC0L, 0x00000000D83DDCBDL, 0x00000000D83DDCBEL,
|
0x00000000D83DDCF7L, 0x00000000D83DDCF9L, 0x00000000D83DDCFCL, 0x00000000D83DDCBFL, 0x00000000D83DDCC0L, 0x00000000D83DDCBDL, 0x00000000D83DDCBEL,
|
||||||
|
@ -144,7 +147,8 @@ public class Emoji {
|
||||||
0x00000000D83CDF49L, 0x00000000D83CDF53L, 0x00000000D83CDF51L, 0x00000000D83CDF48L, 0x00000000D83CDF4CL, 0x00000000D83CDF50L, 0x00000000D83CDF4DL,
|
0x00000000D83CDF49L, 0x00000000D83CDF53L, 0x00000000D83CDF51L, 0x00000000D83CDF48L, 0x00000000D83CDF4CL, 0x00000000D83CDF50L, 0x00000000D83CDF4DL,
|
||||||
0x00000000D83CDF60L, 0x00000000D83CDF46L, 0x00000000D83CDF45L, 0x00000000D83CDF3DL},
|
0x00000000D83CDF60L, 0x00000000D83CDF46L, 0x00000000D83CDF45L, 0x00000000D83CDF3DL},
|
||||||
new long[]//101
|
new long[]//101
|
||||||
{0x00000000D83CDFE0L, 0x00000000D83CDFE1L, 0x00000000D83CDFEBL, 0x00000000D83CDFE2L, 0x00000000D83CDFE3L, 0x00000000D83CDFE5L, 0x00000000D83CDFE6L, 0x00000000D83CDFEAL,
|
{
|
||||||
|
0x00000000D83CDFE0L, 0x00000000D83CDFE1L, 0x00000000D83CDFEBL, 0x00000000D83CDFE2L, 0x00000000D83CDFE3L, 0x00000000D83CDFE5L, 0x00000000D83CDFE6L, 0x00000000D83CDFEAL,
|
||||||
0x00000000D83CDFE9L, 0x00000000D83CDFE8L, 0x00000000D83DDC92L, 0x00000000000026EAL, 0x00000000D83CDFECL, 0x00000000D83CDFE4L, 0x00000000D83CDF07L,
|
0x00000000D83CDFE9L, 0x00000000D83CDFE8L, 0x00000000D83DDC92L, 0x00000000000026EAL, 0x00000000D83CDFECL, 0x00000000D83CDFE4L, 0x00000000D83CDF07L,
|
||||||
0x00000000D83CDF06L, 0x00000000D83CDFEFL, 0x00000000D83CDFF0L, 0x00000000000026FAL, 0x00000000D83CDFEDL, 0x00000000D83DDDFCL, 0x00000000D83DDDFEL,
|
0x00000000D83CDF06L, 0x00000000D83CDFEFL, 0x00000000D83CDFF0L, 0x00000000000026FAL, 0x00000000D83CDFEDL, 0x00000000D83DDDFCL, 0x00000000D83DDDFEL,
|
||||||
0x00000000D83DDDFBL, 0x00000000D83CDF04L, 0x00000000D83CDF05L, 0x00000000D83CDF03L, 0x00000000D83DDDFDL, 0x00000000D83CDF09L, 0x00000000D83CDFA0L,
|
0x00000000D83DDDFBL, 0x00000000D83CDF04L, 0x00000000D83CDF05L, 0x00000000D83CDF03L, 0x00000000D83DDDFDL, 0x00000000D83CDF09L, 0x00000000D83CDFA0L,
|
||||||
|
@ -160,7 +164,8 @@ public class Emoji {
|
||||||
0xD83CDDF0D83CDDF7L, 0xD83CDDE9D83CDDEAL, 0xD83CDDE8D83CDDF3L, 0xD83CDDFAD83CDDF8L, 0xD83CDDEBD83CDDF7L, 0xD83CDDEAD83CDDF8L, 0xD83CDDEED83CDDF9L,
|
0xD83CDDF0D83CDDF7L, 0xD83CDDE9D83CDDEAL, 0xD83CDDE8D83CDDF3L, 0xD83CDDFAD83CDDF8L, 0xD83CDDEBD83CDDF7L, 0xD83CDDEAD83CDDF8L, 0xD83CDDEED83CDDF9L,
|
||||||
0xD83CDDF7D83CDDFAL, 0xD83CDDECD83CDDE7L},
|
0xD83CDDF7D83CDDFAL, 0xD83CDDECD83CDDE7L},
|
||||||
new long[]//209
|
new long[]//209
|
||||||
{0x00000000003120E3L, 0x00000000003220E3L, 0x00000000003320E3L, 0x00000000003420E3L, 0x00000000003520E3L, 0x00000000003620E3L, 0x00000000003720E3L,
|
{
|
||||||
|
0x00000000003120E3L, 0x00000000003220E3L, 0x00000000003320E3L, 0x00000000003420E3L, 0x00000000003520E3L, 0x00000000003620E3L, 0x00000000003720E3L,
|
||||||
0x00000000003820E3L, 0x00000000003920E3L, 0x00000000003020E3L, 0x00000000D83DDD1FL, 0x00000000D83DDD22L, 0x00000000002320E3L, 0x00000000D83DDD23L,
|
0x00000000003820E3L, 0x00000000003920E3L, 0x00000000003020E3L, 0x00000000D83DDD1FL, 0x00000000D83DDD22L, 0x00000000002320E3L, 0x00000000D83DDD23L,
|
||||||
0x0000000000002B06L, 0x0000000000002B07L, 0x0000000000002B05L, 0x00000000000027A1L, 0x00000000D83DDD20L, 0x00000000D83DDD21L, 0x00000000D83DDD24L,
|
0x0000000000002B06L, 0x0000000000002B07L, 0x0000000000002B05L, 0x00000000000027A1L, 0x00000000D83DDD20L, 0x00000000D83DDD21L, 0x00000000D83DDD24L,
|
||||||
0x0000000000002197L, 0x0000000000002196L, 0x0000000000002198L, 0x0000000000002199L, 0x0000000000002194L, 0x0000000000002195L, 0x00000000D83DDD04L,
|
0x0000000000002197L, 0x0000000000002196L, 0x0000000000002198L, 0x0000000000002199L, 0x0000000000002194L, 0x0000000000002195L, 0x00000000D83DDD04L,
|
||||||
|
@ -213,7 +218,7 @@ public class Emoji {
|
||||||
for (int j = 1; j < data.length; j++) {
|
for (int j = 1; j < data.length; j++) {
|
||||||
for (int i = 0; i < data[j].length; i++) {
|
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);
|
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)));
|
rects.put(data[j][i], new DrawableInfo(rect, (byte) (j - 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
placeholderPaint = new Paint();
|
placeholderPaint = new Paint();
|
||||||
|
@ -290,7 +295,7 @@ public class Emoji {
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.emojiDidLoaded);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.emojiDidLoaded);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch(Throwable x) {
|
} catch (Throwable x) {
|
||||||
FileLog.e("tmessages", "Error loading emoji", x);
|
FileLog.e("tmessages", "Error loading emoji", x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,7 +315,7 @@ public class Emoji {
|
||||||
|
|
||||||
public static void invalidateAll(View view) {
|
public static void invalidateAll(View view) {
|
||||||
if (view instanceof ViewGroup) {
|
if (view instanceof ViewGroup) {
|
||||||
ViewGroup g = (ViewGroup)view;
|
ViewGroup g = (ViewGroup) view;
|
||||||
for (int i = 0; i < g.getChildCount(); i++) {
|
for (int i = 0; i < g.getChildCount(); i++) {
|
||||||
invalidateAll(g.getChildAt(i));
|
invalidateAll(g.getChildAt(i));
|
||||||
}
|
}
|
||||||
|
@ -425,18 +430,22 @@ public class Emoji {
|
||||||
return value == 0xd83cdffb || value == 0xd83cdffc || value == 0xd83cdffd || value == 0xd83cdffe || value == 0xd83cdfff;
|
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) {
|
if (cs == null || cs.length() == 0) {
|
||||||
return cs;
|
return cs;
|
||||||
}
|
}
|
||||||
|
//SpannableStringLight.isFieldsAvailable();
|
||||||
|
//SpannableStringLight s = new SpannableStringLight(cs.toString());
|
||||||
Spannable s;
|
Spannable s;
|
||||||
if (cs instanceof Spannable) {
|
if (!createNew && cs instanceof Spannable) {
|
||||||
s = (Spannable)cs;
|
s = (Spannable) cs;
|
||||||
} else {
|
} else {
|
||||||
s = Spannable.Factory.getInstance().newSpannable(cs);
|
s = Spannable.Factory.getInstance().newSpannable(cs.toString());
|
||||||
}
|
}
|
||||||
long buf = 0;
|
long buf = 0;
|
||||||
int emojiCount = 0;
|
int emojiCount = 0;
|
||||||
|
//s.setSpansCount(emojiCount);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (int i = 0; i < cs.length(); i++) {
|
for (int i = 0; i < cs.length(); i++) {
|
||||||
char c = cs.charAt(i);
|
char c = cs.charAt(i);
|
||||||
|
@ -450,12 +459,12 @@ public class Emoji {
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
||||||
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
||||||
emojiCount++;
|
|
||||||
if (c >= 0xDDE6 && c <= 0xDDFA) {
|
if (c >= 0xDDE6 && c <= 0xDDFA) {
|
||||||
s.setSpan(span, i - 3, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
s.setSpan(span, i - 3, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
} else {
|
} else {
|
||||||
s.setSpan(span, i - 1, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
s.setSpan(span, i - 1, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
}
|
}
|
||||||
|
emojiCount++;
|
||||||
if (nextIsSkinTone) {
|
if (nextIsSkinTone) {
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
|
@ -472,8 +481,8 @@ public class Emoji {
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
||||||
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
||||||
emojiCount++;
|
|
||||||
s.setSpan(span, i - 1, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
s.setSpan(span, i - 1, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
emojiCount++;
|
||||||
if (nextIsSkinTone) {
|
if (nextIsSkinTone) {
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
|
@ -486,8 +495,8 @@ public class Emoji {
|
||||||
if (d != null) {
|
if (d != null) {
|
||||||
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
boolean nextIsSkinTone = isNextCharIsColor(cs, i);
|
||||||
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
EmojiSpan span = new EmojiSpan(d, DynamicDrawableSpan.ALIGN_BOTTOM, size, fontMetrics);
|
||||||
emojiCount++;
|
|
||||||
s.setSpan(span, i, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
s.setSpan(span, i, i + (nextIsSkinTone ? 3 : 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
emojiCount++;
|
||||||
if (nextIsSkinTone) {
|
if (nextIsSkinTone) {
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1285,6 +1285,9 @@ public class ImageLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Float getFileProgress(String location) {
|
public Float getFileProgress(String location) {
|
||||||
|
if (location == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return fileProgresses.get(location);
|
return fileProgresses.get(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,8 @@ import android.os.Vibrator;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.view.View;
|
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.InputSurface;
|
||||||
import org.telegram.android.video.MP4Builder;
|
import org.telegram.android.video.MP4Builder;
|
||||||
import org.telegram.android.video.Mp4Movie;
|
import org.telegram.android.video.Mp4Movie;
|
||||||
|
@ -78,22 +80,34 @@ import java.util.concurrent.Semaphore;
|
||||||
public class MediaController implements NotificationCenter.NotificationCenterDelegate, SensorEventListener {
|
public class MediaController implements NotificationCenter.NotificationCenterDelegate, SensorEventListener {
|
||||||
|
|
||||||
private native int startRecord(String path);
|
private native int startRecord(String path);
|
||||||
|
|
||||||
private native int writeFrame(ByteBuffer frame, int len);
|
private native int writeFrame(ByteBuffer frame, int len);
|
||||||
|
|
||||||
private native void stopRecord();
|
private native void stopRecord();
|
||||||
|
|
||||||
private native int openOpusFile(String path);
|
private native int openOpusFile(String path);
|
||||||
|
|
||||||
private native int seekOpusFile(float position);
|
private native int seekOpusFile(float position);
|
||||||
|
|
||||||
private native int isOpusFile(String path);
|
private native int isOpusFile(String path);
|
||||||
|
|
||||||
private native void closeOpusFile();
|
private native void closeOpusFile();
|
||||||
|
|
||||||
private native void readOpusFile(ByteBuffer buffer, int capacity, int[] args);
|
private native void readOpusFile(ByteBuffer buffer, int capacity, int[] args);
|
||||||
|
|
||||||
private native long getTotalPcmDuration();
|
private native long getTotalPcmDuration();
|
||||||
|
|
||||||
public static int[] readArgs = new int[3];
|
public static int[] readArgs = new int[3];
|
||||||
|
|
||||||
public interface FileDownloadProgressListener {
|
public interface FileDownloadProgressListener {
|
||||||
void onFailedDownload(String fileName);
|
void onFailedDownload(String fileName);
|
||||||
|
|
||||||
void onSuccessDownload(String fileName);
|
void onSuccessDownload(String fileName);
|
||||||
|
|
||||||
void onProgressDownload(String fileName, float progress);
|
void onProgressDownload(String fileName, float progress);
|
||||||
|
|
||||||
void onProgressUpload(String fileName, float progress, boolean isEncrypted);
|
void onProgressUpload(String fileName, float progress, boolean isEncrypted);
|
||||||
|
|
||||||
int getObserverTag();
|
int getObserverTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +141,16 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
MediaStore.Video.Media.DATE_TAKEN
|
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 static class AlbumEntry {
|
||||||
public int bucketId;
|
public int bucketId;
|
||||||
public String bucketName;
|
public String bucketName;
|
||||||
|
@ -221,7 +245,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
private HashMap<String, DownloadObject> downloadQueueKeys = new HashMap<>();
|
private HashMap<String, DownloadObject> downloadQueueKeys = new HashMap<>();
|
||||||
|
|
||||||
private boolean saveToGallery = true;
|
private boolean saveToGallery = true;
|
||||||
|
private boolean shuffleMusic;
|
||||||
|
private int repeatMode;
|
||||||
|
|
||||||
|
private Runnable refreshGalleryRunnable;
|
||||||
public static AlbumEntry allPhotosAlbumEntry;
|
public static AlbumEntry allPhotosAlbumEntry;
|
||||||
|
|
||||||
private HashMap<String, ArrayList<WeakReference<FileDownloadProgressListener>>> loadingFileObservers = new HashMap<>();
|
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 final Object progressTimerSync = new Object();
|
||||||
private boolean useFrontSpeaker;
|
private boolean useFrontSpeaker;
|
||||||
private int buffersWrited;
|
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 AudioRecord audioRecorder = null;
|
||||||
private TLRPC.TL_audio recordingAudio = null;
|
private TLRPC.TL_audio recordingAudio = null;
|
||||||
|
@ -271,6 +303,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
private int recordBufferSize;
|
private int recordBufferSize;
|
||||||
private boolean sendAfterDone;
|
private boolean sendAfterDone;
|
||||||
|
|
||||||
|
private Runnable recordStartRunnable;
|
||||||
private DispatchQueue recordQueue;
|
private DispatchQueue recordQueue;
|
||||||
private DispatchQueue fileEncodingQueue;
|
private DispatchQueue fileEncodingQueue;
|
||||||
private Runnable recordRunnable = new Runnable() {
|
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() {
|
public GalleryObserverInternal() {
|
||||||
super(null);
|
super(null);
|
||||||
}
|
}
|
||||||
|
@ -367,9 +400,13 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
super.onChange(selfChange);
|
super.onChange(selfChange);
|
||||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
if (refreshGalleryRunnable != null) {
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(refreshGalleryRunnable);
|
||||||
|
}
|
||||||
|
AndroidUtilities.runOnUIThread(refreshGalleryRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
refreshGalleryRunnable = null;
|
||||||
loadGalleryPhotosAlbums(0);
|
loadGalleryPhotosAlbums(0);
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
@ -384,14 +421,18 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange) {
|
public void onChange(boolean selfChange) {
|
||||||
super.onChange(selfChange);
|
super.onChange(selfChange);
|
||||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
if (refreshGalleryRunnable != null) {
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(refreshGalleryRunnable);
|
||||||
|
}
|
||||||
|
AndroidUtilities.runOnUIThread(refreshGalleryRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
refreshGalleryRunnable = null;
|
||||||
loadGalleryPhotosAlbums(0);
|
loadGalleryPhotosAlbums(0);
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
private ExternalObserver externalObserver = null;
|
private ExternalObserver externalObserver = null;
|
||||||
private InternalObserver internalObserver = null;
|
private InternalObserver internalObserver = null;
|
||||||
|
@ -402,6 +443,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
private ArrayList<Long> lastSecretChatVisibleMessages = null;
|
private ArrayList<Long> lastSecretChatVisibleMessages = null;
|
||||||
private int startObserverToken = 0;
|
private int startObserverToken = 0;
|
||||||
private StopMediaObserverRunnable stopMediaObserverRunnable = null;
|
private StopMediaObserverRunnable stopMediaObserverRunnable = null;
|
||||||
|
|
||||||
private final class StopMediaObserverRunnable implements Runnable {
|
private final class StopMediaObserverRunnable implements Runnable {
|
||||||
public int currentObserverToken = 0;
|
public int currentObserverToken = 0;
|
||||||
|
|
||||||
|
@ -427,9 +469,11 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] mediaProjections = null;
|
private String[] mediaProjections = null;
|
||||||
|
|
||||||
private static volatile MediaController Instance = null;
|
private static volatile MediaController Instance = null;
|
||||||
|
|
||||||
public static MediaController getInstance() {
|
public static MediaController getInstance() {
|
||||||
MediaController localInstance = Instance;
|
MediaController localInstance = Instance;
|
||||||
if (localInstance == null) {
|
if (localInstance == null) {
|
||||||
|
@ -484,6 +528,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
wifiDownloadMask = preferences.getInt("wifiDownloadMask", AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO);
|
wifiDownloadMask = preferences.getInt("wifiDownloadMask", AUTODOWNLOAD_MASK_PHOTO | AUTODOWNLOAD_MASK_AUDIO);
|
||||||
roamingDownloadMask = preferences.getInt("roamingDownloadMask", 0);
|
roamingDownloadMask = preferences.getInt("roamingDownloadMask", 0);
|
||||||
saveToGallery = preferences.getBoolean("save_gallery", false);
|
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.FileDidFailedLoad);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileDidLoaded);
|
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.FileUploadProgressChanged);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesDeleted);
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.messagesDeleted);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.removeAllMessagesFromDialog);
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.removeAllMessagesFromDialog);
|
||||||
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.musicDidLoaded);
|
||||||
|
|
||||||
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
|
BroadcastReceiver networkStateReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -506,7 +553,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 16) {
|
if (Build.VERSION.SDK_INT >= 16) {
|
||||||
mediaProjections = new String[] {
|
mediaProjections = new String[]{
|
||||||
MediaStore.Images.ImageColumns.DATA,
|
MediaStore.Images.ImageColumns.DATA,
|
||||||
MediaStore.Images.ImageColumns.DISPLAY_NAME,
|
MediaStore.Images.ImageColumns.DISPLAY_NAME,
|
||||||
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
|
MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
|
||||||
|
@ -516,7 +563,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
MediaStore.Images.ImageColumns.HEIGHT
|
MediaStore.Images.ImageColumns.HEIGHT
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
mediaProjections = new String[] {
|
mediaProjections = new String[]{
|
||||||
MediaStore.Images.ImageColumns.DATA,
|
MediaStore.Images.ImageColumns.DATA,
|
||||||
MediaStore.Images.ImageColumns.DISPLAY_NAME,
|
MediaStore.Images.ImageColumns.DISPLAY_NAME,
|
||||||
MediaStore.Images.ImageColumns.BUCKET_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());
|
ApplicationLoader.applicationContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, new GalleryObserverExternal());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e("tmessages", 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());
|
ApplicationLoader.applicationContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, false, new GalleryObserverInternal());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startProgressTimer() {
|
private void startProgressTimer() {
|
||||||
|
@ -606,12 +653,13 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
clenupPlayer(false);
|
clenupPlayer(false, true);
|
||||||
if (currentGifDrawable != null) {
|
if (currentGifDrawable != null) {
|
||||||
currentGifDrawable.recycle();
|
currentGifDrawable.recycle();
|
||||||
currentGifDrawable = null;
|
currentGifDrawable = null;
|
||||||
}
|
}
|
||||||
currentMediaCell = null;
|
currentMediaCell = null;
|
||||||
|
audioInfo = null;
|
||||||
currentGifMessageObject = null;
|
currentGifMessageObject = null;
|
||||||
photoDownloadQueue.clear();
|
photoDownloadQueue.clear();
|
||||||
audioDownloadQueue.clear();
|
audioDownloadQueue.clear();
|
||||||
|
@ -619,6 +667,8 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
videoDownloadQueue.clear();
|
videoDownloadQueue.clear();
|
||||||
downloadQueueKeys.clear();
|
downloadQueueKeys.clear();
|
||||||
videoConvertQueue.clear();
|
videoConvertQueue.clear();
|
||||||
|
playlist.clear();
|
||||||
|
shuffledPlaylist.clear();
|
||||||
typingTimes.clear();
|
typingTimes.clear();
|
||||||
cancelVideoConvert(null);
|
cancelVideoConvert(null);
|
||||||
}
|
}
|
||||||
|
@ -652,7 +702,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (DownloadObject downloadObject : photoDownloadQueue) {
|
for (DownloadObject downloadObject : photoDownloadQueue) {
|
||||||
FileLoader.getInstance().cancelLoadFile((TLRPC.PhotoSize)downloadObject.object);
|
FileLoader.getInstance().cancelLoadFile((TLRPC.PhotoSize) downloadObject.object);
|
||||||
}
|
}
|
||||||
photoDownloadQueue.clear();
|
photoDownloadQueue.clear();
|
||||||
}
|
}
|
||||||
|
@ -662,7 +712,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (DownloadObject downloadObject : audioDownloadQueue) {
|
for (DownloadObject downloadObject : audioDownloadQueue) {
|
||||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Audio)downloadObject.object);
|
FileLoader.getInstance().cancelLoadFile((TLRPC.Audio) downloadObject.object);
|
||||||
}
|
}
|
||||||
audioDownloadQueue.clear();
|
audioDownloadQueue.clear();
|
||||||
}
|
}
|
||||||
|
@ -672,7 +722,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (DownloadObject downloadObject : documentDownloadQueue) {
|
for (DownloadObject downloadObject : documentDownloadQueue) {
|
||||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Document)downloadObject.object);
|
FileLoader.getInstance().cancelLoadFile((TLRPC.Document) downloadObject.object);
|
||||||
}
|
}
|
||||||
documentDownloadQueue.clear();
|
documentDownloadQueue.clear();
|
||||||
}
|
}
|
||||||
|
@ -682,7 +732,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (DownloadObject downloadObject : videoDownloadQueue) {
|
for (DownloadObject downloadObject : videoDownloadQueue) {
|
||||||
FileLoader.getInstance().cancelLoadFile((TLRPC.Video)downloadObject.object);
|
FileLoader.getInstance().cancelLoadFile((TLRPC.Video) downloadObject.object);
|
||||||
}
|
}
|
||||||
videoDownloadQueue.clear();
|
videoDownloadQueue.clear();
|
||||||
}
|
}
|
||||||
|
@ -713,7 +763,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
private int getCurrentDownloadMask() {
|
private int getCurrentDownloadMask() {
|
||||||
if (ConnectionsManager.isConnectedToWiFi()) {
|
if (ConnectionsManager.isConnectedToWiFi()) {
|
||||||
return wifiDownloadMask;
|
return wifiDownloadMask;
|
||||||
} else if(ConnectionsManager.isRoaming()) {
|
} else if (ConnectionsManager.isRoaming()) {
|
||||||
return roamingDownloadMask;
|
return roamingDownloadMask;
|
||||||
} else {
|
} else {
|
||||||
return mobileDataDownloadMask;
|
return mobileDataDownloadMask;
|
||||||
|
@ -742,13 +792,13 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
|
|
||||||
boolean added = true;
|
boolean added = true;
|
||||||
if (downloadObject.object instanceof TLRPC.Audio) {
|
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) {
|
} 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) {
|
} 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) {
|
} 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 {
|
} else {
|
||||||
added = false;
|
added = false;
|
||||||
}
|
}
|
||||||
|
@ -986,7 +1036,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
public void didReceivedNotification(int id, Object... args) {
|
public void didReceivedNotification(int id, Object... args) {
|
||||||
if (id == NotificationCenter.FileDidFailedLoad) {
|
if (id == NotificationCenter.FileDidFailedLoad) {
|
||||||
listenerInProgress = true;
|
listenerInProgress = true;
|
||||||
String fileName = (String)args[0];
|
String fileName = (String) args[0];
|
||||||
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||||
if (arrayList != null) {
|
if (arrayList != null) {
|
||||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||||
|
@ -999,10 +1049,16 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
listenerInProgress = false;
|
listenerInProgress = false;
|
||||||
processLaterArrays();
|
processLaterArrays();
|
||||||
checkDownloadFinished(fileName, (Integer)args[1]);
|
checkDownloadFinished(fileName, (Integer) args[1]);
|
||||||
} else if (id == NotificationCenter.FileDidLoaded) {
|
} else if (id == NotificationCenter.FileDidLoaded) {
|
||||||
listenerInProgress = true;
|
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);
|
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||||
if (arrayList != null) {
|
if (arrayList != null) {
|
||||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||||
|
@ -1018,10 +1074,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
checkDownloadFinished(fileName, 0);
|
checkDownloadFinished(fileName, 0);
|
||||||
} else if (id == NotificationCenter.FileLoadProgressChanged) {
|
} else if (id == NotificationCenter.FileLoadProgressChanged) {
|
||||||
listenerInProgress = true;
|
listenerInProgress = true;
|
||||||
String fileName = (String)args[0];
|
String fileName = (String) args[0];
|
||||||
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||||
if (arrayList != null) {
|
if (arrayList != null) {
|
||||||
Float progress = (Float)args[1];
|
Float progress = (Float) args[1];
|
||||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||||
if (reference.get() != null) {
|
if (reference.get() != null) {
|
||||||
reference.get().onProgressDownload(fileName, progress);
|
reference.get().onProgressDownload(fileName, progress);
|
||||||
|
@ -1032,11 +1088,11 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
processLaterArrays();
|
processLaterArrays();
|
||||||
} else if (id == NotificationCenter.FileUploadProgressChanged) {
|
} else if (id == NotificationCenter.FileUploadProgressChanged) {
|
||||||
listenerInProgress = true;
|
listenerInProgress = true;
|
||||||
String fileName = (String)args[0];
|
String fileName = (String) args[0];
|
||||||
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
ArrayList<WeakReference<FileDownloadProgressListener>> arrayList = loadingFileObservers.get(fileName);
|
||||||
if (arrayList != null) {
|
if (arrayList != null) {
|
||||||
Float progress = (Float)args[1];
|
Float progress = (Float) args[1];
|
||||||
Boolean enc = (Boolean)args[2];
|
Boolean enc = (Boolean) args[2];
|
||||||
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
for (WeakReference<FileDownloadProgressListener> reference : arrayList) {
|
||||||
if (reference.get() != null) {
|
if (reference.get() != null) {
|
||||||
reference.get().onProgressUpload(fileName, progress, enc);
|
reference.get().onProgressUpload(fileName, progress, enc);
|
||||||
|
@ -1070,15 +1126,27 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
} else if (id == NotificationCenter.messagesDeleted) {
|
} else if (id == NotificationCenter.messagesDeleted) {
|
||||||
if (playingMessageObject != null) {
|
if (playingMessageObject != null) {
|
||||||
ArrayList<Integer> markAsDeletedMessages = (ArrayList<Integer>)args[0];
|
ArrayList<Integer> markAsDeletedMessages = (ArrayList<Integer>) args[0];
|
||||||
if (markAsDeletedMessages.contains(playingMessageObject.getId())) {
|
if (markAsDeletedMessages.contains(playingMessageObject.getId())) {
|
||||||
clenupPlayer(false);
|
clenupPlayer(false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (id == NotificationCenter.removeAllMessagesFromDialog) {
|
} else if (id == NotificationCenter.removeAllMessagesFromDialog) {
|
||||||
long did = (Long)args[0];
|
long did = (Long) args[0];
|
||||||
if (playingMessageObject != null && playingMessageObject.getDialogId() == did) {
|
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);
|
audioTrackPlayer.setNotificationMarkerPosition(1);
|
||||||
}
|
}
|
||||||
if (finalBuffersWrited == 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);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioRouteChanged, useFrontSpeaker);
|
||||||
MessageObject currentMessageObject = playingMessageObject;
|
MessageObject currentMessageObject = playingMessageObject;
|
||||||
float progress = playingMessageObject.audioProgress;
|
float progress = playingMessageObject.audioProgress;
|
||||||
clenupPlayer(false);
|
clenupPlayer(false, true);
|
||||||
currentMessageObject.audioProgress = progress;
|
currentMessageObject.audioProgress = progress;
|
||||||
playAudio(currentMessageObject);
|
playAudio(currentMessageObject);
|
||||||
ignoreProximity = false;
|
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();
|
stopProximitySensor();
|
||||||
if (audioPlayer != null || audioTrackPlayer != null) {
|
if (playingMessageObject != null) {
|
||||||
if (audioPlayer != null) {
|
if (audioPlayer != null) {
|
||||||
try {
|
try {
|
||||||
audioPlayer.stop();
|
audioPlayer.stop();
|
||||||
|
@ -1301,12 +1369,20 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
lastProgress = 0;
|
lastProgress = 0;
|
||||||
buffersWrited = 0;
|
buffersWrited = 0;
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
|
if (downloadingCurrentMessage) {
|
||||||
|
FileLoader.getInstance().cancelLoadFile(playingMessageObject.messageOwner.media.document);
|
||||||
|
}
|
||||||
MessageObject lastFile = playingMessageObject;
|
MessageObject lastFile = playingMessageObject;
|
||||||
playingMessageObject.audioProgress = 0.0f;
|
playingMessageObject.audioProgress = 0.0f;
|
||||||
playingMessageObject.audioProgressSec = 0;
|
playingMessageObject.audioProgressSec = 0;
|
||||||
playingMessageObject = null;
|
playingMessageObject = null;
|
||||||
|
downloadingCurrentMessage = false;
|
||||||
if (notify) {
|
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;
|
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) {
|
public boolean playAudio(MessageObject messageObject) {
|
||||||
if (messageObject == null) {
|
if (messageObject == null) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1374,8 +1595,10 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioDidStarted, messageObject);
|
if (audioTrackPlayer != null) {
|
||||||
clenupPlayer(true);
|
MusicPlayerService.setIgnoreAudioFocus();
|
||||||
|
}
|
||||||
|
clenupPlayer(true, false);
|
||||||
File file = null;
|
File file = null;
|
||||||
if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() > 0) {
|
if (messageObject.messageOwner.attachPath != null && messageObject.messageOwner.attachPath.length() > 0) {
|
||||||
file = new File(messageObject.messageOwner.attachPath);
|
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);
|
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) {
|
if (isOpusFile(cacheFile.getAbsolutePath()) == 1) {
|
||||||
|
playlist.clear();
|
||||||
|
shuffledPlaylist.clear();
|
||||||
synchronized (playerObjectSync) {
|
synchronized (playerObjectSync) {
|
||||||
try {
|
try {
|
||||||
ignoreFirstProgress = 3;
|
ignoreFirstProgress = 3;
|
||||||
|
@ -1410,7 +1658,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
audioTrackPlayer.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener() {
|
audioTrackPlayer.setPlaybackPositionUpdateListener(new AudioTrack.OnPlaybackPositionUpdateListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onMarkerReached(AudioTrack audioTrack) {
|
public void onMarkerReached(AudioTrack audioTrack) {
|
||||||
clenupPlayer(true);
|
clenupPlayer(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1420,7 +1668,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
});
|
});
|
||||||
audioTrackPlayer.play();
|
audioTrackPlayer.play();
|
||||||
startProgressTimer();
|
startProgressTimer();
|
||||||
|
if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaAudio) {
|
||||||
startProximitySensor();
|
startProximitySensor();
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
if (audioTrackPlayer != null) {
|
if (audioTrackPlayer != null) {
|
||||||
|
@ -1428,6 +1678,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
audioTrackPlayer = null;
|
audioTrackPlayer = null;
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
playingMessageObject = null;
|
playingMessageObject = null;
|
||||||
|
downloadingCurrentMessage = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1440,13 +1691,28 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
audioPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
audioPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCompletion(MediaPlayer mediaPlayer) {
|
public void onCompletion(MediaPlayer mediaPlayer) {
|
||||||
clenupPlayer(true);
|
if (!playlist.isEmpty() && playlist.size() > 1) {
|
||||||
|
playNextMessage(true);
|
||||||
|
} else {
|
||||||
|
clenupPlayer(true, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
audioPlayer.prepare();
|
audioPlayer.prepare();
|
||||||
audioPlayer.start();
|
audioPlayer.start();
|
||||||
startProgressTimer();
|
startProgressTimer();
|
||||||
|
if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaAudio) {
|
||||||
|
audioInfo = null;
|
||||||
|
playlist.clear();
|
||||||
|
shuffledPlaylist.clear();
|
||||||
startProximitySensor();
|
startProximitySensor();
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
audioInfo = AudioInfo.getAudioInfo(cacheFile);
|
||||||
|
} catch (Exception e) {
|
||||||
|
FileLog.e("tmessages", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
if (audioPlayer != null) {
|
if (audioPlayer != null) {
|
||||||
|
@ -1454,6 +1720,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
audioPlayer = null;
|
audioPlayer = null;
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
playingMessageObject = null;
|
playingMessageObject = null;
|
||||||
|
downloadingCurrentMessage = false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1463,6 +1730,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
lastProgress = 0;
|
lastProgress = 0;
|
||||||
lastPlayPcm = 0;
|
lastPlayPcm = 0;
|
||||||
playingMessageObject = messageObject;
|
playingMessageObject = messageObject;
|
||||||
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioDidStarted, messageObject);
|
||||||
|
|
||||||
if (audioPlayer != null) {
|
if (audioPlayer != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -1484,7 +1752,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
if (playingMessageObject != null && playingMessageObject.audioProgress != 0) {
|
if (playingMessageObject != null && playingMessageObject.audioProgress != 0) {
|
||||||
lastPlayPcm = (long)(currentTotalPcmDuration * playingMessageObject.audioProgress);
|
lastPlayPcm = (long) (currentTotalPcmDuration * playingMessageObject.audioProgress);
|
||||||
seekOpusFile(playingMessageObject.audioProgress);
|
seekOpusFile(playingMessageObject.audioProgress);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,7 +1809,55 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
stopProgressTimer();
|
stopProgressTimer();
|
||||||
playingMessageObject = null;
|
playingMessageObject = null;
|
||||||
|
downloadingCurrentMessage = false;
|
||||||
isPaused = 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) {
|
public boolean pauseAudio(MessageObject messageObject) {
|
||||||
|
@ -1549,6 +1873,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
audioTrackPlayer.pause();
|
audioTrackPlayer.pause();
|
||||||
}
|
}
|
||||||
isPaused = true;
|
isPaused = true;
|
||||||
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
isPaused = false;
|
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()) {
|
if (audioTrackPlayer == null && audioPlayer == null || messageObject == null || playingMessageObject == null || playingMessageObject != null && playingMessageObject.getId() != messageObject.getId()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaAudio) {
|
||||||
startProximitySensor();
|
startProximitySensor();
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
startProgressTimer();
|
startProgressTimer();
|
||||||
if (audioPlayer != null) {
|
if (audioPlayer != null) {
|
||||||
|
@ -1571,6 +1898,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
checkPlayerQueue();
|
checkPlayerQueue();
|
||||||
}
|
}
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.audioPlayStateChanged, playingMessageObject.getId());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
return false;
|
return false;
|
||||||
|
@ -1579,15 +1907,23 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPlayingAudio(MessageObject messageObject) {
|
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() {
|
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) {
|
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 {
|
try {
|
||||||
Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE);
|
Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
@ -1596,13 +1932,14 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
recordQueue.postRunnable(new Runnable() {
|
recordQueue.postRunnable(recordStartRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (audioRecorder != null) {
|
if (audioRecorder != null) {
|
||||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
recordStartRunnable = null;
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1624,6 +1961,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
recordStartRunnable = null;
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1653,6 +1991,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
recordStartRunnable = null;
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStartError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1663,11 +2002,12 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
AndroidUtilities.runOnUIThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
recordStartRunnable = null;
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStarted);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.recordStarted);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}, paused ? 500 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopRecordingInternal(final boolean send) {
|
private void stopRecordingInternal(final boolean send) {
|
||||||
|
@ -1709,6 +2049,9 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopRecording(final boolean send) {
|
public void stopRecording(final boolean send) {
|
||||||
|
if (recordStartRunnable != null) {
|
||||||
|
recordQueue.cancelRunnable(recordStartRunnable);
|
||||||
|
}
|
||||||
recordQueue.postRunnable(new Runnable() {
|
recordQueue.postRunnable(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -1790,10 +2133,15 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
destFile = AndroidUtilities.generateVideoPath();
|
destFile = AndroidUtilities.generateVideoPath();
|
||||||
} else if (type == 2) {
|
} else if (type == 2) {
|
||||||
File f = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
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);
|
destFile = new File(f, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!destFile.exists()) {
|
if (!destFile.exists()) {
|
||||||
destFile.createNewFile();
|
destFile.createNewFile();
|
||||||
}
|
}
|
||||||
FileChannel source = null;
|
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));
|
AndroidUtilities.addMediaToGallery(Uri.fromFile(destFile));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -1940,7 +2288,7 @@ public class MediaController implements NotificationCenter.NotificationCenterDel
|
||||||
String str = new String(header);
|
String str = new String(header);
|
||||||
if (str != null) {
|
if (str != null) {
|
||||||
str = str.toLowerCase();
|
str = str.toLowerCase();
|
||||||
if (str.startsWith("riff") && str.endsWith("webp")){
|
if (str.startsWith("riff") && str.endsWith("webp")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -313,6 +313,8 @@ public class MessageObject {
|
||||||
} else {
|
} else {
|
||||||
messageText = LocaleController.getString("AttachSticker", R.string.AttachSticker);
|
messageText = LocaleController.getString("AttachSticker", R.string.AttachSticker);
|
||||||
}
|
}
|
||||||
|
} else if (isMusic()) {
|
||||||
|
messageText = LocaleController.getString("AttachMusic", R.string.AttachMusic);
|
||||||
} else {
|
} else {
|
||||||
String name = FileLoader.getDocumentFileName(message.media.document);
|
String name = FileLoader.getDocumentFileName(message.media.document);
|
||||||
if (name != null && name.length() > 0) {
|
if (name != null && name.length() > 0) {
|
||||||
|
@ -327,7 +329,9 @@ public class MessageObject {
|
||||||
} else {
|
} else {
|
||||||
messageText = message.message;
|
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 (message instanceof TLRPC.TL_message || message instanceof TLRPC.TL_messageForwarded_old2) {
|
||||||
if (isMediaEmpty()) {
|
if (isMediaEmpty()) {
|
||||||
|
@ -355,6 +359,9 @@ public class MessageObject {
|
||||||
type = 8;
|
type = 8;
|
||||||
} else if (message.media.document.mime_type.equals("image/webp") && isSticker()) {
|
} else if (message.media.document.mime_type.equals("image/webp") && isSticker()) {
|
||||||
type = 13;
|
type = 13;
|
||||||
|
} else if (isMusic()) {
|
||||||
|
type = 14;
|
||||||
|
contentType = 8;
|
||||||
} else {
|
} else {
|
||||||
type = 9;
|
type = 9;
|
||||||
}
|
}
|
||||||
|
@ -600,7 +607,7 @@ public class MessageObject {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (messageOwner.media != null && messageOwner.media.caption != null && messageOwner.media.caption.length() > 0) {
|
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)) {
|
if (containsUrls(caption)) {
|
||||||
try {
|
try {
|
||||||
Linkify.addLinks((Spannable) caption, Linkify.WEB_URLS);
|
Linkify.addLinks((Spannable) caption, Linkify.WEB_URLS);
|
||||||
|
@ -950,6 +957,17 @@ public class MessageObject {
|
||||||
return false;
|
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) {
|
public static TLRPC.InputStickerSet getInputStickerSet(TLRPC.Message message) {
|
||||||
if (message.media != null && message.media.document != null) {
|
if (message.media != null && message.media.document != null) {
|
||||||
for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) {
|
for (TLRPC.DocumentAttribute attribute : message.media.document.attributes) {
|
||||||
|
@ -986,6 +1004,8 @@ public class MessageObject {
|
||||||
return AndroidUtilities.dp(100);
|
return AndroidUtilities.dp(100);
|
||||||
} else if (type == 4) {
|
} else if (type == 4) {
|
||||||
return AndroidUtilities.dp(114);
|
return AndroidUtilities.dp(114);
|
||||||
|
} else if (type == 14) {
|
||||||
|
return AndroidUtilities.dp(78);
|
||||||
} else if (type == 13) {
|
} else if (type == 13) {
|
||||||
float maxHeight = AndroidUtilities.displaySize.y * 0.4f;
|
float maxHeight = AndroidUtilities.displaySize.y * 0.4f;
|
||||||
float maxWidth;
|
float maxWidth;
|
||||||
|
@ -1061,6 +1081,39 @@ public class MessageObject {
|
||||||
return isStickerMessage(messageOwner);
|
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() {
|
public TLRPC.InputStickerSet getInputStickerSet() {
|
||||||
return getInputStickerSet(messageOwner);
|
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() || !markAsReadEncrypted.isEmpty()) {
|
||||||
if (!markAsReadMessagesInbox.isEmpty() || !markAsReadMessagesOutbox.isEmpty()) {
|
if (!markAsReadMessagesInbox.isEmpty() || !markAsReadMessagesOutbox.isEmpty()) {
|
||||||
MessagesStorage.getInstance().updateDialogsWithReadedMessages(markAsReadMessagesInbox, true);
|
MessagesStorage.getInstance().updateDialogsWithReadMessages(markAsReadMessagesInbox, true);
|
||||||
}
|
}
|
||||||
MessagesStorage.getInstance().markMessagesAsRead(markAsReadMessagesInbox, markAsReadMessagesOutbox, markAsReadEncrypted, 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 {
|
try {
|
||||||
HashMap<Long, Integer> dialogsToUpdate = new HashMap<>();
|
HashMap<Long, Integer> dialogsToUpdate = new HashMap<>();
|
||||||
StringBuilder dialogsToReload = new StringBuilder();
|
StringBuilder dialogsToReload = new StringBuilder();
|
||||||
|
@ -1184,15 +1184,14 @@ 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()));
|
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()) {
|
if (cursor.next()) {
|
||||||
int count = cursor.intValue(0);
|
int count = cursor.intValue(0);
|
||||||
if (count == 0) {
|
if (count != 0) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
dialogsToUpdate.put((long) entry.getKey(), count);
|
dialogsToUpdate.put((long) entry.getKey(), count);
|
||||||
if (dialogsToReload.length() != 0) {
|
if (dialogsToReload.length() != 0) {
|
||||||
dialogsToReload.append(",");
|
dialogsToReload.append(",");
|
||||||
}
|
}
|
||||||
dialogsToReload.append(entry.getKey());
|
dialogsToReload.append(entry.getKey());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cursor.dispose();
|
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()) {
|
if (inbox.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1239,11 +1238,11 @@ public class MessagesStorage {
|
||||||
storageQueue.postRunnable(new Runnable() {
|
storageQueue.postRunnable(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
updateDialogsWithReadedMessagesInternal(null, inbox);
|
updateDialogsWithReadMessagesInternal(null, inbox);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
updateDialogsWithReadedMessagesInternal(null, inbox);
|
updateDialogsWithReadMessagesInternal(null, inbox);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3473,7 +3472,7 @@ public class MessagesStorage {
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesDeleted, mids);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.messagesDeleted, mids);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
MessagesStorage.getInstance().updateDialogsWithReadedMessagesInternal(mids, null);
|
MessagesStorage.getInstance().updateDialogsWithReadMessagesInternal(mids, null);
|
||||||
MessagesStorage.getInstance().markMessagesAsDeletedInternal(mids);
|
MessagesStorage.getInstance().markMessagesAsDeletedInternal(mids);
|
||||||
MessagesStorage.getInstance().updateDialogsWithDeletedMessagesInternal(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 botInfoDidLoaded = totalEvents++;
|
||||||
public static final int botKeyboardDidLoaded = totalEvents++;
|
public static final int botKeyboardDidLoaded = totalEvents++;
|
||||||
public static final int chatSearchResultsAvailable = totalEvents++;
|
public static final int chatSearchResultsAvailable = totalEvents++;
|
||||||
|
public static final int musicDidLoaded = totalEvents++;
|
||||||
|
|
||||||
public static final int httpFileDidLoaded = totalEvents++;
|
public static final int httpFileDidLoaded = totalEvents++;
|
||||||
public static final int httpFileDidFailedLoad = totalEvents++;
|
public static final int httpFileDidFailedLoad = totalEvents++;
|
||||||
|
@ -89,6 +90,7 @@ public class NotificationCenter {
|
||||||
|
|
||||||
public static final int audioProgressDidChanged = totalEvents++;
|
public static final int audioProgressDidChanged = totalEvents++;
|
||||||
public static final int audioDidReset = totalEvents++;
|
public static final int audioDidReset = totalEvents++;
|
||||||
|
public static final int audioPlayStateChanged = totalEvents++;
|
||||||
public static final int recordProgressChanged = totalEvents++;
|
public static final int recordProgressChanged = totalEvents++;
|
||||||
public static final int recordStarted = totalEvents++;
|
public static final int recordStarted = totalEvents++;
|
||||||
public static final int recordStartError = 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.NotificationManagerCompat;
|
||||||
import android.support.v4.app.RemoteInput;
|
import android.support.v4.app.RemoteInput;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
import org.telegram.messenger.ConnectionsManager;
|
import org.telegram.messenger.ConnectionsManager;
|
||||||
import org.telegram.messenger.DispatchQueue;
|
import org.telegram.messenger.DispatchQueue;
|
||||||
import org.telegram.messenger.FileLog;
|
import org.telegram.messenger.FileLog;
|
||||||
|
@ -602,11 +600,8 @@ public class NotificationsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
String lastMessage = null;
|
String lastMessage = null;
|
||||||
String lastMessageFull = null;
|
|
||||||
if (pushMessages.size() == 1) {
|
if (pushMessages.size() == 1) {
|
||||||
String message = lastMessageFull = getStringForMessage(pushMessages.get(0), false);
|
String message = lastMessage = getStringForMessage(pushMessages.get(0), false);
|
||||||
//lastMessage = getStringForMessage(pushMessages.get(0), true);
|
|
||||||
lastMessage = lastMessageFull;
|
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -630,8 +625,7 @@ public class NotificationsController {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
lastMessageFull = message;
|
lastMessage = message;
|
||||||
lastMessage = lastMessageFull;
|
|
||||||
}
|
}
|
||||||
if (pushDialogs.size() == 1) {
|
if (pushDialogs.size() == 1) {
|
||||||
if (replace) {
|
if (replace) {
|
||||||
|
@ -692,9 +686,6 @@ public class NotificationsController {
|
||||||
|
|
||||||
showExtraNotifications(mBuilder, notifyAboutLast);
|
showExtraNotifications(mBuilder, notifyAboutLast);
|
||||||
notificationManager.notify(1, mBuilder.build());
|
notificationManager.notify(1, mBuilder.build());
|
||||||
if (preferences.getBoolean("EnablePebbleNotifications", false)) {
|
|
||||||
sendAlertToPebble(lastMessageFull);
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleNotificationRepeat();
|
scheduleNotificationRepeat();
|
||||||
} catch (Exception e) {
|
} 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) {
|
public void processReadMessages(HashMap<Integer, Integer> inbox, long dialog_id, int max_date, int max_id, boolean isPopup) {
|
||||||
int oldCount = popupMessages.size();
|
int oldCount = popupMessages.size();
|
||||||
if (inbox != null) {
|
if (inbox != null) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import android.provider.MediaStore;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.telegram.android.audioinfo.AudioInfo;
|
||||||
import org.telegram.messenger.ConnectionsManager;
|
import org.telegram.messenger.ConnectionsManager;
|
||||||
import org.telegram.messenger.FileLoader;
|
import org.telegram.messenger.FileLoader;
|
||||||
import org.telegram.messenger.FileLog;
|
import org.telegram.messenger.FileLog;
|
||||||
|
@ -557,6 +558,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
||||||
|
|
||||||
for (int a = 0; a < messages.size(); a++) {
|
for (int a = 0; a < messages.size(); a++) {
|
||||||
MessageObject msgObj = messages.get(a);
|
MessageObject msgObj = messages.get(a);
|
||||||
|
if (msgObj.getId() <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final TLRPC.Message newMsg = new TLRPC.TL_message();
|
final TLRPC.Message newMsg = new TLRPC.TL_message();
|
||||||
newMsg.flags |= TLRPC.MESSAGE_FLAG_FWD;
|
newMsg.flags |= TLRPC.MESSAGE_FLAG_FWD;
|
||||||
|
@ -1858,6 +1862,7 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MimeTypeMap myMime = MimeTypeMap.getSingleton();
|
MimeTypeMap myMime = MimeTypeMap.getSingleton();
|
||||||
|
TLRPC.TL_documentAttributeAudio attributeAudio = null;
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
String extension = null;
|
String extension = null;
|
||||||
if (mime != null) {
|
if (mime != null) {
|
||||||
|
@ -1885,9 +1890,32 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
ext = path.substring(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) {
|
if (originalPath != null) {
|
||||||
|
if (attributeAudio != null) {
|
||||||
|
originalPath += "audio" + f.length();
|
||||||
|
} else {
|
||||||
originalPath += "" + f.length();
|
originalPath += "" + f.length();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TLRPC.TL_document document = null;
|
TLRPC.TL_document document = null;
|
||||||
if (!isEncrypted) {
|
if (!isEncrypted) {
|
||||||
|
@ -1905,6 +1933,9 @@ public class SendMessagesHelper implements NotificationCenter.NotificationCenter
|
||||||
document.attributes.add(fileName);
|
document.attributes.add(fileName);
|
||||||
document.size = (int) f.length();
|
document.size = (int) f.length();
|
||||||
document.dc_id = 0;
|
document.dc_id = 0;
|
||||||
|
if (attributeAudio != null) {
|
||||||
|
document.attributes.add(attributeAudio);
|
||||||
|
}
|
||||||
if (ext.length() != 0) {
|
if (ext.length() != 0) {
|
||||||
if (ext.toLowerCase().equals("webp")) {
|
if (ext.toLowerCase().equals("webp")) {
|
||||||
document.mime_type = "image/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);
|
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) {
|
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()) {
|
if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
package org.telegram.android;
|
package org.telegram.android;
|
||||||
|
|
||||||
|
import org.telegram.PhoneFormat.PhoneFormat;
|
||||||
import org.telegram.messenger.R;
|
import org.telegram.messenger.R;
|
||||||
import org.telegram.messenger.TLRPC;
|
import org.telegram.messenger.TLRPC;
|
||||||
|
|
||||||
|
@ -29,7 +30,8 @@ public class UserObject {
|
||||||
if (user == null || isDeleted(user)) {
|
if (user == null || isDeleted(user)) {
|
||||||
return LocaleController.getString("HiddenName", R.string.HiddenName);
|
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) {
|
public static String getFirstName(TLRPC.User user) {
|
||||||
|
@ -40,6 +42,6 @@ public class UserObject {
|
||||||
if (name == null || name.length() == 0) {
|
if (name == null || name.length() == 0) {
|
||||||
name = user.last_name;
|
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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package android.support.v7.util;
|
package org.telegram.android.support.util;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
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
|
* 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++;
|
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}.
|
* The class that controls the behavior of the {@link SortedList}.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -19,6 +19,9 @@ package org.telegram.android.support.widget;
|
||||||
import android.support.v4.util.Pools;
|
import android.support.v4.util.Pools;
|
||||||
import android.util.Log;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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.ViewCompat;
|
||||||
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
import android.support.v4.view.ViewPropertyAnimatorCompat;
|
||||||
import android.support.v4.view.ViewPropertyAnimatorListener;
|
import android.support.v4.view.ViewPropertyAnimatorListener;
|
||||||
|
|
||||||
import org.telegram.android.support.widget.RecyclerView.ViewHolder;
|
import org.telegram.android.support.widget.RecyclerView.ViewHolder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ import android.util.SparseIntArray;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import org.telegram.android.support.widget.RecyclerView;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -60,11 +60,14 @@ class LayoutState {
|
||||||
int mLayoutDirection;
|
int mLayoutDirection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used if you want to pre-layout items that are not yet visible.
|
* This is the target pixel closest to the start of the layout that we are trying to fill
|
||||||
* The difference with {@link #mAvailable} is that, when recycling, distance rendered for
|
|
||||||
* {@link #mExtra} is not considered not to recycle visible children.
|
|
||||||
*/
|
*/
|
||||||
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
|
* @return true if there are more items in the data adapter
|
||||||
|
@ -84,4 +87,16 @@ class LayoutState {
|
||||||
mCurrentPosition += mItemDirection;
|
mCurrentPosition += mItemDirection;
|
||||||
return view;
|
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.ViewCompat;
|
||||||
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
import android.support.v4.view.accessibility.AccessibilityEventCompat;
|
||||||
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
|
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.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -33,10 +39,11 @@ import java.util.List;
|
||||||
import static org.telegram.android.support.widget.RecyclerView.NO_POSITION;
|
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}.
|
* 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";
|
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.
|
* Re-used variable to keep anchor information on re-layout.
|
||||||
* Anchor position and coordinate defines the reference point for LLM while doing a 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
|
* 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.
|
* @param reverseLayout When set to true, layouts from end to start.
|
||||||
*/
|
*/
|
||||||
public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
|
public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
|
||||||
mAnchorInfo = new AnchorInfo();
|
|
||||||
setOrientation(orientation);
|
setOrientation(orientation);
|
||||||
setReverseLayout(reverseLayout);
|
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.
|
* 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.
|
* 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
|
* When set to true, If {@link RecyclerView} is LTR, than it will
|
||||||
* layout from RTL, if {@link android.support.v7.widget.RecyclerView}} is RTL, it will layout
|
* layout from RTL, if {@link RecyclerView}} is RTL, it will layout
|
||||||
* from LTR.
|
* from LTR.
|
||||||
*
|
*
|
||||||
* If you are looking for the exact same behavior of
|
* 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 firstChild = getPosition(getChildAt(0));
|
||||||
final int viewPosition = position - firstChild;
|
final int viewPosition = position - firstChild;
|
||||||
if (viewPosition >= 0 && viewPosition < childCount) {
|
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
|
// override layout from end values for consistency
|
||||||
anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
|
anchorInfo.mLayoutFromEnd = mShouldReverseLayout;
|
||||||
|
// if this changes, we should update prepareForDrop as well
|
||||||
if (mShouldReverseLayout) {
|
if (mShouldReverseLayout) {
|
||||||
anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
|
anchorInfo.mCoordinate = mOrientationHelper.getEndAfterPadding() -
|
||||||
mPendingScrollPositionOffset;
|
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.
|
* <code>item[10]</code>'s bottom is 20 pixels above the RecyclerView's bottom.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that scroll position change will not be reflected until the next layout call.
|
* Note that scroll position change will not be reflected until the next layout call.
|
||||||
*
|
|
||||||
* <p>
|
* <p>
|
||||||
* If you are just trying to make a position visible, use {@link #scrollToPosition(int)}.
|
* 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.
|
* 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
|
* @param dt This can be used to add additional padding to the visible area. This is used
|
||||||
* to
|
* to detect children that will go out of bounds after scrolling, without
|
||||||
* detect children that will go out of bounds after scrolling, without actually
|
* actually moving them.
|
||||||
* moving them.
|
|
||||||
*/
|
*/
|
||||||
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
|
private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
|
||||||
if (dt < 0) {
|
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.
|
* 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
|
* @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
|
* to detect children that will go out of bounds after scrolling, without
|
||||||
* actually moving them.
|
* 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
|
* @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
|
* we may consider moving it out of this view so passing around as a
|
||||||
* parameter for now, rather than accessing {@link #mLayoutState}
|
* parameter for now, rather than accessing {@link #mLayoutState}
|
||||||
* @see #recycleViewsFromStart(android.support.v7.widget.RecyclerView.Recycler, int)
|
* @see #recycleViewsFromStart(RecyclerView.Recycler, int)
|
||||||
* @see #recycleViewsFromEnd(android.support.v7.widget.RecyclerView.Recycler, int)
|
* @see #recycleViewsFromEnd(RecyclerView.Recycler, int)
|
||||||
* @see android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection
|
* @see android.support.v7.widget.LinearLayoutManager.LayoutState#mLayoutDirection
|
||||||
*/
|
*/
|
||||||
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
|
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
|
||||||
|
@ -1788,6 +1797,40 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager {
|
||||||
return mPendingSavedState == null && mLastStackFromEnd == mStackFromEnd;
|
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
|
* Helper class that keeps temporary state while {LayoutManager} is filling out the empty
|
||||||
* space.
|
* space.
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
|
|
||||||
package org.telegram.android.support.widget;
|
package org.telegram.android.support.widget;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.telegram.android.support.widget.AdapterHelper.UpdateOp;
|
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.ADD;
|
||||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.MOVE;
|
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.REMOVE;
|
||||||
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.UPDATE;
|
import static org.telegram.android.support.widget.AdapterHelper.UpdateOp.UPDATE;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
class OpReorderer {
|
class OpReorderer {
|
||||||
|
|
||||||
final Callback mCallback;
|
final Callback mCallback;
|
||||||
|
@ -83,7 +83,7 @@ class OpReorderer {
|
||||||
removeOp.positionStart--;
|
removeOp.positionStart--;
|
||||||
} else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
|
} else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
|
||||||
// move is removed.
|
// move is removed.
|
||||||
removeOp.itemCount --;
|
removeOp.itemCount--;
|
||||||
moveOp.cmd = REMOVE;
|
moveOp.cmd = REMOVE;
|
||||||
moveOp.itemCount = 1;
|
moveOp.itemCount = 1;
|
||||||
if (removeOp.itemCount == 0) {
|
if (removeOp.itemCount == 0) {
|
||||||
|
@ -228,7 +228,7 @@ class OpReorderer {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static interface Callback {
|
interface Callback {
|
||||||
|
|
||||||
UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount);
|
UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount);
|
||||||
|
|
||||||
|
|
|
@ -456,5 +456,4 @@ class PositionMap<E> implements Cloneable {
|
||||||
return ~lo; // value not present
|
return ~lo; // value not present
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.telegram.android.support.widget;
|
package org.telegram.android.support.widget;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.database.Observable;
|
import android.database.Observable;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
|
@ -30,6 +31,8 @@ import android.support.annotation.Nullable;
|
||||||
import android.support.v4.util.ArrayMap;
|
import android.support.v4.util.ArrayMap;
|
||||||
import android.support.v4.view.InputDeviceCompat;
|
import android.support.v4.view.InputDeviceCompat;
|
||||||
import android.support.v4.view.MotionEventCompat;
|
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.ScrollingView;
|
||||||
import android.support.v4.view.VelocityTrackerCompat;
|
import android.support.v4.view.VelocityTrackerCompat;
|
||||||
import android.support.v4.view.ViewCompat;
|
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.view.accessibility.AccessibilityRecordCompat;
|
||||||
import android.support.v4.widget.EdgeEffectCompat;
|
import android.support.v4.widget.EdgeEffectCompat;
|
||||||
import android.support.v4.widget.ScrollerCompat;
|
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.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
|
@ -58,12 +58,15 @@ import android.view.accessibility.AccessibilityEvent;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.view.animation.Interpolator;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
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.
|
* A flexible view for providing a limited window into a large data set.
|
||||||
*
|
*
|
||||||
|
@ -128,8 +131,10 @@ import java.util.List;
|
||||||
* <p>
|
* <p>
|
||||||
* When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
|
* 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.
|
* 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";
|
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.
|
* >Try increasing your pool size and item cache size.
|
||||||
*/
|
*/
|
||||||
private static final String TRACE_CREATE_VIEW_TAG = "RV CreateView";
|
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();
|
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
|
||||||
|
|
||||||
|
@ -283,6 +290,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
private boolean mAdapterUpdateDuringMeasure;
|
private boolean mAdapterUpdateDuringMeasure;
|
||||||
private final boolean mPostUpdatesOnAnimation;
|
private final boolean mPostUpdatesOnAnimation;
|
||||||
private final AccessibilityManager mAccessibilityManager;
|
private final AccessibilityManager mAccessibilityManager;
|
||||||
|
private List<OnChildAttachStateChangeListener> mOnChildAttachStateListeners;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true when an adapter data set changed notification is received.
|
* 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();
|
new ItemAnimatorRestoreListener();
|
||||||
private boolean mPostedAnimatorRunner = false;
|
private boolean mPostedAnimatorRunner = false;
|
||||||
private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
|
private RecyclerViewAccessibilityDelegate mAccessibilityDelegate;
|
||||||
|
private ChildDrawingOrderCallback mChildDrawingOrderCallback;
|
||||||
|
|
||||||
// simple array to keep min and max child position during a layout calculation
|
// simple array to keep min and max child position during a layout calculation
|
||||||
// preserved not to create a new one in each layout pass
|
// preserved not to create a new one in each layout pass
|
||||||
private final int[] mMinMaxLayoutPositions = new int[2];
|
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() {
|
private Runnable mItemAnimatorRunner = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -408,6 +422,10 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
mAccessibilityManager = (AccessibilityManager) getContext()
|
mAccessibilityManager = (AccessibilityManager) getContext()
|
||||||
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||||
setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));
|
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);
|
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() {
|
private void initChildrenHelper() {
|
||||||
mChildHelper = new ChildHelper(new ChildHelper.Callback() {
|
mChildHelper = new ChildHelper(new ChildHelper.Callback() {
|
||||||
@Override
|
@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.
|
* 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) {
|
public void addItemDecoration(ItemDecoration decor, int index) {
|
||||||
if (mLayout != null) {
|
if (mLayout != null) {
|
||||||
mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or"
|
mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or"
|
||||||
+ " layout");
|
+ "layout");
|
||||||
}
|
}
|
||||||
if (mItemDecorations.isEmpty()) {
|
if (mItemDecorations.isEmpty()) {
|
||||||
setWillNotDraw(false);
|
setWillNotDraw(false);
|
||||||
|
@ -1035,7 +1159,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
public void removeItemDecoration(ItemDecoration decor) {
|
public void removeItemDecoration(ItemDecoration decor) {
|
||||||
if (mLayout != null) {
|
if (mLayout != null) {
|
||||||
mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll or"
|
mLayout.assertNotInLayoutOrScroll("Cannot remove item decoration during a scroll or"
|
||||||
+ " layout");
|
+ "layout");
|
||||||
}
|
}
|
||||||
mItemDecorations.remove(decor);
|
mItemDecorations.remove(decor);
|
||||||
if (mItemDecorations.isEmpty()) {
|
if (mItemDecorations.isEmpty()) {
|
||||||
|
@ -1045,6 +1169,26 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
requestLayout();
|
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.
|
* 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 canScrollHorizontal = mLayout.canScrollHorizontally();
|
||||||
final boolean canScrollVertical = mLayout.canScrollVertically();
|
final boolean canScrollVertical = mLayout.canScrollVertically();
|
||||||
if (canScrollHorizontal || canScrollVertical) {
|
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 x The amount of horizontal scroll request
|
||||||
* @param y The amount of vertical scroll request
|
* @param y The amount of vertical scroll request
|
||||||
* @param fromMotionEvent If request is originated from a MotionEvent, this should be set to
|
* @param ev The originating MotionEvent, or null if not from a touch event.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* @return Whether any scroll was consumed in either direction.
|
* @return Whether any scroll was consumed in either direction.
|
||||||
*/
|
*/
|
||||||
boolean scrollByInternal(int x, int y, boolean fromMotionEvent, int motionX, int motionY) {
|
boolean scrollByInternal(int x, int y, MotionEvent ev) {
|
||||||
int overscrollX = 0, overscrollY = 0;
|
int unconsumedX = 0, unconsumedY = 0;
|
||||||
int hresult = 0, vresult = 0;
|
int consumedX = 0, consumedY = 0;
|
||||||
|
|
||||||
consumePendingUpdateOperations();
|
consumePendingUpdateOperations();
|
||||||
if (mAdapter != null) {
|
if (mAdapter != null) {
|
||||||
eatRequestLayout();
|
eatRequestLayout();
|
||||||
onEnterLayoutOrScroll();
|
onEnterLayoutOrScroll();
|
||||||
if (x != 0) {
|
if (x != 0) {
|
||||||
hresult = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
|
consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
|
||||||
overscrollX = x - hresult;
|
unconsumedX = x - consumedX;
|
||||||
}
|
}
|
||||||
if (y != 0) {
|
if (y != 0) {
|
||||||
vresult = mLayout.scrollVerticallyBy(y, mRecycler, mState);
|
consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
|
||||||
overscrollY = y - vresult;
|
unconsumedY = y - consumedY;
|
||||||
}
|
}
|
||||||
if (supportsChangeAnimations()) {
|
if (supportsChangeAnimations()) {
|
||||||
// Fix up shadow views used by changing animations
|
// Fix up shadow views used by changing animations
|
||||||
|
@ -1227,19 +1367,27 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
if (!mItemDecorations.isEmpty()) {
|
if (!mItemDecorations.isEmpty()) {
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
|
|
||||||
if (fromMotionEvent) {
|
if (dispatchNestedScroll(consumedX, consumedY, unconsumedX, unconsumedY, mScrollOffset)) {
|
||||||
pullGlows(motionX, overscrollX, motionY, overscrollY);
|
// 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);
|
considerReleasingGlowsOnScroll(x, y);
|
||||||
}
|
}
|
||||||
if (hresult != 0 || vresult != 0) {
|
if (consumedX != 0 || consumedY != 0) {
|
||||||
dispatchOnScrolled(hresult, vresult);
|
dispatchOnScrolled(consumedX, consumedY);
|
||||||
}
|
}
|
||||||
if (!awakenScrollBars()) {
|
if (!awakenScrollBars()) {
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
return hresult != 0 || vresult != 0;
|
return consumedX != 0 || consumedY != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1432,20 +1580,32 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
"Call setLayoutManager with a non-null argument.");
|
"Call setLayoutManager with a non-null argument.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
|
final boolean canScrollHorizontal = mLayout.canScrollHorizontally();
|
||||||
final boolean canScrollVertical = mLayout.canScrollVertically();
|
final boolean canScrollVertical = mLayout.canScrollVertically();
|
||||||
|
|
||||||
if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
|
if (!canScrollHorizontal || Math.abs(velocityX) < mMinFlingVelocity) {
|
||||||
velocityX = 0;
|
velocityX = 0;
|
||||||
}
|
}
|
||||||
if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
|
if (!canScrollVertical || Math.abs(velocityY) < mMinFlingVelocity) {
|
||||||
velocityY = 0;
|
velocityY = 0;
|
||||||
}
|
}
|
||||||
|
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));
|
velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
|
||||||
velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
|
velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
|
||||||
if (velocityX != 0 || velocityY != 0) {
|
|
||||||
mViewFlinger.fling(velocityX, velocityY);
|
mViewFlinger.fling(velocityX, velocityY);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
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
|
* 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;
|
boolean invalidate = false;
|
||||||
if (overscrollX < 0) {
|
if (overscrollX < 0) {
|
||||||
ensureLeftGlow();
|
ensureLeftGlow();
|
||||||
invalidate = mLeftGlow.onPull(-overscrollX / (float) getWidth(),
|
if (mLeftGlow.onPull(-overscrollX / getWidth(), 1f - y / getHeight())) {
|
||||||
1f - y / (float) getHeight()) || invalidate;
|
invalidate = true;
|
||||||
|
}
|
||||||
} else if (overscrollX > 0) {
|
} else if (overscrollX > 0) {
|
||||||
ensureRightGlow();
|
ensureRightGlow();
|
||||||
invalidate = mRightGlow.onPull(overscrollX / (float) getWidth(),
|
if (mRightGlow.onPull(overscrollX / getWidth(), y / getHeight())) {
|
||||||
y / (float) getHeight()) || invalidate;
|
invalidate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overscrollY < 0) {
|
if (overscrollY < 0) {
|
||||||
ensureTopGlow();
|
ensureTopGlow();
|
||||||
invalidate = mTopGlow.onPull(-overscrollY / (float) getHeight(),
|
if (mTopGlow.onPull(-overscrollY / getHeight(), x / getWidth())) {
|
||||||
x / (float) getWidth()) || invalidate;
|
invalidate = true;
|
||||||
|
}
|
||||||
} else if (overscrollY > 0) {
|
} else if (overscrollY > 0) {
|
||||||
ensureBottomGlow();
|
ensureBottomGlow();
|
||||||
invalidate = mBottomGlow.onPull(overscrollY / (float) getHeight(),
|
if (mBottomGlow.onPull(overscrollY / getHeight(), 1f - x / getWidth())) {
|
||||||
1f - x / (float) getWidth()) || invalidate;
|
invalidate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invalidate || overscrollX != 0 || overscrollY != 0) {
|
if (invalidate || overscrollX != 0 || overscrollY != 0) {
|
||||||
|
@ -1693,6 +1876,14 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
removeCallbacks(mItemAnimatorRunner);
|
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
|
* Checks if RecyclerView is in the middle of a layout or scroll and throws an
|
||||||
* {@link IllegalStateException} if it <b>is not</b>.
|
* {@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>
|
* for each incoming MotionEvent until the end of the gesture.</p>
|
||||||
*
|
*
|
||||||
* @param listener Listener to add
|
* @param listener Listener to add
|
||||||
|
* @see SimpleOnItemTouchListener
|
||||||
*/
|
*/
|
||||||
public void addOnItemTouchListener(OnItemTouchListener listener) {
|
public void addOnItemTouchListener(OnItemTouchListener listener) {
|
||||||
mOnItemTouchListeners.add(listener);
|
mOnItemTouchListeners.add(listener);
|
||||||
|
@ -1832,6 +2024,15 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
getParent().requestDisallowInterceptTouchEvent(true);
|
getParent().requestDisallowInterceptTouchEvent(true);
|
||||||
setScrollState(SCROLL_STATE_DRAGGING);
|
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;
|
break;
|
||||||
|
|
||||||
case MotionEventCompat.ACTION_POINTER_DOWN:
|
case MotionEventCompat.ACTION_POINTER_DOWN:
|
||||||
|
@ -1874,6 +2075,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
|
|
||||||
case MotionEvent.ACTION_UP: {
|
case MotionEvent.ACTION_UP: {
|
||||||
mVelocityTracker.clear();
|
mVelocityTracker.clear();
|
||||||
|
stopNestedScroll();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case MotionEvent.ACTION_CANCEL: {
|
case MotionEvent.ACTION_CANCEL: {
|
||||||
|
@ -1883,6 +2085,16 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
return mScrollState == SCROLL_STATE_DRAGGING;
|
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
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent e) {
|
public boolean onTouchEvent(MotionEvent e) {
|
||||||
if (dispatchOnItemTouch(e)) {
|
if (dispatchOnItemTouch(e)) {
|
||||||
|
@ -1898,14 +2110,29 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
}
|
}
|
||||||
mVelocityTracker.addMovement(e);
|
mVelocityTracker.addMovement(e);
|
||||||
|
|
||||||
|
final MotionEvent vtev = MotionEvent.obtain(e);
|
||||||
final int action = MotionEventCompat.getActionMasked(e);
|
final int action = MotionEventCompat.getActionMasked(e);
|
||||||
final int actionIndex = MotionEventCompat.getActionIndex(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) {
|
switch (action) {
|
||||||
case MotionEvent.ACTION_DOWN: {
|
case MotionEvent.ACTION_DOWN: {
|
||||||
mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
|
mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
|
||||||
mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
|
mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
|
||||||
mInitialTouchY = mLastTouchY = (int) (e.getY() + 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;
|
} break;
|
||||||
|
|
||||||
case MotionEventCompat.ACTION_POINTER_DOWN: {
|
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 x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
|
||||||
final int y = (int) (MotionEventCompat.getY(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) {
|
if (mScrollState != SCROLL_STATE_DRAGGING) {
|
||||||
final int dx = x - mInitialTouchX;
|
|
||||||
final int dy = y - mInitialTouchY;
|
|
||||||
boolean startScroll = false;
|
boolean startScroll = false;
|
||||||
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
|
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
|
||||||
mLastTouchX = mInitialTouchX + mTouchSlop * (dx < 0 ? -1 : 1);
|
if (dx > 0) {
|
||||||
|
dx -= mTouchSlop;
|
||||||
|
} else {
|
||||||
|
dx += mTouchSlop;
|
||||||
|
}
|
||||||
startScroll = true;
|
startScroll = true;
|
||||||
}
|
}
|
||||||
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
|
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
|
||||||
mLastTouchY = mInitialTouchY + mTouchSlop * (dy < 0 ? -1 : 1);
|
if (dy > 0) {
|
||||||
|
dy -= mTouchSlop;
|
||||||
|
} else {
|
||||||
|
dy += mTouchSlop;
|
||||||
|
}
|
||||||
startScroll = true;
|
startScroll = true;
|
||||||
}
|
}
|
||||||
if (startScroll) {
|
if (startScroll) {
|
||||||
setScrollState(SCROLL_STATE_DRAGGING);
|
setScrollState(SCROLL_STATE_DRAGGING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mScrollState == SCROLL_STATE_DRAGGING) {
|
if (mScrollState == SCROLL_STATE_DRAGGING) {
|
||||||
final int dx = x - mLastTouchX;
|
mLastTouchX = x - mScrollOffset[0];
|
||||||
final int dy = y - mLastTouchY;
|
mLastTouchY = y - mScrollOffset[1];
|
||||||
if (scrollByInternal(canScrollHorizontally ? -dx : 0,
|
|
||||||
canScrollVertically ? -dy : 0, true, x, y)) {
|
if (scrollByInternal(
|
||||||
|
canScrollHorizontally ? dx : 0,
|
||||||
|
canScrollVertically ? dy : 0,
|
||||||
|
vtev)) {
|
||||||
getParent().requestDisallowInterceptTouchEvent(true);
|
getParent().requestDisallowInterceptTouchEvent(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mLastTouchX = x;
|
|
||||||
mLastTouchY = y;
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case MotionEventCompat.ACTION_POINTER_UP: {
|
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))) {
|
if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
|
||||||
setScrollState(SCROLL_STATE_IDLE);
|
setScrollState(SCROLL_STATE_IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
mVelocityTracker.clear();
|
mVelocityTracker.clear();
|
||||||
releaseGlows();
|
releaseGlows();
|
||||||
} break;
|
} break;
|
||||||
|
@ -1974,6 +2222,8 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vtev.recycle();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1981,6 +2231,7 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
if (mVelocityTracker != null) {
|
if (mVelocityTracker != null) {
|
||||||
mVelocityTracker.clear();
|
mVelocityTracker.clear();
|
||||||
}
|
}
|
||||||
|
stopNestedScroll();
|
||||||
releaseGlows();
|
releaseGlows();
|
||||||
setScrollState(SCROLL_STATE_IDLE);
|
setScrollState(SCROLL_STATE_IDLE);
|
||||||
}
|
}
|
||||||
|
@ -2823,6 +3074,18 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
return mLayout.generateLayoutParams(p);
|
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() {
|
void saveOldPositions() {
|
||||||
final int childCount = mChildHelper.getUnfilteredChildCount();
|
final int childCount = mChildHelper.getUnfilteredChildCount();
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
@ -3232,6 +3495,11 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
return null;
|
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.
|
* Offset the bounds of all child views by <code>dy</code> pixels.
|
||||||
* Useful for implementing simple scrolling in {@link LayoutManager LayoutManagers}.
|
* 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) {
|
private void dispatchChildDetached(View child) {
|
||||||
if (mAdapter != null) {
|
final ViewHolder viewHolder = getChildViewHolderInt(child);
|
||||||
mAdapter.onViewDetachedFromWindow(getChildViewHolderInt(child));
|
|
||||||
}
|
|
||||||
onChildDetachedFromWindow(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) {
|
private void dispatchChildAttached(View child) {
|
||||||
if (mAdapter != null) {
|
final ViewHolder viewHolder = getChildViewHolderInt(child);
|
||||||
mAdapter.onViewAttachedToWindow(getChildViewHolderInt(child));
|
|
||||||
}
|
|
||||||
onChildAttachedToWindow(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 <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
|
* a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock
|
||||||
* layout managers are provided for general use.
|
* 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 {
|
public static abstract class LayoutManager {
|
||||||
ChildHelper mChildHelper;
|
ChildHelper mChildHelper;
|
||||||
|
@ -7259,6 +7550,20 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
int action, Bundle args) {
|
int action, Bundle args) {
|
||||||
return false;
|
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
|
* manipulation of item views within the RecyclerView. OnItemTouchListeners may intercept
|
||||||
* a touch interaction already in progress even if the RecyclerView is already handling that
|
* a touch interaction already in progress even if the RecyclerView is already handling that
|
||||||
* gesture stream itself for the purposes of scrolling.</p>
|
* 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
|
* 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.
|
* 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.
|
* the RecyclerView's coordinate system.
|
||||||
*/
|
*/
|
||||||
public void onTouchEvent(RecyclerView rv, MotionEvent e);
|
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
|
* An OnScrollListener can be set on a RecyclerView to receive messages
|
||||||
* when a scrolling event has occurred on that RecyclerView.
|
* 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
|
* want your components to be able to easily replace the listener use
|
||||||
* RecyclerView#setOnScrollListener.
|
* RecyclerView#setOnScrollListener.
|
||||||
*/
|
*/
|
||||||
abstract static public class OnScrollListener {
|
public abstract static class OnScrollListener {
|
||||||
/**
|
/**
|
||||||
* Callback method to be invoked when RecyclerView's scroll state changes.
|
* 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);
|
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.
|
* 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);
|
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 android.view.ViewGroup.MarginLayoutParams LayoutParams} subclass for children of
|
||||||
* {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
|
* {@link RecyclerView}. Custom {@link LayoutManager layout managers} are encouraged
|
||||||
|
@ -9318,4 +9731,35 @@ public class RecyclerView extends ViewGroup implements ScrollingView {
|
||||||
this.bottom = bottom;
|
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
|
@Override
|
||||||
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||||
ensureOrientationHelper();
|
ensureOrientationHelper();
|
||||||
|
|
||||||
final AnchorInfo anchorInfo = mAnchorInfo;
|
final AnchorInfo anchorInfo = mAnchorInfo;
|
||||||
anchorInfo.reset();
|
anchorInfo.reset();
|
||||||
|
|
||||||
|
@ -577,21 +576,22 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
detachAndScrapAttachedViews(recycler);
|
detachAndScrapAttachedViews(recycler);
|
||||||
mLaidOutInvalidFullSpan = false;
|
mLaidOutInvalidFullSpan = false;
|
||||||
updateMeasureSpecs();
|
updateMeasureSpecs();
|
||||||
|
updateLayoutState(anchorInfo.mPosition, state);
|
||||||
if (anchorInfo.mLayoutFromEnd) {
|
if (anchorInfo.mLayoutFromEnd) {
|
||||||
// Layout start.
|
// Layout start.
|
||||||
updateLayoutStateToFillStart(anchorInfo.mPosition, state);
|
setLayoutStateDirection(LAYOUT_START);
|
||||||
fill(recycler, mLayoutState, state);
|
fill(recycler, mLayoutState, state);
|
||||||
// Layout end.
|
// Layout end.
|
||||||
updateLayoutStateToFillEnd(anchorInfo.mPosition, state);
|
setLayoutStateDirection(LAYOUT_END);
|
||||||
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
|
mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection;
|
||||||
fill(recycler, mLayoutState, state);
|
fill(recycler, mLayoutState, state);
|
||||||
} else {
|
} else {
|
||||||
// Layout end.
|
// Layout end.
|
||||||
updateLayoutStateToFillEnd(anchorInfo.mPosition, state);
|
setLayoutStateDirection(LAYOUT_END);
|
||||||
fill(recycler, mLayoutState, state);
|
fill(recycler, mLayoutState, state);
|
||||||
// Layout start.
|
// Layout start.
|
||||||
updateLayoutStateToFillStart(anchorInfo.mPosition, state);
|
setLayoutStateDirection(LAYOUT_START);
|
||||||
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
|
mLayoutState.mCurrentPosition = anchorInfo.mPosition + mLayoutState.mItemDirection;
|
||||||
fill(recycler, mLayoutState, state);
|
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.mAvailable = 0;
|
||||||
mLayoutState.mCurrentPosition = anchorPosition;
|
mLayoutState.mCurrentPosition = anchorPosition;
|
||||||
|
int startExtra = 0;
|
||||||
|
int endExtra = 0;
|
||||||
if (isSmoothScrolling()) {
|
if (isSmoothScrolling()) {
|
||||||
final int targetPos = state.getTargetScrollPosition();
|
final int targetPos = state.getTargetScrollPosition();
|
||||||
|
if (targetPos != NO_POSITION) {
|
||||||
if (mShouldReverseLayout == targetPos < anchorPosition) {
|
if (mShouldReverseLayout == targetPos < anchorPosition) {
|
||||||
mLayoutState.mExtra = 0;
|
endExtra = mPrimaryOrientation.getTotalSpace();
|
||||||
} else {
|
} else {
|
||||||
mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace();
|
startExtra = mPrimaryOrientation.getTotalSpace();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
mLayoutState.mExtra = 0;
|
|
||||||
}
|
}
|
||||||
mLayoutState.mLayoutDirection = LAYOUT_START;
|
|
||||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL
|
|
||||||
: ITEM_DIRECTION_HEAD;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateLayoutStateToFillEnd(int anchorPosition, RecyclerView.State state) {
|
// Line of the furthest row.
|
||||||
mLayoutState.mAvailable = 0;
|
final boolean clipToPadding = getClipToPadding();
|
||||||
mLayoutState.mCurrentPosition = anchorPosition;
|
if (clipToPadding) {
|
||||||
if (isSmoothScrolling()) {
|
mLayoutState.mStartLine = mPrimaryOrientation.getStartAfterPadding() - startExtra;
|
||||||
final int targetPos = state.getTargetScrollPosition();
|
mLayoutState.mEndLine = mPrimaryOrientation.getEndAfterPadding() + endExtra;
|
||||||
if (mShouldReverseLayout == targetPos > anchorPosition) {
|
|
||||||
mLayoutState.mExtra = 0;
|
|
||||||
} else {
|
} else {
|
||||||
mLayoutState.mExtra = mPrimaryOrientation.getTotalSpace();
|
mLayoutState.mEndLine = mPrimaryOrientation.getEnd() + endExtra;
|
||||||
|
mLayoutState.mStartLine = -startExtra;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
mLayoutState.mExtra = 0;
|
|
||||||
}
|
}
|
||||||
mLayoutState.mLayoutDirection = LAYOUT_END;
|
|
||||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD
|
private void setLayoutStateDirection(int direction) {
|
||||||
: ITEM_DIRECTION_TAIL;
|
mLayoutState.mLayoutDirection = direction;
|
||||||
|
mLayoutState.mItemDirection = (mShouldReverseLayout == (direction == LAYOUT_START)) ?
|
||||||
|
ITEM_DIRECTION_TAIL : ITEM_DIRECTION_HEAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1383,31 +1380,25 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
mRemainingSpans.set(0, mSpanCount, true);
|
mRemainingSpans.set(0, mSpanCount, true);
|
||||||
// The target position we are trying to reach.
|
// The target position we are trying to reach.
|
||||||
final int targetLine;
|
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.
|
// Line of the furthest row.
|
||||||
if (layoutState.mLayoutDirection == LAYOUT_END) {
|
if (layoutState.mLayoutDirection == LAYOUT_END) {
|
||||||
// ignore padding for recycler
|
targetLine = layoutState.mEndLine + layoutState.mAvailable;
|
||||||
recycleLine = mPrimaryOrientation.getEndAfterPadding() + mLayoutState.mAvailable;
|
|
||||||
targetLine = recycleLine + mLayoutState.mExtra + mPrimaryOrientation.getEndPadding();
|
|
||||||
|
|
||||||
} else { // LAYOUT_START
|
} else { // LAYOUT_START
|
||||||
// ignore padding for recycler
|
targetLine = layoutState.mStartLine - layoutState.mAvailable;
|
||||||
recycleLine = mPrimaryOrientation.getStartAfterPadding() - mLayoutState.mAvailable;
|
|
||||||
targetLine = recycleLine - mLayoutState.mExtra -
|
|
||||||
mPrimaryOrientation.getStartAfterPadding();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine);
|
updateAllRemainingSpans(layoutState.mLayoutDirection, targetLine);
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "FILLING targetLine: " + targetLine + "," +
|
||||||
|
"remaining spans:" + mRemainingSpans + ", state: " + layoutState);
|
||||||
|
}
|
||||||
|
|
||||||
// the default coordinate to add new view.
|
// the default coordinate to add new view.
|
||||||
final int defaultNewViewLine = mShouldReverseLayout
|
final int defaultNewViewLine = mShouldReverseLayout
|
||||||
? mPrimaryOrientation.getEndAfterPadding()
|
? mPrimaryOrientation.getEndAfterPadding()
|
||||||
: mPrimaryOrientation.getStartAfterPadding();
|
: mPrimaryOrientation.getStartAfterPadding();
|
||||||
|
boolean added = false;
|
||||||
while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty()) {
|
while (layoutState.hasMore(state) && !mRemainingSpans.isEmpty()) {
|
||||||
View view = layoutState.next(recycler);
|
View view = layoutState.next(recycler);
|
||||||
LayoutParams lp = ((LayoutParams) view.getLayoutParams());
|
LayoutParams lp = ((LayoutParams) view.getLayoutParams());
|
||||||
|
@ -1500,18 +1491,21 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
} else {
|
} else {
|
||||||
updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine);
|
updateRemainingSpans(currentSpan, mLayoutState.mLayoutDirection, targetLine);
|
||||||
}
|
}
|
||||||
recycle(recycler, mLayoutState, currentSpan, recycleLine);
|
recycle(recycler, mLayoutState);
|
||||||
|
added = true;
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (!added) {
|
||||||
Log.d(TAG, "fill, " + getChildCount());
|
recycle(recycler, mLayoutState);
|
||||||
}
|
}
|
||||||
|
final int diff;
|
||||||
if (mLayoutState.mLayoutDirection == LAYOUT_START) {
|
if (mLayoutState.mLayoutDirection == LAYOUT_START) {
|
||||||
final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding());
|
final int minStart = getMinStart(mPrimaryOrientation.getStartAfterPadding());
|
||||||
return Math.max(0, mLayoutState.mAvailable + (recycleLine - minStart));
|
diff = mPrimaryOrientation.getStartAfterPadding() - minStart;
|
||||||
} else {
|
} else {
|
||||||
final int max = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
|
final int maxEnd = getMaxEnd(mPrimaryOrientation.getEndAfterPadding());
|
||||||
return Math.max(0, mLayoutState.mAvailable + (max - recycleLine));
|
diff = maxEnd - mPrimaryOrientation.getEndAfterPadding();
|
||||||
}
|
}
|
||||||
|
return diff > 0 ? Math.min(layoutState.mAvailable, diff) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LazySpanLookup.FullSpanItem createFullSpanItemFromEnd(int newItemTop) {
|
private LazySpanLookup.FullSpanItem createFullSpanItemFromEnd(int newItemTop) {
|
||||||
|
@ -1548,19 +1542,40 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState,
|
private void recycle(RecyclerView.Recycler recycler, LayoutState layoutState) {
|
||||||
Span updatedSpan, int recycleLine) {
|
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 {
|
||||||
|
// scrolling case, recycle line can be shifted by how much space we could cover
|
||||||
|
// by adding new views
|
||||||
if (layoutState.mLayoutDirection == LAYOUT_START) {
|
if (layoutState.mLayoutDirection == LAYOUT_START) {
|
||||||
// calculate recycle line
|
// calculate recycle line
|
||||||
int maxStart = getMaxStart(updatedSpan.getStartLine());
|
int scrolled = layoutState.mStartLine - getMaxStart(layoutState.mStartLine);
|
||||||
recycleFromEnd(recycler, Math.max(recycleLine, maxStart) +
|
final int line;
|
||||||
(mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding()));
|
if (scrolled < 0) {
|
||||||
|
line = layoutState.mEndLine;
|
||||||
|
} else {
|
||||||
|
line = layoutState.mEndLine - Math.min(scrolled, layoutState.mAvailable);
|
||||||
|
}
|
||||||
|
recycleFromEnd(recycler, line);
|
||||||
} else {
|
} else {
|
||||||
// calculate recycle line
|
// calculate recycle line
|
||||||
int minEnd = getMinEnd(updatedSpan.getEndLine());
|
int scrolled = getMinEnd(layoutState.mEndLine) - layoutState.mEndLine;
|
||||||
recycleFromStart(recycler, Math.min(recycleLine, minEnd) -
|
final int line;
|
||||||
(mPrimaryOrientation.getEnd() - mPrimaryOrientation.getStartAfterPadding()));
|
if (scrolled < 0) {
|
||||||
|
line = layoutState.mStartLine;
|
||||||
|
} else {
|
||||||
|
line = layoutState.mStartLine + Math.min(scrolled, layoutState.mAvailable);
|
||||||
}
|
}
|
||||||
|
recycleFromStart(recycler, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendViewToAllSpans(View view) {
|
private void appendViewToAllSpans(View view) {
|
||||||
|
@ -1602,12 +1617,12 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
final int deletedSize = span.getDeletedSize();
|
final int deletedSize = span.getDeletedSize();
|
||||||
if (layoutDir == LAYOUT_START) {
|
if (layoutDir == LAYOUT_START) {
|
||||||
final int line = span.getStartLine();
|
final int line = span.getStartLine();
|
||||||
if (line + deletedSize < targetLine) {
|
if (line + deletedSize <= targetLine) {
|
||||||
mRemainingSpans.set(span.mIndex, false);
|
mRemainingSpans.set(span.mIndex, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final int line = span.getEndLine();
|
final int line = span.getEndLine();
|
||||||
if (line - deletedSize > targetLine) {
|
if (line - deletedSize >= targetLine) {
|
||||||
mRemainingSpans.set(span.mIndex, false);
|
mRemainingSpans.set(span.mIndex, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1678,18 +1693,24 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recycleFromStart(RecyclerView.Recycler recycler, int line) {
|
private void recycleFromStart(RecyclerView.Recycler recycler, int line) {
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "recycling from start for line " + line);
|
|
||||||
}
|
|
||||||
while (getChildCount() > 0) {
|
while (getChildCount() > 0) {
|
||||||
View child = getChildAt(0);
|
View child = getChildAt(0);
|
||||||
if (mPrimaryOrientation.getDecoratedEnd(child) < line) {
|
if (mPrimaryOrientation.getDecoratedEnd(child) <= line) {
|
||||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
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) {
|
if (lp.mFullSpan) {
|
||||||
|
for (int j = 0; j < mSpanCount; j++) {
|
||||||
|
if (mSpans[j].mViews.size() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int j = 0; j < mSpanCount; j++) {
|
for (int j = 0; j < mSpanCount; j++) {
|
||||||
mSpans[j].popStart();
|
mSpans[j].popStart();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (lp.mSpan.mViews.size() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
lp.mSpan.popStart();
|
lp.mSpan.popStart();
|
||||||
}
|
}
|
||||||
removeAndRecycleView(child, recycler);
|
removeAndRecycleView(child, recycler);
|
||||||
|
@ -1704,13 +1725,22 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
int i;
|
int i;
|
||||||
for (i = childCount - 1; i >= 0; i--) {
|
for (i = childCount - 1; i >= 0; i--) {
|
||||||
View child = getChildAt(i);
|
View child = getChildAt(i);
|
||||||
if (mPrimaryOrientation.getDecoratedStart(child) > line) {
|
if (mPrimaryOrientation.getDecoratedStart(child) >= line) {
|
||||||
LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
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) {
|
if (lp.mFullSpan) {
|
||||||
|
for (int j = 0; j < mSpanCount; j++) {
|
||||||
|
if (mSpans[j].mViews.size() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int j = 0; j < mSpanCount; j++) {
|
for (int j = 0; j < mSpanCount; j++) {
|
||||||
mSpans[j].popEnd();
|
mSpans[j].popEnd();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (lp.mSpan.mViews.size() == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
lp.mSpan.popEnd();
|
lp.mSpan.popEnd();
|
||||||
}
|
}
|
||||||
removeAndRecycleView(child, recycler);
|
removeAndRecycleView(child, recycler);
|
||||||
|
@ -1860,21 +1890,19 @@ public class StaggeredGridLayoutManager extends RecyclerView.LayoutManager {
|
||||||
int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
int scrollBy(int dt, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||||
ensureOrientationHelper();
|
ensureOrientationHelper();
|
||||||
final int referenceChildPosition;
|
final int referenceChildPosition;
|
||||||
|
final int layoutDir;
|
||||||
if (dt > 0) { // layout towards end
|
if (dt > 0) { // layout towards end
|
||||||
mLayoutState.mLayoutDirection = LAYOUT_END;
|
layoutDir = LAYOUT_END;
|
||||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_HEAD
|
|
||||||
: ITEM_DIRECTION_TAIL;
|
|
||||||
referenceChildPosition = getLastChildPosition();
|
referenceChildPosition = getLastChildPosition();
|
||||||
} else {
|
} else {
|
||||||
mLayoutState.mLayoutDirection = LAYOUT_START;
|
layoutDir = LAYOUT_START;
|
||||||
mLayoutState.mItemDirection = mShouldReverseLayout ? ITEM_DIRECTION_TAIL
|
|
||||||
: ITEM_DIRECTION_HEAD;
|
|
||||||
referenceChildPosition = getFirstChildPosition();
|
referenceChildPosition = getFirstChildPosition();
|
||||||
}
|
}
|
||||||
|
updateLayoutState(referenceChildPosition, state);
|
||||||
|
setLayoutStateDirection(layoutDir);
|
||||||
mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
|
mLayoutState.mCurrentPosition = referenceChildPosition + mLayoutState.mItemDirection;
|
||||||
final int absDt = Math.abs(dt);
|
final int absDt = Math.abs(dt);
|
||||||
mLayoutState.mAvailable = absDt;
|
mLayoutState.mAvailable = absDt;
|
||||||
mLayoutState.mExtra = isSmoothScrolling() ? mPrimaryOrientation.getTotalSpace() : 0;
|
|
||||||
int consumed = fill(recycler, mLayoutState, state);
|
int consumed = fill(recycler, mLayoutState, state);
|
||||||
final int totalScroll;
|
final int totalScroll;
|
||||||
if (absDt < consumed) {
|
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;
|
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;
|
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);
|
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
|
||||||
int selectedBackground = preferences.getInt("selectedBackground", 1000001);
|
int selectedBackground = preferences.getInt("selectedBackground", 1000001);
|
||||||
selectedColor = preferences.getInt("selectedColor", 0);
|
selectedColor = preferences.getInt("selectedColor", 0);
|
||||||
int cacheColorHint = 0;
|
|
||||||
if (selectedColor == 0) {
|
if (selectedColor == 0) {
|
||||||
if (selectedBackground == 1000001) {
|
if (selectedBackground == 1000001) {
|
||||||
cachedWallpaper = applicationContext.getResources().getDrawable(R.drawable.background_hd);
|
cachedWallpaper = applicationContext.getResources().getDrawable(R.drawable.background_hd);
|
||||||
|
|
|
@ -363,7 +363,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
|
||||||
try {
|
try {
|
||||||
SerializedData data = new SerializedData(configFile);
|
SerializedData data = new SerializedData(configFile);
|
||||||
isTestBackend = data.readInt32(false);
|
isTestBackend = data.readInt32(false);
|
||||||
int version = data.readInt32(false);
|
data.readInt32(false);
|
||||||
sessionsToDestroy.clear();
|
sessionsToDestroy.clear();
|
||||||
int count = data.readInt32(false);
|
int count = data.readInt32(false);
|
||||||
for (int a = 0; a < count; a++) {
|
for (int a = 0; a < count; a++) {
|
||||||
|
@ -2700,7 +2700,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int messageLength = data.readInt32(false);
|
data.readInt32(false);
|
||||||
|
|
||||||
TLObject message = deserialize(getRequestWithMessageId(messageId), data, true);
|
TLObject message = deserialize(getRequestWithMessageId(messageId), data, true);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ public class FileLoadOperation {
|
||||||
private final static int stateFinished = 3;
|
private final static int stateFinished = 3;
|
||||||
|
|
||||||
private final static int downloadChunkSize = 1024 * 32;
|
private final static int downloadChunkSize = 1024 * 32;
|
||||||
|
private final static int downloadChunkSizeBig = 1024 * 128;
|
||||||
private final static int maxDownloadRequests = 3;
|
private final static int maxDownloadRequests = 3;
|
||||||
|
|
||||||
private int datacenter_id;
|
private int datacenter_id;
|
||||||
|
@ -38,6 +39,7 @@ public class FileLoadOperation {
|
||||||
private FileLoadOperationDelegate delegate;
|
private FileLoadOperationDelegate delegate;
|
||||||
private byte[] key;
|
private byte[] key;
|
||||||
private byte[] iv;
|
private byte[] iv;
|
||||||
|
private int currentDownloadChunkSize;
|
||||||
|
|
||||||
private int nextDownloadOffset = 0;
|
private int nextDownloadOffset = 0;
|
||||||
private ArrayList<RequestInfo> requestInfos = new ArrayList<>(maxDownloadRequests);
|
private ArrayList<RequestInfo> requestInfos = new ArrayList<>(maxDownloadRequests);
|
||||||
|
@ -165,6 +167,7 @@ public class FileLoadOperation {
|
||||||
if (state != stateIdle) {
|
if (state != stateIdle) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
currentDownloadChunkSize = totalBytesCount >= 1024 * 1024 * 30 ? downloadChunkSizeBig : downloadChunkSize;
|
||||||
state = stateDownloading;
|
state = stateDownloading;
|
||||||
if (location == null) {
|
if (location == null) {
|
||||||
Utilities.stageQueue.postRunnable(new Runnable() {
|
Utilities.stageQueue.postRunnable(new Runnable() {
|
||||||
|
@ -175,7 +178,6 @@ public class FileLoadOperation {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Long mediaId = null;
|
|
||||||
String fileNameFinal;
|
String fileNameFinal;
|
||||||
String fileNameTemp;
|
String fileNameTemp;
|
||||||
String fileNameIv = null;
|
String fileNameIv = null;
|
||||||
|
@ -223,7 +225,7 @@ public class FileLoadOperation {
|
||||||
cacheFileTemp = new File(tempPath, fileNameTemp);
|
cacheFileTemp = new File(tempPath, fileNameTemp);
|
||||||
if (cacheFileTemp.exists()) {
|
if (cacheFileTemp.exists()) {
|
||||||
downloadedBytes = (int)cacheFileTemp.length();
|
downloadedBytes = (int)cacheFileTemp.length();
|
||||||
nextDownloadOffset = downloadedBytes = downloadedBytes / 1024 * 1024;
|
nextDownloadOffset = downloadedBytes = downloadedBytes / currentDownloadChunkSize * currentDownloadChunkSize;
|
||||||
}
|
}
|
||||||
if (fileNameIv != null) {
|
if (fileNameIv != null) {
|
||||||
cacheIvTemp = new File(tempPath, fileNameIv);
|
cacheIvTemp = new File(tempPath, fileNameIv);
|
||||||
|
@ -388,10 +390,10 @@ public class FileLoadOperation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentBytesSize != downloadChunkSize) {
|
if (currentBytesSize != currentDownloadChunkSize) {
|
||||||
onFinishLoadingFile();
|
onFinishLoadingFile();
|
||||||
} else {
|
} else {
|
||||||
if (totalBytesCount != downloadedBytes && downloadedBytes % downloadChunkSize == 0 || totalBytesCount > 0 && totalBytesCount > downloadedBytes) {
|
if (totalBytesCount != downloadedBytes && downloadedBytes % currentDownloadChunkSize == 0 || totalBytesCount > 0 && totalBytesCount > downloadedBytes) {
|
||||||
startDownloadRequest();
|
startDownloadRequest();
|
||||||
} else {
|
} else {
|
||||||
onFinishLoadingFile();
|
onFinishLoadingFile();
|
||||||
|
@ -422,7 +424,7 @@ public class FileLoadOperation {
|
||||||
startDownloadRequest();
|
startDownloadRequest();
|
||||||
}
|
}
|
||||||
} else if (error.text.contains("OFFSET_INVALID")) {
|
} else if (error.text.contains("OFFSET_INVALID")) {
|
||||||
if (downloadedBytes % downloadChunkSize == 0) {
|
if (downloadedBytes % currentDownloadChunkSize == 0) {
|
||||||
try {
|
try {
|
||||||
onFinishLoadingFile();
|
onFinishLoadingFile();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -460,12 +462,12 @@ public class FileLoadOperation {
|
||||||
if (totalBytesCount > 0 && nextDownloadOffset >= totalBytesCount) {
|
if (totalBytesCount > 0 && nextDownloadOffset >= totalBytesCount) {
|
||||||
break;
|
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();
|
TLRPC.TL_upload_getFile req = new TLRPC.TL_upload_getFile();
|
||||||
req.location = location;
|
req.location = location;
|
||||||
req.offset = nextDownloadOffset;
|
req.offset = nextDownloadOffset;
|
||||||
req.limit = downloadChunkSize;
|
req.limit = currentDownloadChunkSize;
|
||||||
nextDownloadOffset += downloadChunkSize;
|
nextDownloadOffset += currentDownloadChunkSize;
|
||||||
|
|
||||||
final RequestInfo requestInfo = new RequestInfo();
|
final RequestInfo requestInfo = new RequestInfo();
|
||||||
requestInfos.add(requestInfo);
|
requestInfos.add(requestInfo);
|
||||||
|
|
|
@ -675,6 +675,15 @@ public class FileLoader {
|
||||||
return closestObject;
|
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) {
|
public static String getDocumentFileName(TLRPC.Document document) {
|
||||||
if (document != null) {
|
if (document != null) {
|
||||||
if (document.file_name != 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));
|
FileLog.d("tmessages", String.format("===== Duplicate message id %d received, ignoring", messageId));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int messageLength = data.readInt32(false);
|
data.readInt32(false);
|
||||||
|
|
||||||
int constructor = data.readInt32(false);
|
int constructor = data.readInt32(false);
|
||||||
TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, false);
|
TLObject object = TLClassStore.Instance().TLdeserialize(data, constructor, false);
|
||||||
|
|
|
@ -11634,6 +11634,9 @@ public class TLRPC {
|
||||||
case 0x9eddf188:
|
case 0x9eddf188:
|
||||||
result = new TL_inputMessagesFilterDocument();
|
result = new TL_inputMessagesFilterDocument();
|
||||||
break;
|
break;
|
||||||
|
case 0x5afbf764:
|
||||||
|
result = new TL_inputMessagesFilterAudioDocuments();
|
||||||
|
break;
|
||||||
case 0x9fc00e65:
|
case 0x9fc00e65:
|
||||||
result = new TL_inputMessagesFilterVideo();
|
result = new TL_inputMessagesFilterVideo();
|
||||||
break;
|
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 class TL_inputMessagesFilterVideo extends MessagesFilter {
|
||||||
public static int constructor = 0x9fc00e65;
|
public static int constructor = 0x9fc00e65;
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ public class TcpConnection extends ConnectionContext {
|
||||||
FileLog.e("tmessages", e2);
|
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;
|
firstPacket = true;
|
||||||
if (restOfTheData != null) {
|
if (restOfTheData != null) {
|
||||||
BuffersStorage.getInstance().reuseFreeBuffer(restOfTheData);
|
BuffersStorage.getInstance().reuseFreeBuffer(restOfTheData);
|
||||||
|
|
|
@ -15,7 +15,6 @@ import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
@ -23,6 +22,7 @@ import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.telegram.android.AndroidUtilities;
|
import org.telegram.android.AndroidUtilities;
|
||||||
|
import org.telegram.messenger.ApplicationLoader;
|
||||||
import org.telegram.messenger.R;
|
import org.telegram.messenger.R;
|
||||||
import org.telegram.ui.Components.LayoutHelper;
|
import org.telegram.ui.Components.LayoutHelper;
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@ public class ActionBar extends FrameLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FrameLayout titleFrameLayout;
|
|
||||||
private ImageView backButtonImageView;
|
private ImageView backButtonImageView;
|
||||||
private TextView titleTextView;
|
private TextView titleTextView;
|
||||||
private TextView subTitleTextView;
|
private TextView subTitleTextView;
|
||||||
|
@ -60,116 +59,6 @@ public class ActionBar extends FrameLayout {
|
||||||
|
|
||||||
public ActionBar(Context context) {
|
public ActionBar(Context context) {
|
||||||
super(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() {
|
private void createBackButtonImage() {
|
||||||
|
@ -177,9 +66,10 @@ public class ActionBar extends FrameLayout {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
backButtonImageView = new ImageView(getContext());
|
backButtonImageView = new ImageView(getContext());
|
||||||
titleFrameLayout.addView(backButtonImageView);
|
|
||||||
backButtonImageView.setScaleType(ImageView.ScaleType.CENTER);
|
backButtonImageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||||
backButtonImageView.setBackgroundResource(itemsBackgroundResourceId);
|
backButtonImageView.setBackgroundResource(itemsBackgroundResourceId);
|
||||||
|
addView(backButtonImageView, LayoutHelper.createFrame(54, 54, Gravity.LEFT | Gravity.TOP));
|
||||||
|
|
||||||
backButtonImageView.setOnClickListener(new OnClickListener() {
|
backButtonImageView.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
@ -195,31 +85,19 @@ public class ActionBar extends FrameLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBackButtonDrawable(Drawable drawable) {
|
public void setBackButtonDrawable(Drawable drawable) {
|
||||||
boolean reposition = false;
|
|
||||||
if (backButtonImageView == null) {
|
if (backButtonImageView == null) {
|
||||||
createBackButtonImage();
|
createBackButtonImage();
|
||||||
} else {
|
|
||||||
reposition = true;
|
|
||||||
}
|
}
|
||||||
backButtonImageView.setVisibility(drawable == null ? GONE : VISIBLE);
|
backButtonImageView.setVisibility(drawable == null ? GONE : VISIBLE);
|
||||||
backButtonImageView.setImageDrawable(drawable);
|
backButtonImageView.setImageDrawable(drawable);
|
||||||
if (reposition) {
|
|
||||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBackButtonImage(int resource) {
|
public void setBackButtonImage(int resource) {
|
||||||
boolean reposition = false;
|
|
||||||
if (backButtonImageView == null) {
|
if (backButtonImageView == null) {
|
||||||
createBackButtonImage();
|
createBackButtonImage();
|
||||||
} else {
|
|
||||||
reposition = true;
|
|
||||||
}
|
}
|
||||||
backButtonImageView.setVisibility(resource == 0 ? GONE : VISIBLE);
|
backButtonImageView.setVisibility(resource == 0 ? GONE : VISIBLE);
|
||||||
backButtonImageView.setImageResource(resource);
|
backButtonImageView.setImageResource(resource);
|
||||||
if (reposition) {
|
|
||||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createSubtitleTextView() {
|
private void createSubtitleTextView() {
|
||||||
|
@ -227,13 +105,13 @@ public class ActionBar extends FrameLayout {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
subTitleTextView = new TextView(getContext());
|
subTitleTextView = new TextView(getContext());
|
||||||
titleFrameLayout.addView(subTitleTextView);
|
|
||||||
subTitleTextView.setGravity(Gravity.LEFT);
|
subTitleTextView.setGravity(Gravity.LEFT);
|
||||||
subTitleTextView.setTextColor(0xffd7e8f7);
|
subTitleTextView.setTextColor(0xffd7e8f7);
|
||||||
subTitleTextView.setSingleLine(true);
|
subTitleTextView.setSingleLine(true);
|
||||||
subTitleTextView.setLines(1);
|
subTitleTextView.setLines(1);
|
||||||
subTitleTextView.setMaxLines(1);
|
subTitleTextView.setMaxLines(1);
|
||||||
subTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
|
subTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||||
|
addView(subTitleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSubtitle(CharSequence value) {
|
public void setSubtitle(CharSequence value) {
|
||||||
|
@ -243,22 +121,6 @@ public class ActionBar extends FrameLayout {
|
||||||
if (subTitleTextView != null) {
|
if (subTitleTextView != null) {
|
||||||
subTitleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
subTitleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
||||||
subTitleTextView.setText(value);
|
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 = new TextView(getContext());
|
||||||
titleTextView.setGravity(Gravity.LEFT);
|
titleTextView.setGravity(Gravity.LEFT);
|
||||||
titleTextView.setSingleLine(true);
|
|
||||||
titleTextView.setLines(1);
|
titleTextView.setLines(1);
|
||||||
titleTextView.setMaxLines(1);
|
titleTextView.setMaxLines(1);
|
||||||
|
titleTextView.setSingleLine(true);
|
||||||
titleTextView.setEllipsize(TextUtils.TruncateAt.END);
|
titleTextView.setEllipsize(TextUtils.TruncateAt.END);
|
||||||
titleFrameLayout.addView(titleTextView);
|
|
||||||
titleTextView.setTextColor(0xffffffff);
|
titleTextView.setTextColor(0xffffffff);
|
||||||
titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
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) {
|
public void setTitle(CharSequence value) {
|
||||||
boolean created = false;
|
|
||||||
if (value != null && titleTextView == null) {
|
if (value != null && titleTextView == null) {
|
||||||
createTitleTextView();
|
createTitleTextView();
|
||||||
created = true;
|
|
||||||
}
|
}
|
||||||
if (titleTextView != null) {
|
if (titleTextView != null) {
|
||||||
lastTitle = value;
|
lastTitle = value;
|
||||||
titleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
titleTextView.setVisibility(value != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
||||||
titleTextView.setText(value);
|
titleTextView.setText(value);
|
||||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
|
||||||
if (!created) {
|
|
||||||
titleTextView.setText(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTitleIcon(int resourceId, int padding) {
|
public TextView getSubTitleTextView() {
|
||||||
if (resourceId != 0 && titleTextView == null) {
|
return subTitleTextView;
|
||||||
createTitleTextView();
|
|
||||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
|
||||||
}
|
}
|
||||||
titleTextView.setCompoundDrawablesWithIntrinsicBounds(resourceId, 0, 0, 0);
|
|
||||||
titleTextView.setCompoundDrawablePadding(padding);
|
public TextView getTitleTextView() {
|
||||||
|
return titleTextView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Drawable getSubTitleIcon() {
|
public Drawable getSubTitleIcon() {
|
||||||
|
@ -332,17 +187,6 @@ public class ActionBar extends FrameLayout {
|
||||||
actionBarMenuOnItemClick = listener;
|
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() {
|
public ActionBarMenu createActionMode() {
|
||||||
if (actionMode != null) {
|
if (actionMode != null) {
|
||||||
return actionMode;
|
return actionMode;
|
||||||
|
@ -358,7 +202,7 @@ public class ActionBar extends FrameLayout {
|
||||||
actionMode.setLayoutParams(layoutParams);
|
actionMode.setLayoutParams(layoutParams);
|
||||||
actionMode.setVisibility(INVISIBLE);
|
actionMode.setVisibility(INVISIBLE);
|
||||||
|
|
||||||
if (occupyStatusBar) {
|
if (occupyStatusBar && actionModeTop == null) {
|
||||||
actionModeTop = new View(getContext());
|
actionModeTop = new View(getContext());
|
||||||
actionModeTop.setBackgroundColor(0x99000000);
|
actionModeTop.setBackgroundColor(0x99000000);
|
||||||
addView(actionModeTop);
|
addView(actionModeTop);
|
||||||
|
@ -381,8 +225,14 @@ public class ActionBar extends FrameLayout {
|
||||||
if (occupyStatusBar && actionModeTop != null) {
|
if (occupyStatusBar && actionModeTop != null) {
|
||||||
actionModeTop.setVisibility(VISIBLE);
|
actionModeTop.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
if (titleFrameLayout != null) {
|
if (titleTextView != null) {
|
||||||
titleFrameLayout.setVisibility(INVISIBLE);
|
titleTextView.setVisibility(INVISIBLE);
|
||||||
|
}
|
||||||
|
if (subTitleTextView != null) {
|
||||||
|
subTitleTextView.setVisibility(INVISIBLE);
|
||||||
|
}
|
||||||
|
if (backButtonImageView != null) {
|
||||||
|
backButtonImageView.setVisibility(INVISIBLE);
|
||||||
}
|
}
|
||||||
if (menu != null) {
|
if (menu != null) {
|
||||||
menu.setVisibility(INVISIBLE);
|
menu.setVisibility(INVISIBLE);
|
||||||
|
@ -397,14 +247,33 @@ public class ActionBar extends FrameLayout {
|
||||||
if (occupyStatusBar && actionModeTop != null) {
|
if (occupyStatusBar && actionModeTop != null) {
|
||||||
actionModeTop.setVisibility(INVISIBLE);
|
actionModeTop.setVisibility(INVISIBLE);
|
||||||
}
|
}
|
||||||
if (titleFrameLayout != null) {
|
if (titleTextView != null) {
|
||||||
titleFrameLayout.setVisibility(VISIBLE);
|
titleTextView.setVisibility(VISIBLE);
|
||||||
|
}
|
||||||
|
if (subTitleTextView != null) {
|
||||||
|
subTitleTextView.setVisibility(VISIBLE);
|
||||||
|
}
|
||||||
|
if (backButtonImageView != null) {
|
||||||
|
backButtonImageView.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
if (menu != null) {
|
if (menu != null) {
|
||||||
menu.setVisibility(VISIBLE);
|
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() {
|
public boolean isActionModeShowed() {
|
||||||
return actionMode != null && actionMode.getVisibility() == VISIBLE;
|
return actionMode != null && actionMode.getVisibility() == VISIBLE;
|
||||||
}
|
}
|
||||||
|
@ -439,12 +308,136 @@ public class ActionBar extends FrameLayout {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
int actionBarHeight = AndroidUtilities.getCurrentActionBarHeight();
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
positionBackImage(actionBarHeight);
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
positionMenu(MeasureSpec.getSize(widthMeasureSpec), actionBarHeight);
|
int actionBarHeight = getCurrentActionBarHeight();
|
||||||
positionTitle(MeasureSpec.getSize(widthMeasureSpec), actionBarHeight);
|
int actionBarHeightSpec = MeasureSpec.makeMeasureSpec(actionBarHeight, MeasureSpec.EXACTLY);
|
||||||
actionBarHeight += occupyStatusBar ? AndroidUtilities.statusBarHeight : 0;
|
|
||||||
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(actionBarHeight + extraHeight, 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() {
|
public void onMenuButtonPressed() {
|
||||||
|
@ -474,10 +467,13 @@ public class ActionBar extends FrameLayout {
|
||||||
if (titleTextView != null) {
|
if (titleTextView != null) {
|
||||||
titleTextView.setVisibility(textToSet != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
titleTextView.setVisibility(textToSet != null && !isSearchFieldVisible ? VISIBLE : INVISIBLE);
|
||||||
titleTextView.setText(textToSet);
|
titleTextView.setText(textToSet);
|
||||||
positionTitle(getMeasuredWidth(), getMeasuredHeight());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSearchFieldVisible() {
|
||||||
|
return isSearchFieldVisible;
|
||||||
|
}
|
||||||
|
|
||||||
public void setExtraHeight(int value, boolean layout) {
|
public void setExtraHeight(int value, boolean layout) {
|
||||||
extraHeight = value;
|
extraHeight = value;
|
||||||
if (layout) {
|
if (layout) {
|
||||||
|
@ -520,4 +516,14 @@ public class ActionBar extends FrameLayout {
|
||||||
super.onTouchEvent(event);
|
super.onTouchEvent(event);
|
||||||
return true;
|
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) {
|
if (child instanceof ActionBar) {
|
||||||
return super.drawChild(canvas, child, drawingTime);
|
return super.drawChild(canvas, child, drawingTime);
|
||||||
} else {
|
} else {
|
||||||
boolean wasActionBar = false;
|
//boolean wasActionBar = false;
|
||||||
int actionBarHeight = 0;
|
int actionBarHeight = 0;
|
||||||
int childCount = getChildCount();
|
int childCount = getChildCount();
|
||||||
for (int a = 0; a < childCount; a++) {
|
for (int a = 0; a < childCount; a++) {
|
||||||
|
@ -339,7 +339,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 2);
|
BaseFragment lastFragment = fragmentsStack.get(fragmentsStack.size() - 2);
|
||||||
View fragmentView = lastFragment.fragmentView;
|
View fragmentView = lastFragment.fragmentView;
|
||||||
if (fragmentView == null) {
|
if (fragmentView == null) {
|
||||||
fragmentView = lastFragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
fragmentView = lastFragment.createView(parentActivity);
|
||||||
} else {
|
} else {
|
||||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
@ -625,7 +625,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
fragment.setParentLayout(this);
|
fragment.setParentLayout(this);
|
||||||
View fragmentView = fragment.fragmentView;
|
View fragmentView = fragment.fragmentView;
|
||||||
if (fragmentView == null) {
|
if (fragmentView == null) {
|
||||||
fragmentView = fragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
fragmentView = fragment.createView(parentActivity);
|
||||||
} else {
|
} else {
|
||||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
@ -829,7 +829,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
previousFragment.setParentLayout(this);
|
previousFragment.setParentLayout(this);
|
||||||
View fragmentView = previousFragment.fragmentView;
|
View fragmentView = previousFragment.fragmentView;
|
||||||
if (fragmentView == null) {
|
if (fragmentView == null) {
|
||||||
fragmentView = previousFragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
fragmentView = previousFragment.createView(parentActivity);
|
||||||
} else {
|
} else {
|
||||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
@ -972,7 +972,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
previousFragment.setParentLayout(this);
|
previousFragment.setParentLayout(this);
|
||||||
View fragmentView = previousFragment.fragmentView;
|
View fragmentView = previousFragment.fragmentView;
|
||||||
if (fragmentView == null) {
|
if (fragmentView == null) {
|
||||||
fragmentView = previousFragment.createView(parentActivity, parentActivity.getLayoutInflater());
|
fragmentView = previousFragment.createView(parentActivity);
|
||||||
} else {
|
} else {
|
||||||
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
ViewGroup parent = (ViewGroup) fragmentView.getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class ActionBarMenu extends LinearLayout {
|
||||||
View view = li.inflate(resourceId, null);
|
View view = li.inflate(resourceId, null);
|
||||||
view.setTag(id);
|
view.setTag(id);
|
||||||
addView(view);
|
addView(view);
|
||||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)view.getLayoutParams();
|
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) view.getLayoutParams();
|
||||||
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
||||||
view.setBackgroundResource(parentActionBar.itemsBackgroundResourceId);
|
view.setBackgroundResource(parentActionBar.itemsBackgroundResourceId);
|
||||||
view.setLayoutParams(layoutParams);
|
view.setLayoutParams(layoutParams);
|
||||||
|
@ -74,14 +74,14 @@ public class ActionBarMenu extends LinearLayout {
|
||||||
menuItem.iconView.setImageResource(icon);
|
menuItem.iconView.setImageResource(icon);
|
||||||
}
|
}
|
||||||
addView(menuItem);
|
addView(menuItem);
|
||||||
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)menuItem.getLayoutParams();
|
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) menuItem.getLayoutParams();
|
||||||
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
layoutParams.height = LayoutHelper.MATCH_PARENT;
|
||||||
layoutParams.width = width;
|
layoutParams.width = width;
|
||||||
menuItem.setLayoutParams(layoutParams);
|
menuItem.setLayoutParams(layoutParams);
|
||||||
menuItem.setOnClickListener(new OnClickListener() {
|
menuItem.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||||
if (item.hasSubMenu()) {
|
if (item.hasSubMenu()) {
|
||||||
if (parentActionBar.actionBarMenuOnItemClick.canOpenMenu()) {
|
if (parentActionBar.actionBarMenuOnItemClick.canOpenMenu()) {
|
||||||
item.toggleSubMenu();
|
item.toggleSubMenu();
|
||||||
|
@ -89,7 +89,7 @@ public class ActionBarMenu extends LinearLayout {
|
||||||
} else if (item.isSearchField()) {
|
} else if (item.isSearchField()) {
|
||||||
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
||||||
} else {
|
} 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++) {
|
for (int a = 0; a < getChildCount(); a++) {
|
||||||
View view = getChildAt(a);
|
View view = getChildAt(a);
|
||||||
if (view instanceof ActionBarMenuItem) {
|
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++) {
|
for (int a = 0; a < getChildCount(); a++) {
|
||||||
View view = getChildAt(a);
|
View view = getChildAt(a);
|
||||||
if (view instanceof ActionBarMenuItem) {
|
if (view instanceof ActionBarMenuItem) {
|
||||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||||
if (item.hasSubMenu() && item.getVisibility() == VISIBLE) {
|
if (item.getVisibility() != VISIBLE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item.hasSubMenu()) {
|
||||||
item.toggleSubMenu();
|
item.toggleSubMenu();
|
||||||
break;
|
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++) {
|
for (int a = 0; a < getChildCount(); a++) {
|
||||||
View view = getChildAt(a);
|
View view = getChildAt(a);
|
||||||
if (view instanceof ActionBarMenuItem) {
|
if (view instanceof ActionBarMenuItem) {
|
||||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||||
if (item.isSearchField()) {
|
if (item.isSearchField()) {
|
||||||
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
||||||
break;
|
break;
|
||||||
|
@ -148,7 +154,7 @@ public class ActionBarMenu extends LinearLayout {
|
||||||
for (int a = 0; a < getChildCount(); a++) {
|
for (int a = 0; a < getChildCount(); a++) {
|
||||||
View view = getChildAt(a);
|
View view = getChildAt(a);
|
||||||
if (view instanceof ActionBarMenuItem) {
|
if (view instanceof ActionBarMenuItem) {
|
||||||
ActionBarMenuItem item = (ActionBarMenuItem)view;
|
ActionBarMenuItem item = (ActionBarMenuItem) view;
|
||||||
if (item.isSearchField()) {
|
if (item.isSearchField()) {
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
parentActionBar.onSearchFieldVisibilityChanged(item.toggleSearch());
|
||||||
|
@ -164,7 +170,7 @@ public class ActionBarMenu extends LinearLayout {
|
||||||
public ActionBarMenuItem getItem(int id) {
|
public ActionBarMenuItem getItem(int id) {
|
||||||
View v = findViewWithTag(id);
|
View v = findViewWithTag(id);
|
||||||
if (v instanceof ActionBarMenuItem) {
|
if (v instanceof ActionBarMenuItem) {
|
||||||
return (ActionBarMenuItem)v;
|
return (ActionBarMenuItem) v;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,6 +82,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
private int subMenuOpenSide = 0;
|
private int subMenuOpenSide = 0;
|
||||||
private ActionBarMenuItemDelegate delegate;
|
private ActionBarMenuItemDelegate delegate;
|
||||||
private boolean allowCloseAnimation = true;
|
private boolean allowCloseAnimation = true;
|
||||||
|
protected boolean overrideMenuClick;
|
||||||
|
private boolean processedPopupClick;
|
||||||
|
|
||||||
public ActionBarMenuItem(Context context, ActionBarMenu menu, int background) {
|
public ActionBarMenuItem(Context context, ActionBarMenu menu, int background) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -129,21 +131,23 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
x -= location[0];
|
x -= location[0];
|
||||||
y -= location[1];
|
y -= location[1];
|
||||||
selectedMenuView = null;
|
selectedMenuView = null;
|
||||||
for (int a = 0; a < popupLayout.getChildCount(); a++) {
|
for (int a = 0; a < popupLayout.getItemsCount(); a++) {
|
||||||
View child = popupLayout.getChildAt(a);
|
View child = popupLayout.getItemAt(a);
|
||||||
child.getHitRect(rect);
|
child.getHitRect(rect);
|
||||||
if ((Integer) child.getTag() < 100) {
|
if ((Integer) child.getTag() < 100) {
|
||||||
if (!rect.contains((int) x, (int) y)) {
|
if (!rect.contains((int) x, (int) y)) {
|
||||||
child.setPressed(false);
|
child.setPressed(false);
|
||||||
child.setSelected(false);
|
child.setSelected(false);
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT == 21) {
|
||||||
child.getBackground().setVisible(false, false);
|
child.getBackground().setVisible(false, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
child.setPressed(true);
|
child.setPressed(true);
|
||||||
child.setSelected(true);
|
child.setSelected(true);
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
if (Build.VERSION.SDK_INT == 21) {
|
||||||
child.getBackground().setVisible(true, false);
|
child.getBackground().setVisible(true, false);
|
||||||
|
}
|
||||||
child.drawableHotspotChanged(x, y - child.getTop());
|
child.drawableHotspotChanged(x, y - child.getTop());
|
||||||
}
|
}
|
||||||
selectedMenuView = child;
|
selectedMenuView = child;
|
||||||
|
@ -192,9 +196,6 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
rect = new Rect();
|
rect = new Rect();
|
||||||
location = new int[2];
|
location = new int[2];
|
||||||
popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getContext());
|
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() {
|
popupLayout.setOnTouchListener(new OnTouchListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
@ -252,6 +253,10 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
if (popupWindow != null && popupWindow.isShowing()) {
|
if (popupWindow != null && popupWindow.isShowing()) {
|
||||||
|
if (processedPopupClick) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processedPopupClick = true;
|
||||||
popupWindow.dismiss(allowCloseAnimation);
|
popupWindow.dismiss(allowCloseAnimation);
|
||||||
}
|
}
|
||||||
if (parentMenu != null) {
|
if (parentMenu != null) {
|
||||||
|
@ -306,6 +311,7 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
processedPopupClick = false;
|
||||||
popupWindow.setFocusable(true);
|
popupWindow.setFocusable(true);
|
||||||
if (popupLayout.getMeasuredWidth() == 0) {
|
if (popupLayout.getMeasuredWidth() == 0) {
|
||||||
updateOrShowPopup(true, true);
|
updateOrShowPopup(true, true);
|
||||||
|
@ -367,6 +373,11 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
return setIsSearchField(value, true);
|
return setIsSearchField(value, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionBarMenuItem setOverrideMenuClick(boolean value) {
|
||||||
|
overrideMenuClick = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ActionBarMenuItem setIsSearchField(boolean value, boolean needClearButton) {
|
public ActionBarMenuItem setIsSearchField(boolean value, boolean needClearButton) {
|
||||||
if (parentMenu == null) {
|
if (parentMenu == null) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -389,7 +400,8 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
searchField.setSingleLine(true);
|
searchField.setSingleLine(true);
|
||||||
searchField.setBackgroundResource(0);
|
searchField.setBackgroundResource(0);
|
||||||
searchField.setPadding(0, 0, 0, 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) {
|
if (android.os.Build.VERSION.SDK_INT < 11) {
|
||||||
searchField.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
|
searchField.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
|
||||||
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
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.width = LayoutHelper.MATCH_PARENT;
|
||||||
layoutParams2.gravity = Gravity.CENTER_VERTICAL;
|
layoutParams2.gravity = Gravity.CENTER_VERTICAL;
|
||||||
layoutParams2.height = AndroidUtilities.dp(36);
|
layoutParams2.height = AndroidUtilities.dp(36);
|
||||||
layoutParams2.rightMargin = AndroidUtilities.dp(48);
|
layoutParams2.rightMargin = needClearButton ? AndroidUtilities.dp(48) : 0;
|
||||||
searchField.setLayoutParams(layoutParams2);
|
searchField.setLayoutParams(layoutParams2);
|
||||||
|
|
||||||
if (needClearButton) {
|
if (needClearButton) {
|
||||||
|
@ -532,6 +544,10 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (show) {
|
||||||
|
popupLayout.scrollToTop();
|
||||||
|
}
|
||||||
|
|
||||||
if (subMenuOpenSide == 0) {
|
if (subMenuOpenSide == 0) {
|
||||||
if (showFromBottom) {
|
if (showFromBottom) {
|
||||||
if (show) {
|
if (show) {
|
||||||
|
@ -574,10 +590,6 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
view.setVisibility(GONE);
|
view.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
view = popupLayout.findViewWithTag(100 + id);
|
|
||||||
if (view != null) {
|
|
||||||
view.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showSubItem(int id) {
|
public void showSubItem(int id) {
|
||||||
|
@ -585,9 +597,5 @@ public class ActionBarMenuItem extends FrameLayoutFixed {
|
||||||
if (view != null) {
|
if (view != null) {
|
||||||
view.setVisibility(VISIBLE);
|
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.os.Build;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.animation.DecelerateInterpolator;
|
import android.view.animation.DecelerateInterpolator;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.PopupWindow;
|
import android.widget.PopupWindow;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
import org.telegram.android.AndroidUtilities;
|
import org.telegram.android.AndroidUtilities;
|
||||||
import org.telegram.messenger.FileLog;
|
import org.telegram.messenger.FileLog;
|
||||||
import org.telegram.messenger.R;
|
import org.telegram.messenger.R;
|
||||||
|
import org.telegram.ui.Components.LayoutHelper;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -62,7 +66,7 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
void onDispatchKeyEvent(KeyEvent keyEvent);
|
void onDispatchKeyEvent(KeyEvent keyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ActionBarPopupWindowLayout extends LinearLayout {
|
public static class ActionBarPopupWindowLayout extends FrameLayout {
|
||||||
|
|
||||||
private OnDispatchKeyEventListener mOnDispatchKeyEventListener;
|
private OnDispatchKeyEventListener mOnDispatchKeyEventListener;
|
||||||
protected static Drawable backgroundDrawable;
|
protected static Drawable backgroundDrawable;
|
||||||
|
@ -73,13 +77,26 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
private boolean showedFromBotton;
|
private boolean showedFromBotton;
|
||||||
private HashMap<View, Integer> positions = new HashMap<>();
|
private HashMap<View, Integer> positions = new HashMap<>();
|
||||||
|
|
||||||
|
private ScrollView scrollView;
|
||||||
|
private LinearLayout linearLayout;
|
||||||
|
|
||||||
public ActionBarPopupWindowLayout(Context context) {
|
public ActionBarPopupWindowLayout(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
setWillNotDraw(false);
|
|
||||||
|
|
||||||
if (backgroundDrawable == null) {
|
if (backgroundDrawable == null) {
|
||||||
backgroundDrawable = getResources().getDrawable(R.drawable.popup_fixed);
|
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) {
|
public void setShowedFromBotton(boolean value) {
|
||||||
|
@ -106,15 +123,15 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
public void setBackScaleY(float value) {
|
public void setBackScaleY(float value) {
|
||||||
backScaleY = value;
|
backScaleY = value;
|
||||||
if (animationEnabled) {
|
if (animationEnabled) {
|
||||||
int count = getChildCount();
|
int count = getItemsCount();
|
||||||
int visibleCount = 0;
|
int visibleCount = 0;
|
||||||
for (int a = 0; a < count; a++) {
|
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);
|
int height = getMeasuredHeight() - AndroidUtilities.dp(16);
|
||||||
if (showedFromBotton) {
|
if (showedFromBotton) {
|
||||||
for (int a = lastStartedChild; a >= 0; a--) {
|
for (int a = lastStartedChild; a >= 0; a--) {
|
||||||
View child = getChildAt(a);
|
View child = getItemAt(a);
|
||||||
if (child.getVisibility() != VISIBLE) {
|
if (child.getVisibility() != VISIBLE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -127,7 +144,7 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int a = lastStartedChild; a < count; a++) {
|
for (int a = lastStartedChild; a < count; a++) {
|
||||||
View child = getChildAt(a);
|
View child = getItemAt(a);
|
||||||
if (child.getVisibility() != VISIBLE) {
|
if (child.getVisibility() != VISIBLE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -155,6 +172,11 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addView(View child) {
|
||||||
|
linearLayout.addView(child);
|
||||||
|
}
|
||||||
|
|
||||||
public float getBackScaleX() {
|
public float getBackScaleX() {
|
||||||
return backScaleX;
|
return backScaleX;
|
||||||
}
|
}
|
||||||
|
@ -183,6 +205,18 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
backgroundDrawable.draw(canvas);
|
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() {
|
public ActionBarPopupWindow() {
|
||||||
|
@ -269,11 +303,11 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
content.setAlpha(1.0f);
|
content.setAlpha(1.0f);
|
||||||
content.setPivotX(content.getMeasuredWidth());
|
content.setPivotX(content.getMeasuredWidth());
|
||||||
content.setPivotY(0);
|
content.setPivotY(0);
|
||||||
int count = content.getChildCount();
|
int count = content.getItemsCount();
|
||||||
content.positions.clear();
|
content.positions.clear();
|
||||||
int visibleCount = 0;
|
int visibleCount = 0;
|
||||||
for (int a = 0; a < count; a++) {
|
for (int a = 0; a < count; a++) {
|
||||||
View child = content.getChildAt(a);
|
View child = content.getItemAt(a);
|
||||||
if (child.getVisibility() != View.VISIBLE) {
|
if (child.getVisibility() != View.VISIBLE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
@ -44,7 +43,7 @@ public class BaseFragment {
|
||||||
classGuid = ConnectionsManager.getInstance().generateClassGuid();
|
classGuid = ConnectionsManager.getInstance().generateClassGuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,18 @@
|
||||||
package org.telegram.ui.ActionBar;
|
package org.telegram.ui.ActionBar;
|
||||||
|
|
||||||
import android.animation.Animator;
|
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.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
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.ColorDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -45,7 +53,7 @@ import java.util.ArrayList;
|
||||||
|
|
||||||
public class BottomSheet extends Dialog {
|
public class BottomSheet extends Dialog {
|
||||||
|
|
||||||
private LinearLayout linearLayout;
|
private LinearLayout containerView;
|
||||||
private FrameLayout container;
|
private FrameLayout container;
|
||||||
|
|
||||||
private boolean dismissed;
|
private boolean dismissed;
|
||||||
|
@ -57,24 +65,73 @@ public class BottomSheet extends Dialog {
|
||||||
private int[] itemIcons;
|
private int[] itemIcons;
|
||||||
private View customView;
|
private View customView;
|
||||||
private CharSequence title;
|
private CharSequence title;
|
||||||
private boolean overrideTabletWidth = true;
|
private boolean fullWidth;
|
||||||
private boolean isGrid;
|
private boolean isGrid;
|
||||||
private ColorDrawable backgroundDrawable = new ColorDrawable(0xff000000);
|
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 revealX;
|
||||||
private int revealY;
|
private int revealY;
|
||||||
private boolean useRevealAnimation;
|
private boolean applyTopPaddings = true;
|
||||||
|
|
||||||
private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
|
private DecelerateInterpolator decelerateInterpolator = new DecelerateInterpolator();
|
||||||
private AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
|
private AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
|
||||||
|
|
||||||
private ArrayList<BottomSheetCell> itemViews = new ArrayList<>();
|
private ArrayList<BottomSheetCell> itemViews = new ArrayList<>();
|
||||||
|
|
||||||
private BottomSheetDelegate delegate;
|
private BottomSheetDelegateInterface delegate;
|
||||||
|
|
||||||
public interface BottomSheetDelegate {
|
public interface BottomSheetDelegateInterface {
|
||||||
void onOpenAnimationStart();
|
void onOpenAnimationStart();
|
||||||
|
|
||||||
void onOpenAnimationEnd();
|
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 {
|
private static class BottomSheetCell extends FrameLayout {
|
||||||
|
@ -139,7 +196,40 @@ public class BottomSheet extends Dialog {
|
||||||
public BottomSheet(Context context) {
|
public BottomSheet(Context context) {
|
||||||
super(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() {
|
container.setOnTouchListener(new View.OnTouchListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
@ -159,25 +249,37 @@ public class BottomSheet extends Dialog {
|
||||||
window.requestFeature(Window.FEATURE_NO_TITLE);
|
window.requestFeature(Window.FEATURE_NO_TITLE);
|
||||||
window.setWindowAnimations(R.style.DialogNoAnimation);
|
window.setWindowAnimations(R.style.DialogNoAnimation);
|
||||||
|
|
||||||
setContentView(container);
|
if (shadowDrawable == null) {
|
||||||
|
Rect padding = new Rect();
|
||||||
linearLayout = new LinearLayout(getContext());
|
shadowDrawable = getContext().getResources().getDrawable(R.drawable.sheet_shadow);
|
||||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
shadowDrawable.getPadding(padding);
|
||||||
if (AndroidUtilities.isTablet() && !overrideTabletWidth) {
|
backgroundPaddingLeft = padding.left;
|
||||||
container.addView(linearLayout, 0, LayoutHelper.createFrame(320, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL));
|
backgroundPaddingTop = padding.top;
|
||||||
} else {
|
|
||||||
container.addView(linearLayout, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
View shadow = new View(getContext());
|
setContentView(container, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
shadow.setBackgroundResource(R.drawable.header_shadow_reverse);
|
|
||||||
linearLayout.addView(shadow, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 3));
|
|
||||||
|
|
||||||
LinearLayout containerView = new LinearLayout(getContext());
|
ciclePaint.setColor(0xffffffff);
|
||||||
containerView.setBackgroundColor(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.setOrientation(LinearLayout.VERTICAL);
|
||||||
containerView.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(isGrid ? 16 : 8));
|
container.addView(containerView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM));
|
||||||
linearLayout.addView(containerView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
|
|
||||||
|
|
||||||
if (title != null) {
|
if (title != null) {
|
||||||
TextView titleView = new TextView(getContext());
|
TextView titleView = new TextView(getContext());
|
||||||
|
@ -273,43 +375,162 @@ public class BottomSheet extends Dialog {
|
||||||
setOnShowListener(new OnShowListener() {
|
setOnShowListener(new OnShowListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onShow(DialogInterface dialog) {
|
public void onShow(DialogInterface dialog) {
|
||||||
if (useRevealAnimation) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
int finalRadius = Math.max(AndroidUtilities.displaySize.x, container.getHeight());
|
startOpenAnimation();
|
||||||
Animator anim = ViewAnimationUtils.createCircularReveal(container, revealX, revealY, 0, finalRadius);
|
}
|
||||||
anim.setDuration(400);
|
}
|
||||||
anim.addListener(new Animator.AnimatorListener() {
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
animatorSet.playTogether(animators);
|
||||||
|
animatorSet.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationStart(Animator animation) {
|
public void onAnimationStart(Animator animation) {
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
delegate.onOpenAnimationStart();
|
delegate.onRevealAnimationStart(open);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
delegate.onOpenAnimationEnd();
|
delegate.onRevealAnimationEnd(open);
|
||||||
|
}
|
||||||
|
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
|
@Override
|
||||||
public void onAnimationCancel(Animator animation) {
|
public void onAnimationCancel(Animator animation) {
|
||||||
|
onAnimationEnd(animation);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAnimationRepeat(Animator animation) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
anim.start();
|
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 {
|
} else {
|
||||||
//startLayoutAnimation(true, true);
|
ViewProxy.setTranslationY(containerView, containerView.getMeasuredHeight());
|
||||||
ViewProxy.setTranslationY(linearLayout, linearLayout.getHeight());
|
|
||||||
backgroundDrawable.setAlpha(0);
|
backgroundDrawable.setAlpha(0);
|
||||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||||
animatorSetProxy.playTogether(
|
animatorSetProxy.playTogether(
|
||||||
ObjectAnimatorProxy.ofFloat(linearLayout, "translationY", 0),
|
ObjectAnimatorProxy.ofFloat(containerView, "translationY", 0),
|
||||||
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 51));
|
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 51));
|
||||||
animatorSetProxy.setDuration(200);
|
animatorSetProxy.setDuration(200);
|
||||||
animatorSetProxy.setStartDelay(20);
|
animatorSetProxy.setStartDelay(20);
|
||||||
|
@ -320,58 +541,14 @@ public class BottomSheet extends Dialog {
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
delegate.onOpenAnimationEnd();
|
delegate.onOpenAnimationEnd();
|
||||||
}
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 11) {
|
||||||
|
container.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
animatorSetProxy.start();
|
animatorSetProxy.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AndroidUtilities.runOnUIThread(new Runnable() {
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
if (animationProgress < 1) {
|
|
||||||
startLayoutAnimation(open, false);
|
|
||||||
} else {
|
|
||||||
if (open && delegate != null) {
|
|
||||||
delegate.onOpenAnimationEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDelegate(BottomSheetDelegate delegate) {
|
public void setDelegate(BottomSheetDelegate delegate) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
@ -382,7 +559,7 @@ public class BottomSheet extends Dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinearLayout getSheetContainer() {
|
public LinearLayout getSheetContainer() {
|
||||||
return linearLayout;
|
return containerView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTag() {
|
public int getTag() {
|
||||||
|
@ -397,13 +574,14 @@ public class BottomSheet extends Dialog {
|
||||||
cell.textView.setText(text);
|
cell.textView.setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismissWithButtonClick(final int item) {
|
public void dismissWithButtonClick(final int item) {
|
||||||
if (dismissed) {
|
if (dismissed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
dismissed = true;
|
||||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||||
animatorSetProxy.playTogether(
|
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)
|
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0)
|
||||||
);
|
);
|
||||||
animatorSetProxy.setDuration(180);
|
animatorSetProxy.setDuration(180);
|
||||||
|
@ -440,9 +618,12 @@ public class BottomSheet extends Dialog {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dismissed = true;
|
dismissed = true;
|
||||||
|
if (useRevealAnimation) {
|
||||||
|
startRevealAnimation(false);
|
||||||
|
} else {
|
||||||
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy();
|
||||||
animatorSetProxy.playTogether(
|
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)
|
ObjectAnimatorProxy.ofInt(backgroundDrawable, "alpha", 0)
|
||||||
);
|
);
|
||||||
animatorSetProxy.setDuration(180);
|
animatorSetProxy.setDuration(180);
|
||||||
|
@ -469,6 +650,7 @@ public class BottomSheet extends Dialog {
|
||||||
});
|
});
|
||||||
animatorSetProxy.start();
|
animatorSetProxy.start();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
|
||||||
|
@ -515,10 +697,10 @@ public class BottomSheet extends Dialog {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setRevealAnimation(int x, int y) {
|
public Builder setUseRevealAnimation() {
|
||||||
bottomSheet.revealX = x;
|
if (Build.VERSION.SDK_INT >= 18 && !AndroidUtilities.isTablet()) {
|
||||||
bottomSheet.revealY = y;
|
|
||||||
bottomSheet.useRevealAnimation = true;
|
bottomSheet.useRevealAnimation = true;
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,8 +714,13 @@ public class BottomSheet extends Dialog {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BottomSheet setOverrideTabletWidth(boolean value) {
|
public Builder setApplyTopPaddings(boolean value) {
|
||||||
bottomSheet.overrideTabletWidth = value;
|
bottomSheet.applyTopPaddings = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BottomSheet setUseFullWidth(boolean value) {
|
||||||
|
bottomSheet.fullWidth = value;
|
||||||
return bottomSheet;
|
return bottomSheet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -383,7 +383,6 @@ public class DrawerLayoutContainer extends FrameLayout {
|
||||||
@Override
|
@Override
|
||||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
inLayout = true;
|
inLayout = true;
|
||||||
final int width = r - l;
|
|
||||||
final int childCount = getChildCount();
|
final int childCount = getChildCount();
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
final View child = getChildAt(i);
|
final View child = getChildAt(i);
|
||||||
|
@ -416,8 +415,6 @@ public class DrawerLayoutContainer extends FrameLayout {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
|
||||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
|
||||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,6 @@ public class CountrySearchAdapter extends BaseFragmentAdapter {
|
||||||
updateSearchResults(new ArrayList<Country>());
|
updateSearchResults(new ArrayList<Country>());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
long time = System.currentTimeMillis();
|
|
||||||
ArrayList<Country> resultArray = new ArrayList<>();
|
ArrayList<Country> resultArray = new ArrayList<>();
|
||||||
|
|
||||||
String n = query.substring(0, 1);
|
String n = query.substring(0, 1);
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.telegram.ui.Cells.PhotoAttachPhotoCell;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class PhotoAttachAdapter extends RecyclerView.Adapter implements NotificationCenter.NotificationCenterDelegate {
|
public class PhotoAttachAdapter extends RecyclerView.Adapter {
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private PhotoAttachAdapterDelegate delegate;
|
private PhotoAttachAdapterDelegate delegate;
|
||||||
|
@ -38,14 +38,6 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter implements Notifica
|
||||||
|
|
||||||
public PhotoAttachAdapter(Context context) {
|
public PhotoAttachAdapter(Context context) {
|
||||||
mContext = 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() {
|
public void clearSelectedPhotos() {
|
||||||
|
@ -64,13 +56,6 @@ public class PhotoAttachAdapter extends RecyclerView.Adapter implements Notifica
|
||||||
delegate = photoAttachAdapterDelegate;
|
delegate = photoAttachAdapterDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void didReceivedNotification(int id, Object... args) {
|
|
||||||
if (id == NotificationCenter.albumsDidLoaded) {
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||||
//if (position != 0) {
|
//if (position != 0) {
|
||||||
|
|
|
@ -87,7 +87,7 @@ public class StickersAdapter extends RecyclerView.Adapter implements Notificatio
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadStikersForEmoji(CharSequence emoji) {
|
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) {
|
if (search) {
|
||||||
lastSticker = emoji.toString();
|
lastSticker = emoji.toString();
|
||||||
HashMap<String, ArrayList<TLRPC.Document>> allStickers = StickersQuery.getAllStickers();
|
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.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -65,7 +64,7 @@ public class BlockedUsersActivity extends BaseFragment implements NotificationCe
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||||
actionBar.setAllowOverlayTitle(true);
|
actionBar.setAllowOverlayTitle(true);
|
||||||
actionBar.setTitle(LocaleController.getString("BlockedUsers", R.string.BlockedUsers));
|
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 height;
|
||||||
private int textX;
|
private int textX;
|
||||||
private int textY;
|
private int textY;
|
||||||
|
private int textXOffset;
|
||||||
|
|
||||||
private ClickableSpan pressedLink;
|
private ClickableSpan pressedLink;
|
||||||
private LinkPath urlPath = new LinkPath();
|
private LinkPath urlPath = new LinkPath();
|
||||||
|
@ -101,7 +102,9 @@ public class BotHelpCell extends View {
|
||||||
width = 0;
|
width = 0;
|
||||||
height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18);
|
height = textLayout.getHeight() + AndroidUtilities.dp(4 + 18);
|
||||||
int count = textLayout.getLineCount();
|
int count = textLayout.getLineCount();
|
||||||
|
textXOffset = Integer.MAX_VALUE;
|
||||||
for (int a = 0; a < count; a++) {
|
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 = (int) Math.ceil(Math.max(width, textLayout.getLineWidth(a) - textLayout.getLineLeft(a)));
|
||||||
}
|
}
|
||||||
width += AndroidUtilities.dp(4 + 18);
|
width += AndroidUtilities.dp(4 + 18);
|
||||||
|
@ -113,7 +116,6 @@ public class BotHelpCell extends View {
|
||||||
float y = event.getY();
|
float y = event.getY();
|
||||||
|
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
int side = AndroidUtilities.dp(48);
|
|
||||||
if (textLayout != null) {
|
if (textLayout != null) {
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
|
if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
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.setBounds(x, y, width + x, height + y);
|
||||||
ResourceLoader.backgroundMediaDrawableIn.draw(canvas);
|
ResourceLoader.backgroundMediaDrawableIn.draw(canvas);
|
||||||
canvas.save();
|
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) {
|
if (pressedLink != null) {
|
||||||
canvas.drawPath(urlPath, urlPaint);
|
canvas.drawPath(urlPath, urlPaint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,8 +174,8 @@ public class ChatActionCell extends BaseCell {
|
||||||
final int line = textLayout.getLineForVertical((int)y);
|
final int line = textLayout.getLineForVertical((int)y);
|
||||||
final int off = textLayout.getOffsetForHorizontal(line, x);
|
final int off = textLayout.getOffsetForHorizontal(line, x);
|
||||||
final float left = textLayout.getLineLeft(line);
|
final float left = textLayout.getLineLeft(line);
|
||||||
if (left <= x && left + textLayout.getLineWidth(line) >= x) {
|
if (left <= x && left + textLayout.getLineWidth(line) >= x && currentMessageObject.messageText instanceof Spannable) {
|
||||||
Spannable buffer = (Spannable)currentMessageObject.messageText;
|
Spannable buffer = (Spannable) currentMessageObject.messageText;
|
||||||
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
|
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
|
||||||
|
|
||||||
if (link.length != 0) {
|
if (link.length != 0) {
|
||||||
|
@ -226,7 +226,6 @@ public class ChatActionCell extends BaseCell {
|
||||||
int linesCount = textLayout.getLineCount();
|
int linesCount = textLayout.getLineCount();
|
||||||
for (int a = 0; a < linesCount; a++) {
|
for (int a = 0; a < linesCount; a++) {
|
||||||
float lineWidth;
|
float lineWidth;
|
||||||
float lineLeft = 0;
|
|
||||||
try {
|
try {
|
||||||
lineWidth = textLayout.getLineWidth(a);
|
lineWidth = textLayout.getLineWidth(a);
|
||||||
textHeight = (int)Math.max(textHeight, Math.ceil(textLayout.getLineBottom(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.AndroidUtilities;
|
||||||
import org.telegram.android.ImageLoader;
|
import org.telegram.android.ImageLoader;
|
||||||
import org.telegram.android.MessagesController;
|
import org.telegram.android.MessagesController;
|
||||||
|
import org.telegram.android.SendMessagesHelper;
|
||||||
import org.telegram.messenger.FileLoader;
|
import org.telegram.messenger.FileLoader;
|
||||||
import org.telegram.android.MediaController;
|
import org.telegram.android.MediaController;
|
||||||
import org.telegram.android.MessageObject;
|
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.ResourceLoader;
|
||||||
import org.telegram.ui.Components.SeekBar;
|
import org.telegram.ui.Components.SeekBar;
|
||||||
|
|
||||||
|
@ -36,10 +37,10 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
private static Paint circlePaint;
|
private static Paint circlePaint;
|
||||||
|
|
||||||
private SeekBar seekBar;
|
private SeekBar seekBar;
|
||||||
private ProgressView progressView;
|
|
||||||
private int seekBarX;
|
private int seekBarX;
|
||||||
private int seekBarY;
|
private int seekBarY;
|
||||||
|
|
||||||
|
private RadialProgress radialProgress;
|
||||||
private int buttonState = 0;
|
private int buttonState = 0;
|
||||||
private int buttonX;
|
private int buttonX;
|
||||||
private int buttonY;
|
private int buttonY;
|
||||||
|
@ -58,7 +59,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
|
|
||||||
seekBar = new SeekBar(context);
|
seekBar = new SeekBar(context);
|
||||||
seekBar.delegate = this;
|
seekBar.delegate = this;
|
||||||
progressView = new ProgressView();
|
radialProgress = new RadialProgress(this);
|
||||||
drawForwardedName = true;
|
drawForwardedName = true;
|
||||||
|
|
||||||
if (timePaint == null) {
|
if (timePaint == null) {
|
||||||
|
@ -78,7 +79,7 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow() {
|
protected void onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
updateButtonState();
|
updateButtonState(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -131,21 +132,25 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
}
|
}
|
||||||
if (result) {
|
if (result) {
|
||||||
buttonState = 1;
|
buttonState = 1;
|
||||||
|
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
} else if (buttonState == 1) {
|
} else if (buttonState == 1) {
|
||||||
boolean result = MediaController.getInstance().pauseAudio(currentMessageObject);
|
boolean result = MediaController.getInstance().pauseAudio(currentMessageObject);
|
||||||
if (result) {
|
if (result) {
|
||||||
buttonState = 0;
|
buttonState = 0;
|
||||||
|
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
} else if (buttonState == 2) {
|
} else if (buttonState == 2) {
|
||||||
FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.audio, true);
|
FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.audio, true);
|
||||||
buttonState = 3;
|
buttonState = 3;
|
||||||
|
radialProgress.setBackground(getDrawableForCurrentState(), true, false);
|
||||||
invalidate();
|
invalidate();
|
||||||
} else if (buttonState == 3) {
|
} else if (buttonState == 3) {
|
||||||
FileLoader.getInstance().cancelLoadFile(currentMessageObject.messageOwner.media.audio);
|
FileLoader.getInstance().cancelLoadFile(currentMessageObject.messageOwner.media.audio);
|
||||||
buttonState = 2;
|
buttonState = 2;
|
||||||
|
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||||
invalidate();
|
invalidate();
|
||||||
} else if (buttonState == 4) {
|
} else if (buttonState == 4) {
|
||||||
if (currentMessageObject.isOut() && currentMessageObject.isSending()) {
|
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);
|
String timeString = String.format("%02d:%02d", duration / 60, duration % 60);
|
||||||
if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) {
|
if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) {
|
||||||
|
lastTimeString = timeString;
|
||||||
timeWidth = (int)Math.ceil(timePaint.measureText(timeString));
|
timeWidth = (int)Math.ceil(timePaint.measureText(timeString));
|
||||||
timeLayout = new StaticLayout(timeString, timePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
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) {
|
if (buttonState == 2) {
|
||||||
FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.audio, true);
|
FileLoader.getInstance().loadFile(currentMessageObject.messageOwner.media.audio, true);
|
||||||
buttonState = 3;
|
buttonState = 3;
|
||||||
invalidate();
|
radialProgress.setBackground(getDrawableForCurrentState(), false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateButtonState() {
|
public void updateButtonState(boolean animated) {
|
||||||
if (currentMessageObject == null) {
|
if (currentMessageObject == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentMessageObject.isOut() && currentMessageObject.isSending()) {
|
if (currentMessageObject.isOut() && currentMessageObject.isSending()) {
|
||||||
|
MediaController.getInstance().addLoadingFileObserver(currentMessageObject.messageOwner.attachPath, this);
|
||||||
buttonState = 4;
|
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 {
|
} else {
|
||||||
File cacheFile = null;
|
File cacheFile = null;
|
||||||
if (currentMessageObject.messageOwner.attachPath != null && currentMessageObject.messageOwner.attachPath.length() > 0) {
|
if (currentMessageObject.messageOwner.attachPath != null && currentMessageObject.messageOwner.attachPath.length() > 0) {
|
||||||
|
@ -212,21 +225,24 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
} else {
|
} else {
|
||||||
buttonState = 1;
|
buttonState = 1;
|
||||||
}
|
}
|
||||||
progressView.setProgress(0);
|
radialProgress.setProgress(0, animated);
|
||||||
|
radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
|
||||||
} else {
|
} else {
|
||||||
String fileName = currentMessageObject.getFileName();
|
String fileName = currentMessageObject.getFileName();
|
||||||
MediaController.getInstance().addLoadingFileObserver(fileName, this);
|
MediaController.getInstance().addLoadingFileObserver(fileName, this);
|
||||||
if (!FileLoader.getInstance().isLoadingFile(fileName)) {
|
if (!FileLoader.getInstance().isLoadingFile(fileName)) {
|
||||||
buttonState = 2;
|
buttonState = 2;
|
||||||
progressView.setProgress(0);
|
radialProgress.setProgress(0, animated);
|
||||||
|
radialProgress.setBackground(getDrawableForCurrentState(), false, animated);
|
||||||
} else {
|
} else {
|
||||||
buttonState = 3;
|
buttonState = 3;
|
||||||
Float progress = ImageLoader.getInstance().getFileProgress(fileName);
|
Float progress = ImageLoader.getInstance().getFileProgress(fileName);
|
||||||
if (progress != null) {
|
if (progress != null) {
|
||||||
progressView.setProgress(progress);
|
radialProgress.setProgress(progress, animated);
|
||||||
} else {
|
} 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
|
@Override
|
||||||
public void onFailedDownload(String fileName) {
|
public void onFailedDownload(String fileName) {
|
||||||
updateButtonState();
|
updateButtonState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccessDownload(String fileName) {
|
public void onSuccessDownload(String fileName) {
|
||||||
updateButtonState();
|
updateButtonState(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProgressDownload(String fileName, float progress) {
|
public void onProgressDownload(String fileName, float progress) {
|
||||||
progressView.setProgress(progress);
|
radialProgress.setProgress(progress, true);
|
||||||
if (buttonState != 3) {
|
if (buttonState != 3) {
|
||||||
updateButtonState();
|
updateButtonState(false);
|
||||||
}
|
}
|
||||||
invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
|
public void onProgressUpload(String fileName, float progress, boolean isEncrypted) {
|
||||||
|
radialProgress.setProgress(progress, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -299,17 +314,17 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
|
|
||||||
seekBar.width = backgroundWidth - AndroidUtilities.dp(70);
|
seekBar.width = backgroundWidth - AndroidUtilities.dp(70);
|
||||||
seekBar.height = AndroidUtilities.dp(30);
|
seekBar.height = AndroidUtilities.dp(30);
|
||||||
progressView.width = backgroundWidth - AndroidUtilities.dp(94);
|
|
||||||
progressView.height = AndroidUtilities.dp(30);
|
|
||||||
seekBarY = AndroidUtilities.dp(11) + namesOffset;
|
seekBarY = AndroidUtilities.dp(11) + namesOffset;
|
||||||
buttonY = AndroidUtilities.dp(13) + namesOffset;
|
buttonY = AndroidUtilities.dp(13) + namesOffset;
|
||||||
|
radialProgress.setProgressRect(buttonX, buttonY, buttonX + AndroidUtilities.dp(40), buttonY + AndroidUtilities.dp(40));
|
||||||
|
|
||||||
updateProgress();
|
updateProgress();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMessageObject(MessageObject messageObject) {
|
public void setMessageObject(MessageObject messageObject) {
|
||||||
if (currentMessageObject != messageObject || isUserDataChanged()) {
|
boolean dataChanged = currentMessageObject == messageObject && isUserDataChanged();
|
||||||
|
if (currentMessageObject != messageObject || dataChanged) {
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(isChat ? 102 : 50), AndroidUtilities.dp(300));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(isChat ? 102 : 50), AndroidUtilities.dp(300));
|
||||||
} else {
|
} else {
|
||||||
|
@ -318,15 +333,20 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
|
|
||||||
if (messageObject.isOut()) {
|
if (messageObject.isOut()) {
|
||||||
seekBar.type = 0;
|
seekBar.type = 0;
|
||||||
progressView.setProgressColors(0xffb4e396, 0xff6ac453);
|
radialProgress.setProgressColor(0xff87bf78);
|
||||||
} else {
|
} else {
|
||||||
seekBar.type = 1;
|
seekBar.type = 1;
|
||||||
progressView.setProgressColors(0xffd9e2eb, 0xff86c5f8);
|
radialProgress.setProgressColor(0xffa2b5c7);
|
||||||
}
|
}
|
||||||
|
|
||||||
super.setMessageObject(messageObject);
|
super.setMessageObject(messageObject);
|
||||||
}
|
}
|
||||||
updateButtonState();
|
updateButtonState(dataChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Drawable getDrawableForCurrentState() {
|
||||||
|
return ResourceLoader.audioStatesDrawable[currentMessageObject.isOut() ? buttonState : buttonState + 5][0];
|
||||||
|
//buttonPressed ? 1 :
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -338,27 +358,18 @@ public class ChatAudioCell extends ChatBaseCell implements SeekBar.SeekBarDelega
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.save();
|
canvas.save();
|
||||||
if (buttonState == 0 || buttonState == 1) {
|
|
||||||
canvas.translate(seekBarX, seekBarY);
|
canvas.translate(seekBarX, seekBarY);
|
||||||
seekBar.draw(canvas);
|
seekBar.draw(canvas);
|
||||||
} else {
|
|
||||||
canvas.translate(seekBarX + AndroidUtilities.dp(12), seekBarY);
|
|
||||||
progressView.draw(canvas);
|
|
||||||
}
|
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
|
|
||||||
int state = buttonState;
|
|
||||||
if (currentMessageObject.isOut()) {
|
if (currentMessageObject.isOut()) {
|
||||||
timePaint.setColor(0xff70b15c);
|
timePaint.setColor(0xff70b15c);
|
||||||
circlePaint.setColor(0xff87bf78);
|
circlePaint.setColor(0xff87bf78);
|
||||||
} else {
|
} else {
|
||||||
state += 5;
|
|
||||||
timePaint.setColor(0xffa1aab3);
|
timePaint.setColor(0xffa1aab3);
|
||||||
circlePaint.setColor(0xff4195e5);
|
circlePaint.setColor(0xff4195e5);
|
||||||
}
|
}
|
||||||
Drawable buttonDrawable = ResourceLoader.audioStatesDrawable[state][buttonPressed ? 1 : 0];
|
radialProgress.onDraw(canvas);
|
||||||
setDrawableBounds(buttonDrawable, buttonX, buttonY);
|
|
||||||
buttonDrawable.draw(canvas);
|
|
||||||
|
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset);
|
canvas.translate(timeX, AndroidUtilities.dp(42) + namesOffset);
|
||||||
|
|
|
@ -445,7 +445,7 @@ public class ChatBaseCell extends BaseCell {
|
||||||
mess = mess.substring(0, 150);
|
mess = mess.substring(0, 150);
|
||||||
}
|
}
|
||||||
mess = mess.replace("\n", " ");
|
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);
|
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();
|
float y = event.getY();
|
||||||
|
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
int side = AndroidUtilities.dp(36);
|
|
||||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
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()) {
|
if (x >= avatarImage.getImageX() && x <= avatarImage.getImageX() + namesWidth + AndroidUtilities.dp(42) && y >= avatarImage.getImageY() && y <= avatarImage.getImageY() + avatarImage.getImageHeight()) {
|
||||||
avatarPressed = true;
|
avatarPressed = true;
|
||||||
|
|
|
@ -379,7 +379,6 @@ public class ChatMediaCell extends ChatBaseCell implements MediaController.FileD
|
||||||
|
|
||||||
private Drawable getDrawableForCurrentState() {
|
private Drawable getDrawableForCurrentState() {
|
||||||
if (buttonState >= 0 && buttonState < 4) {
|
if (buttonState >= 0 && buttonState < 4) {
|
||||||
Drawable currentButtonDrawable = null;
|
|
||||||
if (currentMessageObject.type == 9 && gifDrawable == null) {
|
if (currentMessageObject.type == 9 && gifDrawable == null) {
|
||||||
if (buttonState == 1 && !currentMessageObject.isSending()) {
|
if (buttonState == 1 && !currentMessageObject.isSending()) {
|
||||||
return ResourceLoader.buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0];
|
return ResourceLoader.buttonStatesDrawablesDoc[2][currentMessageObject.isOut() ? 1 : 0];
|
||||||
|
|
|
@ -193,7 +193,7 @@ public class ChatMessageCell extends ChatBaseCell {
|
||||||
pressedLink.onClick(this);
|
pressedLink.onClick(this);
|
||||||
} else {
|
} else {
|
||||||
TLRPC.WebPage webPage = currentMessageObject.messageOwner.media.webpage;
|
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);
|
delegate.needOpenWebView(webPage.embed_url, webPage.site_name, webPage.url, webPage.embed_width, webPage.embed_height);
|
||||||
} else {
|
} else {
|
||||||
Uri uri = Uri.parse(webPage.url);
|
Uri uri = Uri.parse(webPage.url);
|
||||||
|
@ -536,7 +536,7 @@ public class ChatMessageCell extends ChatBaseCell {
|
||||||
|
|
||||||
if (webPage.photo != null) {
|
if (webPage.photo != null) {
|
||||||
boolean smallImage = webPage.type != null && (webPage.type.equals("app") || webPage.type.equals("profile") || webPage.type.equals("article"));
|
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;
|
smallImage = false;
|
||||||
isSmallImage = 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()) {
|
if (message.isOut()) {
|
||||||
name = LocaleController.getString("FromYou", R.string.FromYou);
|
name = LocaleController.getString("FromYou", R.string.FromYou);
|
||||||
} else {
|
} else {
|
||||||
if (UserObject.isDeleted(fromUser)) {
|
name = UserObject.getFirstName(fromUser);
|
||||||
name = "Deleted";
|
|
||||||
} else {
|
|
||||||
if (fromUser.first_name != null && fromUser.first_name.length() > 0) {
|
|
||||||
name = fromUser.first_name;
|
|
||||||
} else {
|
|
||||||
name = fromUser.last_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
checkMessage = false;
|
checkMessage = false;
|
||||||
if (message.caption != null) {
|
if (message.caption != null) {
|
||||||
|
@ -373,11 +365,11 @@ public class DialogCell extends BaseCell {
|
||||||
mess = mess.substring(0, 150);
|
mess = mess.substring(0, 150);
|
||||||
}
|
}
|
||||||
mess = mess.replace("\n", " ");
|
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 {
|
} else {
|
||||||
if (message.messageOwner.media != null && !message.isMediaEmpty()) {
|
if (message.messageOwner.media != null && !message.isMediaEmpty()) {
|
||||||
currentMessagePaint = messagePrintingPaint;
|
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 {
|
} else {
|
||||||
if (message.messageOwner.message != null) {
|
if (message.messageOwner.message != null) {
|
||||||
String mess = message.messageOwner.message;
|
String mess = message.messageOwner.message;
|
||||||
|
@ -385,7 +377,7 @@ public class DialogCell extends BaseCell {
|
||||||
mess = mess.substring(0, 150);
|
mess = mess.substring(0, 150);
|
||||||
}
|
}
|
||||||
mess = mess.replace("\n", " ");
|
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.substring(0, 150);
|
||||||
}
|
}
|
||||||
mess = mess.replace("\n", " ");
|
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);
|
messageWidth = Math.max(AndroidUtilities.dp(12), messageWidth);
|
||||||
CharSequence messageStringFinal = TextUtils.ellipsize(messageString, currentMessagePaint, messageWidth - AndroidUtilities.dp(12), TextUtils.TruncateAt.END);
|
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 android.widget.ImageView;
|
||||||
|
|
||||||
import org.telegram.android.AndroidUtilities;
|
import org.telegram.android.AndroidUtilities;
|
||||||
import org.telegram.messenger.R;
|
|
||||||
import org.telegram.ui.Components.LayoutHelper;
|
import org.telegram.ui.Components.LayoutHelper;
|
||||||
|
|
||||||
public class PhotoAttachCameraCell extends FrameLayout {
|
public class PhotoAttachCameraCell extends FrameLayout {
|
||||||
|
@ -23,7 +22,7 @@ public class PhotoAttachCameraCell extends FrameLayout {
|
||||||
|
|
||||||
ImageView imageView = new ImageView(context);
|
ImageView imageView = new ImageView(context);
|
||||||
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||||
imageView.setImageResource(R.drawable.ic_attach_photobig);
|
//imageView.setImageResource(R.drawable.ic_attach_photobig);
|
||||||
imageView.setBackgroundColor(0xff777777);
|
imageView.setBackgroundColor(0xff777777);
|
||||||
addView(imageView, LayoutHelper.createFrame(80, 80));
|
addView(imageView, LayoutHelper.createFrame(80, 80));
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,5 +81,6 @@ public class PhotoAttachPhotoCell extends FrameLayout {
|
||||||
|
|
||||||
public void setOnCheckClickLisnener(OnClickListener onCheckClickLisnener) {
|
public void setOnCheckClickLisnener(OnClickListener onCheckClickLisnener) {
|
||||||
checkFrame.setOnClickListener(onCheckClickLisnener);
|
checkFrame.setOnClickListener(onCheckClickLisnener);
|
||||||
|
imageView.setOnClickListener(onCheckClickLisnener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F
|
||||||
});
|
});
|
||||||
|
|
||||||
nameTextView = new TextView(context);
|
nameTextView = new TextView(context);
|
||||||
nameTextView.setTextColor(0xff222222);
|
nameTextView.setTextColor(0xff212121);
|
||||||
nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||||
nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||||
nameTextView.setLines(1);
|
nameTextView.setLines(1);
|
||||||
|
@ -318,6 +318,9 @@ public class SharedDocumentCell extends FrameLayout implements MediaController.F
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProgressDownload(String fileName, float progress) {
|
public void onProgressDownload(String fileName, float progress) {
|
||||||
|
if (progressView.getVisibility() != VISIBLE) {
|
||||||
|
updateFileExistIcon();
|
||||||
|
}
|
||||||
progressView.setProgress(progress, true);
|
progressView.setProgress(progress, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class SharedMediaSectionCell extends FrameLayout {
|
||||||
textView = new TextView(getContext());
|
textView = new TextView(getContext());
|
||||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||||
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||||
textView.setTextColor(0xff222222);
|
textView.setTextColor(0xff212121);
|
||||||
textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
|
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));
|
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) {
|
for (TLRPC.DocumentAttribute attribute : document.attributes) {
|
||||||
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
|
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
|
||||||
if (attribute.alt != null && attribute.alt.length() > 0) {
|
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;
|
set = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!set) {
|
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);
|
emojiTextView.setVisibility(VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class TextDetailCell extends FrameLayout {
|
||||||
textView.setMaxLines(1);
|
textView.setMaxLines(1);
|
||||||
textView.setSingleLine(true);
|
textView.setSingleLine(true);
|
||||||
textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
|
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 = new TextView(context);
|
||||||
valueTextView.setTextColor(0xff8a8a8a);
|
valueTextView.setTextColor(0xff8a8a8a);
|
||||||
|
@ -45,7 +45,7 @@ public class TextDetailCell extends FrameLayout {
|
||||||
valueTextView.setMaxLines(1);
|
valueTextView.setMaxLines(1);
|
||||||
valueTextView.setSingleLine(true);
|
valueTextView.setSingleLine(true);
|
||||||
valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
|
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 = new ImageView(context);
|
||||||
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||||
|
|
|
@ -16,7 +16,6 @@ import android.text.InputType;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -57,7 +56,7 @@ public class ChangeChatNameActivity extends BaseFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||||
actionBar.setAllowOverlayTitle(true);
|
actionBar.setAllowOverlayTitle(true);
|
||||||
actionBar.setTitle(LocaleController.getString("EditName", R.string.EditName));
|
actionBar.setTitle(LocaleController.getString("EditName", R.string.EditName));
|
||||||
|
|
|
@ -15,7 +15,6 @@ import android.text.InputType;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -50,7 +49,7 @@ public class ChangeNameActivity extends BaseFragment {
|
||||||
private final static int done_button = 1;
|
private final static int done_button = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||||
actionBar.setAllowOverlayTitle(true);
|
actionBar.setAllowOverlayTitle(true);
|
||||||
actionBar.setTitle(LocaleController.getString("EditName", R.string.EditName));
|
actionBar.setTitle(LocaleController.getString("EditName", R.string.EditName));
|
||||||
|
|
|
@ -24,7 +24,6 @@ import android.text.TextWatcher;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
|
@ -102,7 +101,7 @@ public class ChangePhoneActivity extends BaseFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
actionBar.setTitle(LocaleController.getString("AppName", R.string.AppName));
|
actionBar.setTitle(LocaleController.getString("AppName", R.string.AppName));
|
||||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||||
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
|
actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
@ -36,7 +35,7 @@ import org.telegram.ui.Components.LayoutHelper;
|
||||||
public class ChangePhoneHelpActivity extends BaseFragment {
|
public class ChangePhoneHelpActivity extends BaseFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||||
actionBar.setAllowOverlayTitle(true);
|
actionBar.setAllowOverlayTitle(true);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import android.text.TextWatcher;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -62,7 +61,7 @@ public class ChangeUsernameActivity extends BaseFragment {
|
||||||
private final static int done_button = 1;
|
private final static int done_button = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
actionBar.setBackButtonImage(R.drawable.ic_ab_back);
|
||||||
actionBar.setAllowOverlayTitle(true);
|
actionBar.setAllowOverlayTitle(true);
|
||||||
actionBar.setTitle(LocaleController.getString("Username", R.string.Username));
|
actionBar.setTitle(LocaleController.getString("Username", R.string.Username));
|
||||||
|
|
|
@ -14,7 +14,6 @@ import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
@ -32,7 +31,6 @@ import android.util.Base64;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -40,6 +38,7 @@ import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.LinearLayout;
|
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.ActionBarMenu;
|
||||||
import org.telegram.ui.ActionBar.ActionBarMenuItem;
|
import org.telegram.ui.ActionBar.ActionBarMenuItem;
|
||||||
import org.telegram.ui.Cells.ChatMessageCell;
|
import org.telegram.ui.Cells.ChatMessageCell;
|
||||||
|
import org.telegram.ui.Cells.ChatMusicCell;
|
||||||
import org.telegram.ui.Cells.ChatUnreadCell;
|
import org.telegram.ui.Cells.ChatUnreadCell;
|
||||||
import org.telegram.ui.Components.AlertsCreator;
|
import org.telegram.ui.Components.AlertsCreator;
|
||||||
import org.telegram.ui.Components.AvatarDrawable;
|
import org.telegram.ui.Components.AvatarDrawable;
|
||||||
|
@ -123,7 +123,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.concurrent.Semaphore;
|
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 {
|
PhotoViewer.PhotoViewerProvider {
|
||||||
|
|
||||||
protected TLRPC.Chat currentChat;
|
protected TLRPC.Chat currentChat;
|
||||||
|
@ -177,6 +177,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
private ListView mentionListView;
|
private ListView mentionListView;
|
||||||
private AnimatorSetProxy mentionListAnimation;
|
private AnimatorSetProxy mentionListAnimation;
|
||||||
private ChatAttachView chatAttachView;
|
private ChatAttachView chatAttachView;
|
||||||
|
private BottomSheet chatAttachViewSheet;
|
||||||
|
|
||||||
private boolean allowStickersPanel;
|
private boolean allowStickersPanel;
|
||||||
private AnimatorSetProxy runningAnimation;
|
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.removeAllMessagesFromDialog);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioProgressDidChanged);
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioProgressDidChanged);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset);
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioDidReset);
|
||||||
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.screenshotTook);
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.screenshotTook);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.blockedUsersDidLoaded);
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.blockedUsersDidLoaded);
|
||||||
NotificationCenter.getInstance().addObserver(this, NotificationCenter.FileNewChunkAvailable);
|
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.botInfoDidLoaded);
|
||||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.botKeyboardDidLoaded);
|
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.botKeyboardDidLoaded);
|
||||||
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatSearchResultsAvailable);
|
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.chatSearchResultsAvailable);
|
||||||
|
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.audioPlayStateChanged);
|
||||||
|
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
NotificationCenter.getInstance().postNotificationName(NotificationCenter.openedChatChanged, dialog_id, true);
|
NotificationCenter.getInstance().postNotificationName(NotificationCenter.openedChatChanged, dialog_id, true);
|
||||||
|
@ -578,11 +581,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
chatAttachView.onDestroy();
|
chatAttachView.onDestroy();
|
||||||
}
|
}
|
||||||
AndroidUtilities.unlockOrientation(getParentActivity());
|
AndroidUtilities.unlockOrientation(getParentActivity());
|
||||||
|
MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject();
|
||||||
|
if (messageObject != null && !messageObject.isMusic()) {
|
||||||
MediaController.getInstance().stopAudio();
|
MediaController.getInstance().stopAudio();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View createView(Context context, LayoutInflater inflater) {
|
public View createView(Context context) {
|
||||||
|
|
||||||
for (int a = 0; a < 8; a++) {
|
for (int a = 0; a < 8; a++) {
|
||||||
chatMessageCellsCache.add(new ChatMessageCell(context));
|
chatMessageCellsCache.add(new ChatMessageCell(context));
|
||||||
|
@ -595,6 +601,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
lastStatus = null;
|
lastStatus = null;
|
||||||
hasOwnBackground = true;
|
hasOwnBackground = true;
|
||||||
chatAttachView = null;
|
chatAttachView = null;
|
||||||
|
chatAttachViewSheet = null;
|
||||||
|
|
||||||
ResourceLoader.loadRecources(context);
|
ResourceLoader.loadRecources(context);
|
||||||
|
|
||||||
|
@ -673,7 +680,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putBoolean("onlySelect", true);
|
args.putBoolean("onlySelect", true);
|
||||||
args.putInt("dialogsType", 1);
|
args.putInt("dialogsType", 1);
|
||||||
MessagesActivity fragment = new MessagesActivity(args);
|
DialogsActivity fragment = new DialogsActivity(args);
|
||||||
fragment.setDelegate(ChatActivity.this);
|
fragment.setDelegate(ChatActivity.this);
|
||||||
presentFragment(fragment);
|
presentFragment(fragment);
|
||||||
} else if (id == chat_enc_timer) {
|
} else if (id == chat_enc_timer) {
|
||||||
|
@ -770,20 +777,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
selectedMessagesCanCopyIds.clear();
|
selectedMessagesCanCopyIds.clear();
|
||||||
actionBar.hideActionMode();
|
actionBar.hideActionMode();
|
||||||
updateVisibleRows();
|
updateVisibleRows();
|
||||||
}/* else if (id == chat_menu_attach) {
|
} else if (id == chat_menu_attach) {
|
||||||
if (getParentActivity() == null) {
|
if (getParentActivity() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity());
|
|
||||||
if (chatAttachView == null) {
|
if (chatAttachView == null) {
|
||||||
|
BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity());
|
||||||
chatAttachView = new ChatAttachView(getParentActivity());
|
chatAttachView = new ChatAttachView(getParentActivity());
|
||||||
chatAttachView.setDelegate(new ChatAttachView.ChatAttachViewDelegate() {
|
chatAttachView.setDelegate(new ChatAttachView.ChatAttachViewDelegate() {
|
||||||
@Override
|
@Override
|
||||||
public void didPressedButton(int button) {
|
public void didPressedButton(int button) {
|
||||||
if (visibleDialog != null) {
|
|
||||||
visibleDialog.dismiss();
|
|
||||||
}
|
|
||||||
if (button == 7) {
|
if (button == 7) {
|
||||||
|
chatAttachViewSheet.dismiss();
|
||||||
HashMap<Integer, MediaController.PhotoEntry> selectedPhotos = chatAttachView.getSelectedPhotos();
|
HashMap<Integer, MediaController.PhotoEntry> selectedPhotos = chatAttachView.getSelectedPhotos();
|
||||||
if (!selectedPhotos.isEmpty()) {
|
if (!selectedPhotos.isEmpty()) {
|
||||||
ArrayList<String> photos = new ArrayList<>();
|
ArrayList<String> photos = new ArrayList<>();
|
||||||
|
@ -796,31 +802,50 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
showReplyPanel(false, null, null, null, false, true);
|
showReplyPanel(false, null, null, null, false, true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
chatAttachViewSheet.dismissWithButtonClick(button);
|
||||||
}
|
}
|
||||||
processSelectedAttach(button);
|
processSelectedAttach(button);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
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() {
|
builder.setDelegate(new BottomSheet.BottomSheetDelegate() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOpenAnimationStart() {
|
public void onRevealAnimationStart(boolean open) {
|
||||||
chatAttachView.startAnimations(coords[1] > AndroidUtilities.displaySize.y - AndroidUtilities.dp(100));
|
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
|
@Override
|
||||||
public void onOpenAnimationEnd() {
|
public void onOpenAnimationEnd() {
|
||||||
|
chatAttachView.onRevealAnimationEnd(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getRevealView() {
|
||||||
|
return menuItem;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
builder.setApplyTopPaddings(false);
|
||||||
|
builder.setUseRevealAnimation();
|
||||||
|
builder.setCustomView(chatAttachView);
|
||||||
|
chatAttachViewSheet = builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
chatAttachView.init(ChatActivity.this);
|
chatAttachView.init(ChatActivity.this);
|
||||||
showDialog(builder.create());
|
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) {
|
}/* 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);
|
processSelectedAttach(id);
|
||||||
} else if (id == bot_help) {
|
} */else if (id == bot_help) {
|
||||||
SendMessagesHelper.getInstance().sendMessage("/help", dialog_id, null, null, false);
|
SendMessagesHelper.getInstance().sendMessage("/help", dialog_id, null, null, false);
|
||||||
} else if (id == bot_settings) {
|
} else if (id == bot_settings) {
|
||||||
SendMessagesHelper.getInstance().sendMessage("/settings", dialog_id, null, null, false);
|
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();
|
ActionBarMenu menu = actionBar.createMenu();
|
||||||
|
|
||||||
if (currentEncryptedChat == null && !isBroadcast) {
|
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
|
@Override
|
||||||
public void onSearchCollapse() {
|
public void onSearchCollapse() {
|
||||||
avatarContainer.setVisibility(View.VISIBLE);
|
avatarContainer.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);
|
headerItem.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
if (attachItem != null) {
|
||||||
|
attachItem.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
searchItem.setVisibility(View.GONE);
|
searchItem.setVisibility(View.GONE);
|
||||||
//chatActivityEnterView.setVisibility(View.VISIBLE);
|
//chatActivityEnterView.setVisibility(View.VISIBLE);
|
||||||
searchUpItem.clearAnimation();
|
searchUpItem.clearAnimation();
|
||||||
|
@ -945,7 +984,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
searchItem.getSearchField().requestFocus();
|
searchItem.getSearchField().requestFocus();
|
||||||
AndroidUtilities.showKeyboard(searchItem.getSearchField());
|
AndroidUtilities.showKeyboard(searchItem.getSearchField());
|
||||||
}
|
}
|
||||||
}, 200); //TODO find a better way to open keyboard
|
}, 300); //TODO find a better way to open keyboard
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -960,7 +999,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
searchUpItem = menu.addItem(search_up, R.drawable.search_up);
|
searchUpItem = menu.addItem(search_up, R.drawable.search_up);
|
||||||
searchUpItem.setVisibility(View.GONE);
|
searchUpItem.setVisibility(View.GONE);
|
||||||
searchDownItem = menu.addItem(search_down, R.drawable.search_down);
|
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);
|
headerItem = menu.addItem(0, R.drawable.ic_ab_other);
|
||||||
|
@ -990,22 +1029,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
updateSubtitle();
|
updateSubtitle();
|
||||||
updateTitleIcons();
|
updateTitleIcons();
|
||||||
|
|
||||||
attachItem = menu.addItem(chat_menu_attach, R.drawable.ic_ab_other).setAllowCloseAnimation(false);
|
attachItem = menu.addItem(chat_menu_attach, R.drawable.ic_ab_other).setOverrideMenuClick(true).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.setVisibility(View.GONE);
|
attachItem.setVisibility(View.GONE);
|
||||||
|
|
||||||
menuItem = menu.addItem(chat_menu_attach, R.drawable.ic_ab_attach).setAllowCloseAnimation(false);
|
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);
|
menuItem.setBackgroundDrawable(null);
|
||||||
|
|
||||||
actionModeViews.clear();
|
actionModeViews.clear();
|
||||||
|
@ -1053,8 +1079,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
|
||||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
|
||||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
|
||||||
|
@ -1067,14 +1091,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
}
|
}
|
||||||
|
|
||||||
int childCount = getChildCount();
|
int childCount = getChildCount();
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
View child = getChildAt(i);
|
measureChildWithMargins(chatActivityEnterView, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
||||||
if (child == chatActivityEnterView) {
|
inputFieldHeight = chatActivityEnterView.getMeasuredHeight();
|
||||||
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
|
|
||||||
inputFieldHeight = child.getMeasuredHeight();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
View child = getChildAt(i);
|
View child = getChildAt(i);
|
||||||
if (child.getVisibility() == GONE || child == chatActivityEnterView) {
|
if (child.getVisibility() == GONE || child == chatActivityEnterView) {
|
||||||
|
@ -1660,6 +1680,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachButtonHidden() {
|
public void onAttachButtonHidden() {
|
||||||
|
if (actionBar.isSearchFieldVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (attachItem != null) {
|
if (attachItem != null) {
|
||||||
attachItem.setVisibility(View.VISIBLE);
|
attachItem.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
@ -1670,6 +1693,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachButtonShow() {
|
public void onAttachButtonShow() {
|
||||||
|
if (actionBar.isSearchFieldVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (attachItem != null) {
|
if (attachItem != null) {
|
||||||
attachItem.setVisibility(View.GONE);
|
attachItem.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
@ -1680,7 +1706,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onWindowSizeChanged(int size) {
|
public void onWindowSizeChanged(int size) {
|
||||||
if (size < AndroidUtilities.dp(72) + AndroidUtilities.getCurrentActionBarHeight()) {
|
if (size < AndroidUtilities.dp(72) + ActionBar.getCurrentActionBarHeight()) {
|
||||||
allowStickersPanel = false;
|
allowStickersPanel = false;
|
||||||
if (stickersPanel.getVisibility() == View.VISIBLE) {
|
if (stickersPanel.getVisibility() == View.VISIBLE) {
|
||||||
stickersPanel.clearAnimation();
|
stickersPanel.clearAnimation();
|
||||||
|
@ -2089,12 +2115,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
});
|
});
|
||||||
presentFragment(fragment);
|
presentFragment(fragment);
|
||||||
} else if (which == attach_audio) {
|
} else if (which == attach_audio) {
|
||||||
try {
|
AudioSelectActivity fragment = new AudioSelectActivity();
|
||||||
Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
|
fragment.setDelegate(new AudioSelectActivity.AudioSelectActivityDelegate() {
|
||||||
startActivityForResult(intent, 32);
|
@Override
|
||||||
} catch (Exception e) {
|
public void didSelectAudio(ArrayList<MessageObject> audios) {
|
||||||
FileLog.e("tmessages", e);
|
SendMessagesHelper.prepareSendingAudioDocuments(audios, dialog_id, replyingMessageObject);
|
||||||
|
showReplyPanel(false, null, null, null, false, true);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
presentFragment(fragment);
|
||||||
} else if (which == attach_contact) {
|
} else if (which == attach_contact) {
|
||||||
try {
|
try {
|
||||||
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
|
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.substring(0, 150);
|
||||||
}
|
}
|
||||||
mess = mess.replace("\n", " ");
|
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) {
|
} else if (messageObjects != null) {
|
||||||
if (messageObjects.isEmpty()) {
|
if (messageObjects.isEmpty()) {
|
||||||
|
@ -2277,7 +2306,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
mess = mess.substring(0, 150);
|
mess = mess.substring(0, 150);
|
||||||
}
|
}
|
||||||
mess = mess.replace("\n", " ");
|
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 {
|
} else {
|
||||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedMessage", messageObjects.size()));
|
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedMessage", messageObjects.size()));
|
||||||
}
|
}
|
||||||
|
@ -2296,7 +2325,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
}
|
}
|
||||||
} else if (type == 12) {
|
} else if (type == 12) {
|
||||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedContact", messageObjects.size()));
|
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedContact", messageObjects.size()));
|
||||||
} else if (type == 2) {
|
} else if (type == 2 || type == 14) {
|
||||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedAudio", messageObjects.size()));
|
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedAudio", messageObjects.size()));
|
||||||
} else if (type == 13) {
|
} else if (type == 13) {
|
||||||
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedSticker", messageObjects.size()));
|
replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedSticker", messageObjects.size()));
|
||||||
|
@ -3221,22 +3250,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
FileLog.e("tmessages", e);
|
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 &&
|
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) {
|
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) {
|
if (AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) < 17 && currentEncryptedChat.ttl > 0 && currentEncryptedChat.ttl <= 60) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
||||||
builder.setTitle(LocaleController.getString("AppName", R.string.AppName));
|
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];
|
int encId = (Integer) args[0];
|
||||||
if (currentEncryptedChat != null && currentEncryptedChat.id == encId) {
|
if (currentEncryptedChat != null && currentEncryptedChat.id == encId) {
|
||||||
int date = (Integer) args[1];
|
int date = (Integer) args[1];
|
||||||
boolean started = false;
|
|
||||||
for (MessageObject obj : messages) {
|
for (MessageObject obj : messages) {
|
||||||
if (!obj.isOut()) {
|
if (!obj.isOut()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -4018,7 +4029,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
}
|
}
|
||||||
updateVisibleRows();
|
updateVisibleRows();
|
||||||
}
|
}
|
||||||
} else if (id == NotificationCenter.audioDidReset) {
|
} else if (id == NotificationCenter.audioDidReset || id == NotificationCenter.audioPlayStateChanged) {
|
||||||
Integer mid = (Integer) args[0];
|
Integer mid = (Integer) args[0];
|
||||||
if (chatListView != null) {
|
if (chatListView != null) {
|
||||||
int count = chatListView.getChildCount();
|
int count = chatListView.getChildCount();
|
||||||
|
@ -4027,7 +4038,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
if (view instanceof ChatAudioCell) {
|
if (view instanceof ChatAudioCell) {
|
||||||
ChatAudioCell cell = (ChatAudioCell) view;
|
ChatAudioCell cell = (ChatAudioCell) view;
|
||||||
if (cell.getMessageObject() != null && cell.getMessageObject().getId() == mid) {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4045,6 +4062,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
cell.updateProgress();
|
cell.updateProgress();
|
||||||
break;
|
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) {
|
} else if (id == NotificationCenter.audioDidStarted) {
|
||||||
MessageObject messageObject = (MessageObject) args[0];
|
MessageObject messageObject = (MessageObject) args[0];
|
||||||
sendSecretMessageRead(messageObject);
|
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) {
|
} else if (id == NotificationCenter.updateMessageMedia) {
|
||||||
MessageObject messageObject = (MessageObject) args[0];
|
MessageObject messageObject = (MessageObject) args[0];
|
||||||
MessageObject existMessageObject = messagesDict.get(messageObject.getId());
|
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);
|
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);
|
avatarContainer.setPadding(avatarContainer.getPaddingLeft(), padding, avatarContainer.getPaddingRight(), padding);
|
||||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) avatarContainer.getLayoutParams();
|
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) avatarContainer.getLayoutParams();
|
||||||
layoutParams.topMargin = (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0);
|
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};
|
options = new int[]{8, 2, 3, 1};
|
||||||
} else if (type == 4) {
|
} else if (type == 4) {
|
||||||
if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
|
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};
|
options = new int[]{8, 10, 4, 2, 1};
|
||||||
} else {
|
} 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)};
|
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)};
|
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};
|
options = new int[]{8, 5, 4, 2, 1};
|
||||||
} else if (type == 6) {
|
} 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};
|
options = new int[]{8, 7, 10, 6, 2, 1};
|
||||||
} else if (type == 7) {
|
} 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)};
|
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};
|
options = new int[]{2, 3, 1};
|
||||||
} else if (type == 4) {
|
} else if (type == 4) {
|
||||||
if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
|
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};
|
options = new int[]{10, 4, 2, 1};
|
||||||
} else {
|
} else {
|
||||||
items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Forward", R.string.Forward), LocaleController.getString("Delete", R.string.Delete)};
|
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)};
|
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};
|
options = new int[]{5, 4, 2, 1};
|
||||||
} else if (type == 6) {
|
} 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};
|
options = new int[]{7, 10, 6, 2, 1};
|
||||||
} else if (type == 7) {
|
} 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)};
|
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};
|
options = new int[]{3, 1};
|
||||||
} else if (type == 4) {
|
} else if (type == 4) {
|
||||||
if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaDocument) {
|
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};
|
options = new int[]{10, 4, 1};
|
||||||
} else {
|
} else {
|
||||||
items = new CharSequence[]{LocaleController.getString("SaveToGallery", R.string.SaveToGallery), LocaleController.getString("Delete", R.string.Delete)};
|
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();
|
Bundle args = new Bundle();
|
||||||
args.putBoolean("onlySelect", true);
|
args.putBoolean("onlySelect", true);
|
||||||
args.putInt("dialogsType", 1);
|
args.putInt("dialogsType", 1);
|
||||||
MessagesActivity fragment = new MessagesActivity(args);
|
DialogsActivity fragment = new DialogsActivity(args);
|
||||||
fragment.setDelegate(this);
|
fragment.setDelegate(this);
|
||||||
presentFragment(fragment);
|
presentFragment(fragment);
|
||||||
} else if (option == 3) {
|
} else if (option == 3) {
|
||||||
|
@ -4787,7 +4865,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
}
|
}
|
||||||
} else if (option == 4) {
|
} else if (option == 4) {
|
||||||
String fileName = selectedObject.getFileName();
|
|
||||||
String path = selectedObject.messageOwner.attachPath;
|
String path = selectedObject.messageOwner.attachPath;
|
||||||
if (path != null && path.length() > 0) {
|
if (path != null && path.length() > 0) {
|
||||||
File temp = new File(path);
|
File temp = new File(path);
|
||||||
|
@ -4802,7 +4879,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
MediaController.saveFile(path, getParentActivity(), 1, null);
|
MediaController.saveFile(path, getParentActivity(), 1, null);
|
||||||
} else if (selectedObject.type == 1) {
|
} else if (selectedObject.type == 1) {
|
||||||
MediaController.saveFile(path, getParentActivity(), 0, null);
|
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 intent = new Intent(Intent.ACTION_SEND);
|
||||||
intent.setType(selectedObject.messageOwner.media.document.mime_type);
|
intent.setType(selectedObject.messageOwner.media.document.mime_type);
|
||||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(path)));
|
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) {
|
} else if (option == 6 || option == 7) {
|
||||||
String fileName = selectedObject.getFileName();
|
|
||||||
String path = selectedObject.messageOwner.attachPath;
|
String path = selectedObject.messageOwner.attachPath;
|
||||||
if (path != null && path.length() > 0) {
|
if (path != null && path.length() > 0) {
|
||||||
File temp = new File(path);
|
File temp = new File(path);
|
||||||
|
@ -4848,7 +4924,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
if (path == null || path.length() == 0) {
|
if (path == null || path.length() == 0) {
|
||||||
path = FileLoader.getPathToMessage(selectedObject.messageOwner).toString();
|
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) {
|
if (option == 6) {
|
||||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||||
intent.setType(selectedObject.messageOwner.media.document.mime_type);
|
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) {
|
if (path == null || path.length() == 0) {
|
||||||
path = FileLoader.getPathToMessage(selectedObject.messageOwner).toString();
|
path = FileLoader.getPathToMessage(selectedObject.messageOwner).toString();
|
||||||
}
|
}
|
||||||
MediaController.saveFile(path, getParentActivity(), 2, fileName);
|
MediaController.saveFile(path, getParentActivity(), selectedObject.isMusic() ? 3 : 2, fileName);
|
||||||
}
|
}
|
||||||
selectedObject = null;
|
selectedObject = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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())) {
|
if (dialog_id != 0 && (forwaringMessage != null || !selectedMessagesIds.isEmpty())) {
|
||||||
ArrayList<MessageObject> fmessages = new ArrayList<>();
|
ArrayList<MessageObject> fmessages = new ArrayList<>();
|
||||||
if (forwaringMessage != null) {
|
if (forwaringMessage != null) {
|
||||||
|
@ -4961,7 +5037,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
|
|
||||||
public boolean isGoogleMapsInstalled() {
|
public boolean isGoogleMapsInstalled() {
|
||||||
try {
|
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;
|
return true;
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
if (getParentActivity() == null) {
|
if (getParentActivity() == null) {
|
||||||
|
@ -5213,7 +5289,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
if (url.startsWith("@")) {
|
if (url.startsWith("@")) {
|
||||||
MessagesController.openByUserName(url.substring(1), ChatActivity.this, 0);
|
MessagesController.openByUserName(url.substring(1), ChatActivity.this, 0);
|
||||||
} else if (url.startsWith("#")) {
|
} else if (url.startsWith("#")) {
|
||||||
MessagesActivity fragment = new MessagesActivity(null);
|
DialogsActivity fragment = new DialogsActivity(null);
|
||||||
fragment.setSearchString(url);
|
fragment.setSearchString(url);
|
||||||
presentFragment(fragment);
|
presentFragment(fragment);
|
||||||
} else if (url.startsWith("/")) {
|
} 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) {
|
if (view instanceof ChatBaseCell) {
|
||||||
|
@ -5261,7 +5339,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
if (url.startsWith("@")) {
|
if (url.startsWith("@")) {
|
||||||
MessagesController.openByUserName(url.substring(1), ChatActivity.this, 0);
|
MessagesController.openByUserName(url.substring(1), ChatActivity.this, 0);
|
||||||
} else if (url.startsWith("#")) {
|
} else if (url.startsWith("#")) {
|
||||||
MessagesActivity fragment = new MessagesActivity(null);
|
DialogsActivity fragment = new DialogsActivity(null);
|
||||||
fragment.setSearchString(url);
|
fragment.setSearchString(url);
|
||||||
presentFragment(fragment);
|
presentFragment(fragment);
|
||||||
} else if (url.startsWith("/")) {
|
} 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) {
|
public void needOpenWebView(String url, String title, String originalUrl, int w, int h) {
|
||||||
BottomSheet.Builder builder = new BottomSheet.Builder(mContext);
|
BottomSheet.Builder builder = new BottomSheet.Builder(mContext);
|
||||||
builder.setCustomView(new WebFrameLayout(mContext, builder.create(), title, originalUrl, url, w, h));
|
builder.setCustomView(new WebFrameLayout(mContext, builder.create(), title, originalUrl, url, w, h));
|
||||||
builder.setOverrideTabletWidth(true);
|
builder.setUseFullWidth(true);
|
||||||
showDialog(builder.create());
|
showDialog(builder.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5434,6 +5512,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||||
showDialog(builder.create());
|
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) {
|
} else if (view instanceof ChatActionCell) {
|
||||||
((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() {
|
((ChatActionCell) view).setDelegate(new ChatActionCell.ChatActionCellDelegate() {
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class BotKeyboardView extends LinearLayout {
|
||||||
|
|
||||||
public void setPanelHeight(int height) {
|
public void setPanelHeight(int height) {
|
||||||
panelHeight = 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);
|
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 count = container.getChildCount();
|
||||||
int newHeight = AndroidUtilities.dp(buttonHeight);
|
int newHeight = AndroidUtilities.dp(buttonHeight);
|
||||||
|
@ -87,7 +87,7 @@ public class BotKeyboardView extends LinearLayout {
|
||||||
container.removeAllViews();
|
container.removeAllViews();
|
||||||
buttonViews.clear();
|
buttonViews.clear();
|
||||||
|
|
||||||
if (buttons != null) {
|
if (buttons != null && botButtons.rows.size() != 0) {
|
||||||
isFullSize = (buttons.flags & 1) == 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);
|
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++) {
|
for (int a = 0; a < buttons.rows.size(); a++) {
|
||||||
|
@ -106,7 +106,7 @@ public class BotKeyboardView extends LinearLayout {
|
||||||
textView.setGravity(Gravity.CENTER);
|
textView.setGravity(Gravity.CENTER);
|
||||||
textView.setBackgroundResource(R.drawable.bot_keyboard_states);
|
textView.setBackgroundResource(R.drawable.bot_keyboard_states);
|
||||||
textView.setPadding(AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4), 0);
|
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));
|
layout.addView(textView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, weight, 0, 0, b != row.buttons.size() - 1 ? 10 : 0, 0));
|
||||||
textView.setOnClickListener(new OnClickListener() {
|
textView.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.telegram.android.NotificationCenter;
|
||||||
import org.telegram.messenger.R;
|
import org.telegram.messenger.R;
|
||||||
import org.telegram.messenger.TLRPC;
|
import org.telegram.messenger.TLRPC;
|
||||||
import org.telegram.messenger.UserConfig;
|
import org.telegram.messenger.UserConfig;
|
||||||
|
import org.telegram.ui.ActionBar.ActionBar;
|
||||||
import org.telegram.ui.ActionBar.BaseFragment;
|
import org.telegram.ui.ActionBar.BaseFragment;
|
||||||
import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy;
|
import org.telegram.android.AnimationCompat.AnimatorListenerAdapterProxy;
|
||||||
import org.telegram.android.AnimationCompat.AnimatorSetProxy;
|
import org.telegram.android.AnimationCompat.AnimatorSetProxy;
|
||||||
|
@ -117,6 +118,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
private BaseFragment parentFragment;
|
private BaseFragment parentFragment;
|
||||||
private long dialog_id;
|
private long dialog_id;
|
||||||
private boolean ignoreTextChange;
|
private boolean ignoreTextChange;
|
||||||
|
private int innerTextChange;
|
||||||
private MessageObject replyingMessageObject;
|
private MessageObject replyingMessageObject;
|
||||||
private MessageObject botMessageObject;
|
private MessageObject botMessageObject;
|
||||||
private TLRPC.WebPage messageWebPage;
|
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.setHint(LocaleController.getString("TypeMessage", R.string.TypeMessage));
|
||||||
messageEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
messageEditText.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||||
messageEditText.setInputType(messageEditText.getInputType() | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE);
|
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;
|
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() {
|
messageEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
|
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
|
||||||
|
@ -240,6 +243,8 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
messageEditText.addTextChangedListener(new TextWatcher() {
|
messageEditText.addTextChangedListener(new TextWatcher() {
|
||||||
|
boolean processChange = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
|
||||||
|
|
||||||
|
@ -247,16 +252,20 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
|
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
|
||||||
String message = getTrimmedString(charSequence.toString());
|
if (innerTextChange == 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
checkSendButton(true);
|
checkSendButton(true);
|
||||||
|
String message = getTrimmedString(charSequence.toString());
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
if (count > 2 || charSequence == null || charSequence.length() == 0) {
|
if (count > 2 || charSequence == null || charSequence.length() == 0) {
|
||||||
messageWebPageSearch = true;
|
messageWebPageSearch = true;
|
||||||
}
|
}
|
||||||
delegate.onTextChanged(charSequence, before > count + 1 || (count - before) > 2);
|
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) {
|
if (message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) {
|
||||||
int currentTime = ConnectionsManager.getInstance().getCurrentTime();
|
int currentTime = ConnectionsManager.getInstance().getCurrentTime();
|
||||||
TLRPC.User currentUser = null;
|
TLRPC.User currentUser = null;
|
||||||
|
@ -275,19 +284,19 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable editable) {
|
public void afterTextChanged(Editable editable) {
|
||||||
|
if (innerTextChange != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (sendByEnter && editable.length() > 0 && editable.charAt(editable.length() - 1) == '\n') {
|
if (sendByEnter && editable.length() > 0 && editable.charAt(editable.length() - 1) == '\n') {
|
||||||
sendMessage();
|
sendMessage();
|
||||||
}
|
}
|
||||||
int i = 0;
|
if (processChange) {
|
||||||
ImageSpan[] arrayOfImageSpan = editable.getSpans(0, editable.length(), ImageSpan.class);
|
ImageSpan[] spans = editable.getSpans(0, editable.length(), ImageSpan.class);
|
||||||
int j = arrayOfImageSpan.length;
|
for (int i = 0; i < spans.length; i++) {
|
||||||
while (true) {
|
editable.removeSpan(spans[i]);
|
||||||
if (i >= j) {
|
|
||||||
Emoji.replaceEmoji(editable, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20));
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
editable.removeSpan(arrayOfImageSpan[i]);
|
Emoji.replaceEmoji(editable, messageEditText.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false);
|
||||||
i++;
|
processChange = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -590,7 +599,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
delegate.onWindowSizeChanged(size);
|
delegate.onWindowSizeChanged(size);
|
||||||
}
|
}
|
||||||
if (topView != null) {
|
if (topView != null) {
|
||||||
if (size < AndroidUtilities.dp(72) + AndroidUtilities.getCurrentActionBarHeight()) {
|
if (size < AndroidUtilities.dp(72) + ActionBar.getCurrentActionBarHeight()) {
|
||||||
if (allowShowTopView) {
|
if (allowShowTopView) {
|
||||||
allowShowTopView = false;
|
allowShowTopView = false;
|
||||||
if (needShowTopView) {
|
if (needShowTopView) {
|
||||||
|
@ -773,11 +782,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
});
|
});
|
||||||
runningAnimation2.start();
|
runningAnimation2.start();
|
||||||
|
|
||||||
if (messageEditText != null) {
|
updateFieldRight(0);
|
||||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
|
||||||
layoutParams.rightMargin = AndroidUtilities.dp(0);
|
|
||||||
messageEditText.setLayoutParams(layoutParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate.onAttachButtonHidden();
|
delegate.onAttachButtonHidden();
|
||||||
}
|
}
|
||||||
|
@ -823,9 +828,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
attachButton.setVisibility(View.GONE);
|
attachButton.setVisibility(View.GONE);
|
||||||
attachButton.clearAnimation();
|
attachButton.clearAnimation();
|
||||||
delegate.onAttachButtonHidden();
|
delegate.onAttachButtonHidden();
|
||||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
updateFieldRight(0);
|
||||||
layoutParams.rightMargin = AndroidUtilities.dp(0);
|
|
||||||
messageEditText.setLayoutParams(layoutParams);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -854,11 +857,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
runningAnimation2.setDuration(100);
|
runningAnimation2.setDuration(100);
|
||||||
runningAnimation2.start();
|
runningAnimation2.start();
|
||||||
|
|
||||||
if (messageEditText != null) {
|
updateFieldRight(1);
|
||||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
|
||||||
layoutParams.rightMargin = AndroidUtilities.dp(50);
|
|
||||||
messageEditText.setLayoutParams(layoutParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate.onAttachButtonShow();
|
delegate.onAttachButtonShow();
|
||||||
}
|
}
|
||||||
|
@ -903,13 +902,36 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
if (attachButton != null) {
|
if (attachButton != null) {
|
||||||
delegate.onAttachButtonShow();
|
delegate.onAttachButtonShow();
|
||||||
attachButton.setVisibility(View.VISIBLE);
|
attachButton.setVisibility(View.VISIBLE);
|
||||||
|
updateFieldRight(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFieldRight(int attachVisible) {
|
||||||
|
if (messageEditText == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams();
|
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);
|
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);
|
messageEditText.setLayoutParams(layoutParams);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateAudioRecordIntefrace() {
|
private void updateAudioRecordIntefrace() {
|
||||||
if (recordingAudio) {
|
if (recordingAudio) {
|
||||||
|
@ -1108,6 +1130,7 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
} else {
|
} else {
|
||||||
botButton.setVisibility(GONE);
|
botButton.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
updateFieldRight(2);
|
||||||
ViewProxy.setPivotX(attachButton, AndroidUtilities.dp(botButton.getVisibility() == GONE ? 48 : 96));
|
ViewProxy.setPivotX(attachButton, AndroidUtilities.dp(botButton.getVisibility() == GONE ? 48 : 96));
|
||||||
attachButton.clearAnimation();
|
attachButton.clearAnimation();
|
||||||
}
|
}
|
||||||
|
@ -1209,12 +1232,15 @@ public class ChatActivityEnterView extends FrameLayoutFixed implements Notificat
|
||||||
i = 0;
|
i = 0;
|
||||||
}
|
}
|
||||||
try {
|
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));
|
messageEditText.setText(messageEditText.getText().insert(i, localCharSequence));
|
||||||
int j = i + localCharSequence.length();
|
int j = i + localCharSequence.length();
|
||||||
messageEditText.setSelection(j, j);
|
messageEditText.setSelection(j, j);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e("tmessages", e);
|
FileLog.e("tmessages", e);
|
||||||
|
} finally {
|
||||||
|
innerTextChange = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,11 @@
|
||||||
|
|
||||||
package org.telegram.ui.Components;
|
package org.telegram.ui.Components;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -18,6 +21,7 @@ import android.view.Gravity;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.DecelerateInterpolator;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
@ -25,14 +29,16 @@ import android.widget.TextView;
|
||||||
import org.telegram.android.AndroidUtilities;
|
import org.telegram.android.AndroidUtilities;
|
||||||
import org.telegram.android.LocaleController;
|
import org.telegram.android.LocaleController;
|
||||||
import org.telegram.android.MediaController;
|
import org.telegram.android.MediaController;
|
||||||
|
import org.telegram.android.NotificationCenter;
|
||||||
import org.telegram.android.support.widget.LinearLayoutManager;
|
import org.telegram.android.support.widget.LinearLayoutManager;
|
||||||
import org.telegram.messenger.R;
|
import org.telegram.messenger.R;
|
||||||
import org.telegram.ui.Adapters.PhotoAttachAdapter;
|
import org.telegram.ui.Adapters.PhotoAttachAdapter;
|
||||||
import org.telegram.ui.ChatActivity;
|
import org.telegram.ui.ChatActivity;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
public class ChatAttachView extends FrameLayout {
|
public class ChatAttachView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate {
|
||||||
|
|
||||||
public interface ChatAttachViewDelegate {
|
public interface ChatAttachViewDelegate {
|
||||||
void didPressedButton(int button);
|
void didPressedButton(int button);
|
||||||
|
@ -42,7 +48,16 @@ public class ChatAttachView extends FrameLayout {
|
||||||
private PhotoAttachAdapter photoAttachAdapter;
|
private PhotoAttachAdapter photoAttachAdapter;
|
||||||
private ChatActivity baseFragment;
|
private ChatActivity baseFragment;
|
||||||
private AttachButton sendPhotosButton;
|
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;
|
private ChatAttachViewDelegate delegate;
|
||||||
|
|
||||||
|
@ -56,7 +71,6 @@ public class ChatAttachView extends FrameLayout {
|
||||||
|
|
||||||
imageView = new ImageView(context);
|
imageView = new ImageView(context);
|
||||||
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||||
//imageView.setColorFilter(0x33000000);
|
|
||||||
addView(imageView, LayoutHelper.createFrame(64, 64, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
|
addView(imageView, LayoutHelper.createFrame(64, 64, Gravity.CENTER_HORIZONTAL | Gravity.TOP));
|
||||||
|
|
||||||
textView = new TextView(context);
|
textView = new TextView(context);
|
||||||
|
@ -83,10 +97,15 @@ public class ChatAttachView extends FrameLayout {
|
||||||
public ChatAttachView(Context context) {
|
public ChatAttachView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
RecyclerListView attachPhotoRecyclerView = new RecyclerListView(context);
|
NotificationCenter.getInstance().addObserver(this, NotificationCenter.albumsDidLoaded);
|
||||||
if (photoAttachAdapter != null) {
|
if (MediaController.allPhotosAlbumEntry == null) {
|
||||||
photoAttachAdapter.onDestroy();
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
MediaController.loadGalleryPhotosAlbums(0);
|
||||||
}
|
}
|
||||||
|
loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
views[8] = attachPhotoRecyclerView = new RecyclerListView(context);
|
||||||
attachPhotoRecyclerView.setVerticalScrollBarEnabled(true);
|
attachPhotoRecyclerView.setVerticalScrollBarEnabled(true);
|
||||||
attachPhotoRecyclerView.setAdapter(photoAttachAdapter = new PhotoAttachAdapter(context));
|
attachPhotoRecyclerView.setAdapter(photoAttachAdapter = new PhotoAttachAdapter(context));
|
||||||
attachPhotoRecyclerView.setClipToPadding(false);
|
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);
|
lineView.setBackgroundColor(0xffd2d2d2);
|
||||||
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.TOP | Gravity.LEFT);
|
addView(lineView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.TOP | Gravity.LEFT));
|
||||||
layoutParams.topMargin = AndroidUtilities.dp(88);
|
|
||||||
addView(lineView, layoutParams);
|
|
||||||
CharSequence[] items = new CharSequence[]{
|
CharSequence[] items = new CharSequence[]{
|
||||||
LocaleController.getString("ChatCamera", R.string.ChatCamera),
|
LocaleController.getString("ChatCamera", R.string.ChatCamera),
|
||||||
LocaleController.getString("ChatGallery", R.string.ChatGallery),
|
LocaleController.getString("ChatGallery", R.string.ChatGallery),
|
||||||
|
@ -128,23 +150,21 @@ public class ChatAttachView extends FrameLayout {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
int itemIcons[] = new int[] {
|
int itemIcons[] = new int[] {
|
||||||
R.drawable.ic_attach_photo_big,
|
R.drawable.attach_camera_states,
|
||||||
R.drawable.ic_attach_gallery_big,
|
R.drawable.attach_gallery_states,
|
||||||
R.drawable.ic_attach_video_big,
|
R.drawable.attach_video_states,
|
||||||
R.drawable.ic_attach_music_big,
|
R.drawable.attach_audio_states,
|
||||||
R.drawable.ic_attach_file_big,
|
R.drawable.attach_file_states,
|
||||||
R.drawable.ic_attach_contact_big,
|
R.drawable.attach_contact_states,
|
||||||
R.drawable.ic_attach_location_big,
|
R.drawable.attach_location_states,
|
||||||
R.drawable.ic_attach_hide_big,
|
R.drawable.attach_hide_states,
|
||||||
};
|
};
|
||||||
for (int a = 0; a < 8; a++) {
|
for (int a = 0; a < 8; a++) {
|
||||||
AttachButton attachButton = new AttachButton(context);
|
AttachButton attachButton = new AttachButton(context);
|
||||||
attachButton.setTextAndIcon(items[a], itemIcons[a]);
|
attachButton.setTextAndIcon(items[a], itemIcons[a]);
|
||||||
int y = 97 + 95 * (a / 4);
|
addView(attachButton, LayoutHelper.createFrame(85, 90, Gravity.LEFT | Gravity.TOP));
|
||||||
int x = 10 + (a % 4) * 85;
|
|
||||||
addView(attachButton, LayoutHelper.createFrame(85, 90, Gravity.LEFT | Gravity.TOP, x, y, 0, 0));
|
|
||||||
attachButton.setTag(a);
|
attachButton.setTag(a);
|
||||||
buttons[a] = attachButton;
|
views[a] = attachButton;
|
||||||
if (a == 7) {
|
if (a == 7) {
|
||||||
sendPhotosButton = attachButton;
|
sendPhotosButton = attachButton;
|
||||||
sendPhotosButton.imageView.setPadding(0, AndroidUtilities.dp(4), 0, 0);
|
sendPhotosButton.imageView.setPadding(0, AndroidUtilities.dp(4), 0, 0);
|
||||||
|
@ -164,24 +184,58 @@ public class ChatAttachView extends FrameLayout {
|
||||||
return true;
|
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
|
@Override
|
||||||
protected void onMeasure(int widthSpec, int heightSpec) {
|
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() {
|
public void updatePhotosButton() {
|
||||||
int count = photoAttachAdapter.getSelectedPhotos().size();
|
int count = photoAttachAdapter.getSelectedPhotos().size();
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
sendPhotosButton.imageView.setPadding(0, AndroidUtilities.dp(4), 0, 0);
|
sendPhotosButton.imageView.setPadding(0, AndroidUtilities.dp(4), 0, 0);
|
||||||
sendPhotosButton.imageView.setBackgroundResource(R.drawable.ic_attach_hide_big);
|
sendPhotosButton.imageView.setBackgroundResource(R.drawable.attach_hide_states);
|
||||||
sendPhotosButton.imageView.setImageResource(R.drawable.ic_attach_hide_big_icon);
|
sendPhotosButton.imageView.setImageResource(R.drawable.attach_hide2);
|
||||||
sendPhotosButton.textView.setText("");
|
sendPhotosButton.textView.setText("");
|
||||||
} else {
|
} else {
|
||||||
sendPhotosButton.imageView.setPadding(AndroidUtilities.dp(2), 0, 0, 0);
|
sendPhotosButton.imageView.setPadding(AndroidUtilities.dp(2), 0, 0, 0);
|
||||||
sendPhotosButton.imageView.setBackgroundResource(R.drawable.ic_attach_send_big);
|
sendPhotosButton.imageView.setBackgroundResource(R.drawable.attach_send_states);
|
||||||
sendPhotosButton.imageView.setImageResource(R.drawable.ic_attach_send_big_icon);
|
sendPhotosButton.imageView.setImageResource(R.drawable.attach_send2);
|
||||||
sendPhotosButton.textView.setText(LocaleController.formatString("SendItems", R.string.SendItems, String.format("(%d)", count)));
|
sendPhotosButton.textView.setText(LocaleController.formatString("SendItems", R.string.SendItems, String.format("(%d)", count)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,23 +244,84 @@ public class ChatAttachView extends FrameLayout {
|
||||||
delegate = chatAttachViewDelegate;
|
delegate = chatAttachViewDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startAnimations(boolean up) {
|
public void onRevealAnimationEnd(boolean open) {
|
||||||
for (int a = 0; a < 4; a++) {
|
if (open && Build.VERSION.SDK_INT <= 19 && MediaController.allPhotosAlbumEntry == null) {
|
||||||
//buttons[a].setTranslationY(AndroidUtilities.dp(up ? 20 : -20));
|
MediaController.loadGalleryPhotosAlbums(0);
|
||||||
//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);
|
@SuppressLint("NewApi")
|
||||||
buttons[a + 4].setScaleY(0.8f);
|
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 animatorSet = new AnimatorSet();
|
||||||
animatorSet.playTogether(ObjectAnimator.ofFloat(buttons[a], "scaleX", 1),
|
animatorSet.playTogether(animators);
|
||||||
ObjectAnimator.ofFloat(buttons[a + 4], "scaleX", 1),
|
|
||||||
ObjectAnimator.ofFloat(buttons[a], "scaleY", 1),
|
|
||||||
ObjectAnimator.ofFloat(buttons[a + 4], "scaleY", 1));
|
|
||||||
animatorSet.setDuration(150);
|
animatorSet.setDuration(150);
|
||||||
animatorSet.setStartDelay((3 - a) * 40);
|
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();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
animatorSet.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(ChatActivity parentFragment) {
|
public void init(ChatActivity parentFragment) {
|
||||||
|
@ -221,7 +336,7 @@ public class ChatAttachView extends FrameLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
photoAttachAdapter.onDestroy();
|
NotificationCenter.getInstance().removeObserver(this, NotificationCenter.albumsDidLoaded);
|
||||||
baseFragment = null;
|
baseFragment = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue