Messaging: Remove unused code

Change-Id: I8093bb3e305ae323f7069bf42f9e83f8c8647cc7
This commit is contained in:
Michael W
2024-12-26 15:54:37 +01:00
parent 0069df879a
commit e29b158fba
69 changed files with 42 additions and 3228 deletions
-10
View File
@@ -46,17 +46,7 @@
<string name="wireless_alerts_key" translatable="false">buglesub_wireless_alerts_key</string> <string name="wireless_alerts_key" translatable="false">buglesub_wireless_alerts_key</string>
<string name="apn_list_pref_key" translatable="false">buglesub_apn_list</string> <string name="apn_list_pref_key" translatable="false">buglesub_apn_list</string>
<!-- SMS/MMS settings keys -->
<!--
TODO: Several of these are currently unused but are expected to be needed to
implement SMS/MMS delivery and basic settings. Once we have the core functionality in place
we should do a pass to remove any unused values here.
-->
<string name="use_local_apn_pref_key" translatable="false">use_local_apn_pref_key</string>
<bool name="use_local_apn_pref_default" translatable="false">false</bool>
<integer name="mediapicker_transition_duration">600</integer><!-- ms --> <integer name="mediapicker_transition_duration">600</integer><!-- ms -->
<integer name="asyncimage_transition_duration">300</integer><!-- ms -->
<integer name="compose_transition_duration">300</integer><!-- ms --> <integer name="compose_transition_duration">300</integer><!-- ms -->
<integer name="camera_shutter_duration">200</integer><!-- ms --> <integer name="camera_shutter_duration">200</integer><!-- ms -->
<fraction name="camera_shutter_max_alpha">70%</fraction> <fraction name="camera_shutter_max_alpha">70%</fraction>
-1
View File
@@ -116,7 +116,6 @@
<dimen name="vcard_detail_group_indicator_width">40dp</dimen> <dimen name="vcard_detail_group_indicator_width">40dp</dimen>
<dimen name="mms_indicator_size">12sp</dimen> <dimen name="mms_indicator_size">12sp</dimen>
<dimen name="conversation_fast_fling_threshold">10dp</dimen>
<dimen name="list_empty_text_size">14sp</dimen> <dimen name="list_empty_text_size">14sp</dimen>
<dimen name="list_empty_text_top_margin">20dp</dimen> <dimen name="list_empty_text_top_margin">20dp</dimen>
<dimen name="list_empty_text_left_right_margin">60dp</dimen> <dimen name="list_empty_text_left_right_margin">60dp</dimen>
@@ -480,24 +480,4 @@ public class MmsHttpClient {
} }
return null; return null;
} }
/**
* Get NAI using hidden SystemProperties.get(String)
*
* @return the NAI string as system property
*/
private static String getNaiBySystemProperty() {
try {
final Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
if (systemPropertiesClass != null) {
final Method method = systemPropertiesClass.getMethod("get", String.class);
if (method != null) {
return (String) method.invoke(null, NAI_PROPERTY);
}
}
} catch (Exception e) {
Log.w(MmsService.TAG, "SystemProperties.get failed " + e);
}
return null;
}
} }
@@ -36,32 +36,6 @@ public class MmsManager {
// Cached computed overrides for carrier configuration values // Cached computed overrides for carrier configuration values
private static final SparseArray<Bundle> sConfigOverridesMap = new SparseArray<>(); private static final SparseArray<Bundle> sConfigOverridesMap = new SparseArray<>();
/**
* Set the size of thread pool for request execution.
*
* Default is 4
*
* Note: if system MMS API is used, this has no effect
*
* @param size thread pool size
*/
public static void setThreadPoolSize(int size) {
MmsService.setThreadPoolSize(size);
}
/**
* Set whether to use wake lock while sending or downloading MMS.
*
* Default value is true
*
* Note: if system MMS API is used, this has no effect
*
* @param useWakeLock true to use wake lock, false otherwise
*/
public static void setUseWakeLock(final boolean useWakeLock) {
MmsService.setUseWakeLock(useWakeLock);
}
/** /**
* Set the optional carrier config values loader * Set the optional carrier config values loader
* *
@@ -98,23 +98,11 @@ abstract class MmsRequest implements Parcelable {
// Thread pool for transferring PDU with MMS apps // Thread pool for transferring PDU with MMS apps
protected final ExecutorService mPduTransferExecutor = Executors.newCachedThreadPool(); protected final ExecutorService mPduTransferExecutor = Executors.newCachedThreadPool();
// Whether this request should acquire wake lock
private boolean mUseWakeLock;
protected MmsRequest(final String locationUrl, final Uri pduUri, protected MmsRequest(final String locationUrl, final Uri pduUri,
final PendingIntent pendingIntent) { final PendingIntent pendingIntent) {
mLocationUrl = locationUrl; mLocationUrl = locationUrl;
mPduUri = pduUri; mPduUri = pduUri;
mPendingIntent = pendingIntent; mPendingIntent = pendingIntent;
mUseWakeLock = true;
}
void setUseWakeLock(final boolean useWakeLock) {
mUseWakeLock = useWakeLock;
}
boolean getUseWakeLock() {
return mUseWakeLock;
} }
/** /**
@@ -376,7 +364,6 @@ abstract class MmsRequest implements Parcelable {
@Override @Override
public void writeToParcel(Parcel parcel, int flags) { public void writeToParcel(Parcel parcel, int flags) {
parcel.writeByte((byte) (mUseWakeLock ? 1 : 0));
parcel.writeString(mLocationUrl); parcel.writeString(mLocationUrl);
parcel.writeParcelable(mPduUri, 0); parcel.writeParcelable(mPduUri, 0);
parcel.writeParcelable(mPendingIntent, 0); parcel.writeParcelable(mPendingIntent, 0);
@@ -384,7 +371,6 @@ abstract class MmsRequest implements Parcelable {
protected MmsRequest(final Parcel in) { protected MmsRequest(final Parcel in) {
final ClassLoader classLoader = MmsRequest.class.getClassLoader(); final ClassLoader classLoader = MmsRequest.class.getClassLoader();
mUseWakeLock = in.readByte() != 0;
mLocationUrl = in.readString(); mLocationUrl = in.readString();
mPduUri = in.readParcelable(classLoader); mPduUri = in.readParcelable(classLoader);
mPendingIntent = in.readParcelable(classLoader); mPendingIntent = in.readParcelable(classLoader);
-104
View File
@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.PowerManager;
import android.os.Process; import android.os.Process;
import android.telephony.SmsManager; import android.telephony.SmsManager;
import android.util.Log; import android.util.Log;
@@ -45,20 +44,11 @@ public class MmsService extends Service {
private static final String EXTRA_REQUEST = "request"; private static final String EXTRA_REQUEST = "request";
private static final String EXTRA_MYPID = "mypid"; private static final String EXTRA_MYPID = "mypid";
private static final String WAKELOCK_ID = "mmslib_wakelock";
/** /**
* Thread pool size for each request queue * Thread pool size for each request queue
*/ */
private static volatile int sThreadPoolSize = DEFAULT_THREAD_POOL_SIZE; private static volatile int sThreadPoolSize = DEFAULT_THREAD_POOL_SIZE;
/**
* Optional wake lock to use
*/
private static volatile boolean sUseWakeLock = true;
private static volatile PowerManager.WakeLock sWakeLock = null;
private static final Object sWakeLockLock = new Object();
/** /**
* Carrier configuration values loader * Carrier configuration values loader
*/ */
@@ -74,25 +64,6 @@ public class MmsService extends Service {
*/ */
private static volatile UserAgentInfoLoader sUserAgentInfoLoader = null; private static volatile UserAgentInfoLoader sUserAgentInfoLoader = null;
/**
* Set the size of thread pool for request execution.
* Default is DEFAULT_THREAD_POOL_SIZE
*
* @param size thread pool size
*/
static void setThreadPoolSize(final int size) {
sThreadPoolSize = size;
}
/**
* Set whether to use wake lock
*
* @param useWakeLock true to use wake lock, false otherwise
*/
static void setUseWakeLock(final boolean useWakeLock) {
sUseWakeLock = useWakeLock;
}
/** /**
* Set the optional carrier config values * Set the optional carrier config values
* *
@@ -164,52 +135,6 @@ public class MmsService extends Service {
} }
} }
/**
* Acquire the wake lock
*
* @param context the context to use
*/
private static void acquireWakeLock(final Context context) {
synchronized (sWakeLockLock) {
if (sWakeLock == null) {
final PowerManager pm =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
sWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_ID);
}
sWakeLock.acquire();
}
}
/**
* Release the wake lock
*/
private static void releaseWakeLock() {
boolean releasedEmptyWakeLock = false;
synchronized (sWakeLockLock) {
if (sWakeLock != null) {
sWakeLock.release();
} else {
releasedEmptyWakeLock = true;
}
}
if (releasedEmptyWakeLock) {
Log.w(TAG, "Releasing empty wake lock");
}
}
/**
* Check if wake lock is not held (e.g. when service stops)
*/
private static void verifyWakeLockNotHeld() {
boolean wakeLockHeld = false;
synchronized (sWakeLockLock) {
wakeLockHeld = sWakeLock != null && sWakeLock.isHeld();
}
if (wakeLockHeld) {
Log.e(TAG, "Wake lock still held!");
}
}
// Remember my PID to discard restarted intent // Remember my PID to discard restarted intent
private static volatile int sMyPid = -1; private static volatile int sMyPid = -1;
@@ -253,28 +178,6 @@ public class MmsService extends Service {
// Service stop task // Service stop task
private final Runnable mServiceStopRunnable = this::tryStopService; private final Runnable mServiceStopRunnable = this::tryStopService;
/**
* Start the service with a request
*
* @param context the Context to use
* @param request the request to start
*/
public static void startRequest(final Context context, final MmsRequest request) {
final boolean useWakeLock = sUseWakeLock;
request.setUseWakeLock(useWakeLock);
final Intent intent = new Intent(context, MmsService.class);
intent.putExtra(EXTRA_REQUEST, request);
intent.putExtra(EXTRA_MYPID, getMyPid());
if (useWakeLock) {
acquireWakeLock(context);
}
if (context.startService(intent) == null) {
if (useWakeLock) {
releaseWakeLock();
}
}
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
@@ -331,9 +234,6 @@ public class MmsService extends Service {
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Unexpected execution failure", e); Log.w(TAG, "Unexpected execution failure", e);
} finally { } finally {
if (request.getUseWakeLock()) {
releaseWakeLock();
}
releaseService(); releaseService();
} }
}); });
@@ -344,9 +244,6 @@ public class MmsService extends Service {
Log.w(TAG, "Executing request failed " + e); Log.w(TAG, "Executing request failed " + e);
request.returnResult(this, SmsManager.MMS_ERROR_UNSPECIFIED, request.returnResult(this, SmsManager.MMS_ERROR_UNSPECIFIED,
null/*response*/, 0/*httpStatusCode*/); null/*response*/, 0/*httpStatusCode*/);
if (request.getUseWakeLock()) {
releaseWakeLock();
}
} }
} else { } else {
Log.w(TAG, "Empty request"); Log.w(TAG, "Empty request");
@@ -434,7 +331,6 @@ public class MmsService extends Service {
if (stopped != null) { if (stopped != null) {
if (stopped) { if (stopped) {
Log.i(TAG, "Service successfully stopped"); Log.i(TAG, "Service successfully stopped");
verifyWakeLockNotHeld();
} else { } else {
Log.i(TAG, "Service stopping cancelled"); Log.i(TAG, "Service stopping cancelled");
} }
@@ -26,7 +26,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
/** /**
* Encoded-string-value = Text-string | Value-length Char-set Text-string * Encoded-string-value = Text-string | Value-length Char-set Text-string
@@ -224,43 +223,6 @@ public class EncodedStringValue implements Cloneable {
return ret; return ret;
} }
/**
* Extract an EncodedStringValue[] from a given String.
*/
public static EncodedStringValue[] extract(String src) {
String[] values = src.split(";");
ArrayList<EncodedStringValue> list = new ArrayList<>();
for (int i = 0; i < values.length; i++) {
if (values[i].length() > 0) {
list.add(new EncodedStringValue(values[i]));
}
}
int len = list.size();
if (len > 0) {
return list.toArray(new EncodedStringValue[len]);
} else {
return null;
}
}
/**
* Concatenate an EncodedStringValue[] into a single String.
*/
public static String concat(EncodedStringValue[] addr) {
StringBuilder sb = new StringBuilder();
int maxIndex = addr.length - 1;
for (int i = 0; i <= maxIndex; i++) {
sb.append(addr[i].getString());
if (i < maxIndex) {
sb.append(";");
}
}
return sb.toString();
}
public static EncodedStringValue copy(EncodedStringValue value) { public static EncodedStringValue copy(EncodedStringValue value) {
if (value == null) { if (value == null) {
return null; return null;
@@ -116,7 +116,6 @@ public class BugleApplication extends Application implements UncaughtExceptionHa
MmsManager.setApnSettingsLoader(new BugleApnSettingsLoader(context)); MmsManager.setApnSettingsLoader(new BugleApnSettingsLoader(context));
MmsManager.setCarrierConfigValuesLoader(carrierConfigValuesLoader); MmsManager.setCarrierConfigValuesLoader(carrierConfigValuesLoader);
MmsManager.setUserAgentInfoLoader(new BugleUserAgentInfoLoader(context)); MmsManager.setUserAgentInfoLoader(new BugleUserAgentInfoLoader(context));
MmsManager.setUseWakeLock(true);
} }
public static void updateAppConfig(final Context context) { public static void updateAppConfig(final Context context) {
@@ -57,7 +57,6 @@ class FactoryImpl extends Factory {
private MediaResourceManager mMediaResourceManager; private MediaResourceManager mMediaResourceManager;
private MediaCacheManager mMediaCacheManager; private MediaCacheManager mMediaCacheManager;
private ContactContentObserver mContactContentObserver; private ContactContentObserver mContactContentObserver;
private PhoneUtils mPhoneUtils;
private MediaUtil mMediaUtil; private MediaUtil mMediaUtil;
private SparseArray<BugleSubscriptionPrefs> mSubscriptionPrefs; private SparseArray<BugleSubscriptionPrefs> mSubscriptionPrefs;
private BugleCarrierConfigValuesLoader mCarrierConfigValuesLoader; private BugleCarrierConfigValuesLoader mCarrierConfigValuesLoader;
@@ -1,365 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS 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 com.android.messaging.datamodel;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import androidx.annotation.NonNull;
import android.text.TextUtils;
import android.util.SparseArray;
import com.android.messaging.datamodel.MemoryCacheManager.MemoryCache;
import com.android.messaging.util.Assert;
import com.android.messaging.util.LogUtil;
import java.io.InputStream;
/**
* Class for creating / loading / reusing bitmaps. This class allow the user to create a new bitmap,
* reuse an bitmap from the pool and to return a bitmap for future reuse. The pool of bitmaps
* allows for faster decode and more efficient memory usage.
* Note: consumers should not create BitmapPool directly, but instead get the pool they want from
* the BitmapPoolManager.
*/
public class BitmapPool implements MemoryCache {
public static final int MAX_SUPPORTED_IMAGE_DIMENSION = 0xFFFF;
protected static final boolean VERBOSE = false;
/**
* Number of reuse failures to skip before reporting.
*/
private static final int FAILED_REPORTING_FREQUENCY = 100;
/**
* Count of reuse failures which have occurred.
*/
private static volatile int sFailedBitmapReuseCount = 0;
/**
* Overall pool data structure which currently only supports rectangular bitmaps. The size of
* one of the sides is used to index into the SparseArray.
*/
private final SparseArray<SingleSizePool> mPool;
private final Object mPoolLock = new Object();
private final String mPoolName;
private final int mMaxSize;
/**
* Inner structure which holds a pool of bitmaps all the same size (i.e. all have the same
* width as each other and height as each other, but not necessarily the same).
*/
private class SingleSizePool {
int mNumItems;
final Bitmap[] mBitmaps;
SingleSizePool(final int maxPoolSize) {
mNumItems = 0;
mBitmaps = new Bitmap[maxPoolSize];
}
}
/**
* Creates a pool of reused bitmaps with helper decode methods which will attempt to use the
* reclaimed bitmaps. This will help speed up the creation of bitmaps by using already allocated
* bitmaps.
* @param maxSize The overall max size of the pool. When the pool exceeds this size, all calls
* to reclaimBitmap(Bitmap) will result in recycling the bitmap.
* @param name Name of the bitmap pool and only used for logging. Can not be null.
*/
BitmapPool(final int maxSize, @NonNull final String name) {
Assert.isTrue(maxSize > 0);
Assert.isTrue(!TextUtils.isEmpty(name));
mPoolName = name;
mMaxSize = maxSize;
mPool = new SparseArray<>();
}
@Override
public void reclaim() {
synchronized (mPoolLock) {
for (int p = 0; p < mPool.size(); p++) {
final SingleSizePool singleSizePool = mPool.valueAt(p);
for (int i = 0; i < singleSizePool.mNumItems; i++) {
singleSizePool.mBitmaps[i].recycle();
singleSizePool.mBitmaps[i] = null;
}
singleSizePool.mNumItems = 0;
}
mPool.clear();
}
}
/**
* Creates a new BitmapFactory.Options.
*/
public static BitmapFactory.Options getBitmapOptionsForPool(final boolean scaled,
final int inputDensity, final int targetDensity) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = scaled;
options.inDensity = inputDensity;
options.inTargetDensity = targetDensity;
options.inSampleSize = 1;
options.inJustDecodeBounds = false;
options.inMutable = true;
return options;
}
/**
* @return The pool key for the provided image dimensions or 0 if either width or height is
* greater than the max supported image dimension.
*/
private int getPoolKey(final int width, final int height) {
if (width > MAX_SUPPORTED_IMAGE_DIMENSION || height > MAX_SUPPORTED_IMAGE_DIMENSION) {
return 0;
}
return (width << 16) | height;
}
/**
*
* @return A bitmap in the pool with the specified dimensions or null if no bitmap with the
* specified dimension is available.
*/
private Bitmap findPoolBitmap(final int width, final int height) {
final int poolKey = getPoolKey(width, height);
if (poolKey != 0) {
synchronized (mPoolLock) {
// Take a bitmap from the pool if one is available
final SingleSizePool singlePool = mPool.get(poolKey);
if (singlePool != null && singlePool.mNumItems > 0) {
singlePool.mNumItems--;
final Bitmap foundBitmap = singlePool.mBitmaps[singlePool.mNumItems];
singlePool.mBitmaps[singlePool.mNumItems] = null;
return foundBitmap;
}
}
}
return null;
}
/**
* Internal function to try and find a bitmap in the pool which matches the desired width and
* height and then set that in the bitmap options properly.
*
* TODO: Why do we take a width/height? Shouldn't this already be in the
* BitmapFactory.Options instance? Can we assert that they match?
* @param optionsTmp The BitmapFactory.Options to update with the bitmap for the system to try
* to reuse.
* @param width The width of the reusable bitmap.
* @param height The height of the reusable bitmap.
*/
private void assignPoolBitmap(final BitmapFactory.Options optionsTmp, final int width,
final int height) {
if (optionsTmp.inJustDecodeBounds) {
return;
}
optionsTmp.inBitmap = findPoolBitmap(width, height);
}
/**
* Load a resource into a bitmap. Uses a bitmap from the pool if possible to reduce memory
* turnover.
* @param resourceId Resource id to load.
* @param resources Application resources. Cannot be null.
* @param optionsTmp Should be the same options returned from getBitmapOptionsForPool(). Cannot
* be null.
* @param width The width of the bitmap.
* @param height The height of the bitmap.
* @return The decoded Bitmap with the resource drawn in it.
*/
public Bitmap decodeSampledBitmapFromResource(final int resourceId,
@NonNull final Resources resources, @NonNull final BitmapFactory.Options optionsTmp,
final int width, final int height) {
Assert.notNull(resources);
Assert.notNull(optionsTmp);
Assert.isTrue(width > 0);
Assert.isTrue(height > 0);
assignPoolBitmap(optionsTmp, width, height);
Bitmap b = null;
try {
b = BitmapFactory.decodeResource(resources, resourceId, optionsTmp);
} catch (final IllegalArgumentException e) {
// BitmapFactory couldn't decode the file, try again without an inputBufferBitmap.
if (optionsTmp.inBitmap != null) {
optionsTmp.inBitmap = null;
b = BitmapFactory.decodeResource(resources, resourceId, optionsTmp);
sFailedBitmapReuseCount++;
if (sFailedBitmapReuseCount % FAILED_REPORTING_FREQUENCY == 0) {
LogUtil.w(LogUtil.BUGLE_TAG,
"Pooled bitmap consistently not being reused count = " +
sFailedBitmapReuseCount);
}
}
} catch (final OutOfMemoryError e) {
LogUtil.w(LogUtil.BUGLE_TAG, "Oom decoding resource " + resourceId);
reclaim();
}
return b;
}
/**
* Load an input stream into a bitmap. Uses a bitmap from the pool if possible to reduce memory
* turnover.
* @param inputStream InputStream load. Cannot be null.
* @param optionsTmp Should be the same options returned from getBitmapOptionsForPool(). Cannot
* be null.
* @param width The width of the bitmap.
* @param height The height of the bitmap.
* @return The decoded Bitmap with the resource drawn in it.
*/
public Bitmap decodeSampledBitmapFromInputStream(@NonNull final InputStream inputStream,
@NonNull final BitmapFactory.Options optionsTmp,
final int width, final int height) {
Assert.notNull(inputStream);
Assert.isTrue(width > 0);
Assert.isTrue(height > 0);
assignPoolBitmap(optionsTmp, width, height);
Bitmap b = null;
try {
b = BitmapFactory.decodeStream(inputStream, null, optionsTmp);
} catch (final IllegalArgumentException e) {
// BitmapFactory couldn't decode the file, try again without an inputBufferBitmap.
if (optionsTmp.inBitmap != null) {
optionsTmp.inBitmap = null;
b = BitmapFactory.decodeStream(inputStream, null, optionsTmp);
sFailedBitmapReuseCount++;
if (sFailedBitmapReuseCount % FAILED_REPORTING_FREQUENCY == 0) {
LogUtil.w(LogUtil.BUGLE_TAG,
"Pooled bitmap consistently not being reused count = " +
sFailedBitmapReuseCount);
}
}
} catch (final OutOfMemoryError e) {
LogUtil.w(LogUtil.BUGLE_TAG, "Oom decoding inputStream");
reclaim();
}
return b;
}
/**
* Turn encoded bytes into a bitmap. Uses a bitmap from the pool if possible to reduce memory
* turnover.
* @param bytes Encoded bytes to draw on the bitmap. Cannot be null.
* @param optionsTmp The bitmap will set here and the input should be generated from
* getBitmapOptionsForPool(). Cannot be null.
* @param width The width of the bitmap.
* @param height The height of the bitmap.
* @return A Bitmap with the encoded bytes drawn in it.
*/
public Bitmap decodeByteArray(@NonNull final byte[] bytes,
@NonNull final BitmapFactory.Options optionsTmp, final int width,
final int height) throws OutOfMemoryError {
Assert.notNull(bytes);
Assert.notNull(optionsTmp);
Assert.isTrue(width > 0);
Assert.isTrue(height > 0);
assignPoolBitmap(optionsTmp, width, height);
Bitmap b = null;
try {
b = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, optionsTmp);
} catch (final IllegalArgumentException e) {
if (VERBOSE) {
LogUtil.v(LogUtil.BUGLE_TAG, "BitmapPool(" + mPoolName +
") Unable to use pool bitmap");
}
// BitmapFactory couldn't decode the file, try again without an inputBufferBitmap.
// (i.e. without the bitmap from the pool)
if (optionsTmp.inBitmap != null) {
optionsTmp.inBitmap = null;
b = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, optionsTmp);
sFailedBitmapReuseCount++;
if (sFailedBitmapReuseCount % FAILED_REPORTING_FREQUENCY == 0) {
LogUtil.w(LogUtil.BUGLE_TAG,
"Pooled bitmap consistently not being reused count = " +
sFailedBitmapReuseCount);
}
}
}
return b;
}
/**
* Creates a bitmap with the given size, this will reuse a bitmap in the pool, if one is
* available, otherwise this will create a new one.
* @param width The desired width of the bitmap.
* @param height The desired height of the bitmap.
* @return A bitmap with the desired width and height, this maybe a reused bitmap from the pool.
*/
public Bitmap createOrReuseBitmap(final int width, final int height) {
Bitmap b = findPoolBitmap(width, height);
if (b == null) {
b = createBitmap(width, height);
}
return b;
}
/**
* This will create a new bitmap regardless of pool state.
* @param width The desired width of the bitmap.
* @param height The desired height of the bitmap.
* @return A bitmap with the desired width and height.
*/
private Bitmap createBitmap(final int width, final int height) {
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
/**
* Called when a bitmap is finished being used so that it can be used for another bitmap in the
* future or recycled. Any bitmaps returned should not be used by the caller again.
* @param b The bitmap to return to the pool for future usage or recycled. This cannot be null.
*/
public void reclaimBitmap(@NonNull final Bitmap b) {
Assert.notNull(b);
final int poolKey = getPoolKey(b.getWidth(), b.getHeight());
if (poolKey == 0 || !b.isMutable()) {
// Unsupported image dimensions or a immutable bitmap.
b.recycle();
return;
}
synchronized (mPoolLock) {
SingleSizePool singleSizePool = mPool.get(poolKey);
if (singleSizePool == null) {
singleSizePool = new SingleSizePool(mMaxSize);
mPool.append(poolKey, singleSizePool);
}
if (singleSizePool.mNumItems < singleSizePool.mBitmaps.length) {
singleSizePool.mBitmaps[singleSizePool.mNumItems] = b;
singleSizePool.mNumItems++;
} else {
b.recycle();
}
}
}
/**
* @return whether the pool is full for a given width and height.
*/
public boolean isFull(final int width, final int height) {
final int poolKey = getPoolKey(width, height);
synchronized (mPoolLock) {
final SingleSizePool singleSizePool = mPool.get(poolKey);
if (singleSizePool != null &&
singleSizePool.mNumItems >= singleSizePool.mBitmaps.length) {
return true;
}
return false;
}
}
}
@@ -835,17 +835,6 @@ public class BugleDatabaseOperations {
return null; return null;
} }
/**
* Frees up memory associated with phone number to participant id matching.
*/
@DoesNotRunOnMainThread
public static void clearParticipantIdCache() {
Assert.isNotMainThread();
synchronized (sNormalizedPhoneNumberToParticipantIdCache) {
sNormalizedPhoneNumberToParticipantIdCache.clear();
}
}
@DoesNotRunOnMainThread @DoesNotRunOnMainThread
public static ArrayList<String> getRecipientsForConversation(final DatabaseWrapper dbWrapper, public static ArrayList<String> getRecipientsForConversation(final DatabaseWrapper dbWrapper,
final String conversationId) { final String conversationId) {
@@ -940,21 +929,6 @@ public class BugleDatabaseOperations {
return message; return message;
} }
@VisibleForTesting
static MessagePartData readMessagePartData(final DatabaseWrapper dbWrapper,
final String partId) {
MessagePartData messagePartData = null;
try (Cursor cursor = dbWrapper.query(DatabaseHelper.PARTS_TABLE,
MessagePartData.getProjection(), PartColumns._ID + "=?",
new String[]{partId}, null, null, null)) {
Assert.inRange(cursor.getCount(), 0, 1);
if (cursor.moveToFirst()) {
messagePartData = MessagePartData.createFromCursor(cursor);
}
}
return messagePartData;
}
@DoesNotRunOnMainThread @DoesNotRunOnMainThread
public static MessageData readMessageData(final DatabaseWrapper dbWrapper, public static MessageData readMessageData(final DatabaseWrapper dbWrapper,
final Uri smsMessageUri) { final Uri smsMessageUri) {
@@ -1726,17 +1700,6 @@ public class BugleDatabaseOperations {
} }
} }
/**
* Refresh conversation names/avatars based on a changed participant.
*/
@DoesNotRunOnMainThread
public static void refreshConversationsForParticipant(final String participantId) {
Assert.isNotMainThread();
final ArrayList<String> participantList = new ArrayList<>(1);
participantList.add(participantId);
refreshConversationsForParticipants(participantList);
}
/** /**
* Refresh one conversation. * Refresh one conversation.
*/ */
@@ -1,101 +0,0 @@
/*
* 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 com.android.messaging.datamodel;
public class DataModelException extends Exception {
private static final long serialVersionUID = 1L;
private static final int FIRST = 100;
// ERRORS GENERATED INTERNALLY BY DATA MODEL.
// ERRORS RELATED WITH SMS.
public static final int ERROR_SMS_TEMPORARY_FAILURE = 116;
public static final int ERROR_SMS_PERMANENT_FAILURE = 117;
public static final int ERROR_MMS_TEMPORARY_FAILURE = 118;
public static final int ERROR_MMS_PERMANENT_UNKNOWN_FAILURE = 119;
// Request expired.
public static final int ERROR_EXPIRED = 120;
// Request canceled by user.
public static final int ERROR_CANCELED = 121;
public static final int ERROR_MOBILE_DATA_DISABLED = 123;
public static final int ERROR_MMS_SERVICE_BLOCKED = 124;
public static final int ERROR_MMS_INVALID_ADDRESS = 125;
public static final int ERROR_MMS_NETWORK_PROBLEM = 126;
public static final int ERROR_MMS_MESSAGE_NOT_FOUND = 127;
public static final int ERROR_MMS_MESSAGE_FORMAT_CORRUPT = 128;
public static final int ERROR_MMS_CONTENT_NOT_ACCEPTED = 129;
public static final int ERROR_MMS_MESSAGE_NOT_SUPPORTED = 130;
public static final int ERROR_MMS_REPLY_CHARGING_ERROR = 131;
public static final int ERROR_MMS_ADDRESS_HIDING_NOT_SUPPORTED = 132;
public static final int ERROR_MMS_LACK_OF_PREPAID = 133;
public static final int ERROR_MMS_CAN_NOT_PERSIST = 134;
public static final int ERROR_MMS_NO_AVAILABLE_APN = 135;
public static final int ERROR_MMS_INVALID_MESSAGE_TO_SEND = 136;
public static final int ERROR_MMS_INVALID_MESSAGE_RECEIVED = 137;
public static final int ERROR_MMS_NO_CONFIGURATION = 138;
private static final int LAST = 138;
private final boolean mIsInjection;
private final int mErrorCode;
private final String mMessage;
private final long mBackoff;
public DataModelException(final int errorCode, final Exception innerException,
final long backoff, final boolean injection, final String message) {
// Since some of the exceptions passed in may not be serializable, only record message
// instead of setting inner exception for Exception class. Otherwise, we will get
// serialization issues when we pass ServerRequestException as intent extra later.
if (errorCode < FIRST || errorCode > LAST) {
throw new IllegalArgumentException("error code out of range: " + errorCode);
}
mIsInjection = injection;
mErrorCode = errorCode;
if (innerException != null) {
mMessage = innerException.getMessage() + " -- " +
(mIsInjection ? "[INJECTED] -- " : "") + message;
} else {
mMessage = (mIsInjection ? "[INJECTED] -- " : "") + message;
}
mBackoff = backoff;
}
public DataModelException(final int errorCode) {
this(errorCode, null, 0, false, null);
}
public DataModelException(final int errorCode, final Exception innerException) {
this(errorCode, innerException, 0, false, null);
}
public DataModelException(final int errorCode, final String message) {
this(errorCode, null, 0, false, message);
}
@Override
public String getMessage() {
return mMessage;
}
public int getErrorCode() {
return mErrorCode;
}
}
@@ -1102,29 +1102,6 @@ public abstract class MessageNotificationState extends NotificationState {
} }
} }
/*
private static void updateAlertStatusMessages(final long thresholdDeltaMs) {
// TODO may need this when supporting error notifications
final EsDatabaseHelper helper = EsDatabaseHelper.getDatabaseHelper();
final ContentValues values = new ContentValues();
final long nowMicros = System.currentTimeMillis() * 1000;
values.put(MessageColumns.ALERT_STATUS, "1");
final String selection =
MessageColumns.ALERT_STATUS + "=0 AND (" +
MessageColumns.STATUS + "=" + EsProvider.MESSAGE_STATUS_FAILED_TO_SEND + " OR (" +
MessageColumns.STATUS + "!=" + EsProvider.MESSAGE_STATUS_ON_SERVER + " AND " +
MessageColumns.TIMESTAMP + "+" + thresholdDeltaMs*1000 + "<" + nowMicros + ")) ";
final int updateCount = helper.getWritableDatabaseWrapper().update(
EsProvider.MESSAGES_TABLE,
values,
selection,
null);
if (updateCount > 0) {
EsConversationsData.notifyConversationsChanged();
}
}*/
static CharSequence applyWarningTextColor(final Context context, static CharSequence applyWarningTextColor(final Context context,
final CharSequence text) { final CharSequence text) {
if (text == null) { if (text == null) {
@@ -1166,7 +1143,6 @@ public abstract class MessageNotificationState extends NotificationState {
final ArrayList<Integer> failedMessages = new ArrayList<>(); final ArrayList<Integer> failedMessages = new ArrayList<>();
int cursorPosition = -1; int cursorPosition = -1;
final long when = 0;
messageDataCursor.moveToPosition(-1); messageDataCursor.moveToPosition(-1);
while (messageDataCursor.moveToNext()) { while (messageDataCursor.moveToNext()) {
@@ -1195,7 +1171,6 @@ public abstract class MessageNotificationState extends NotificationState {
CharSequence line1; CharSequence line1;
CharSequence line2; CharSequence line2;
final boolean isRichContent = false;
ConversationIdSet conversationIds = null; ConversationIdSet conversationIds = null;
PendingIntent destinationIntent; PendingIntent destinationIntent;
if (failedMessages.size() == 1) { if (failedMessages.size() == 1) {
@@ -1222,12 +1197,6 @@ public abstract class MessageNotificationState extends NotificationState {
} }
line1 = resources.getString(failureStringId); line1 = resources.getString(failureStringId);
line2 = failedMessgeSnippet; line2 = failedMessgeSnippet;
// Set rich text for non-SMS messages or MMS push notification messages
// which we generate locally with rich text
// TODO- fix this
// if (messageData.isMmsInd()) {
// isRichContent = true;
// }
} else { } else {
// We have notifications for multiple conversation, go to the conversation // We have notifications for multiple conversation, go to the conversation
// list. // list.
@@ -1265,29 +1234,18 @@ public abstract class MessageNotificationState extends NotificationState {
builder builder
.setContentTitle(line1) .setContentTitle(line1)
.setTicker(line1) .setTicker(line1)
.setWhen(when > 0 ? when : System.currentTimeMillis()) .setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.ic_failed_light) .setSmallIcon(R.drawable.ic_failed_light)
.setDeleteIntent(pendingIntentForDelete) .setDeleteIntent(pendingIntentForDelete)
.setContentIntent(destinationIntent) .setContentIntent(destinationIntent)
.setSound(UriUtil.getUriForResourceId(context, R.raw.message_failure)); .setSound(UriUtil.getUriForResourceId(context, R.raw.message_failure));
if (isRichContent && !TextUtils.isEmpty(line2)) { builder.setContentText(line2);
final NotificationCompat.InboxStyle inboxStyle =
new NotificationCompat.InboxStyle(builder);
if (line2 != null) {
inboxStyle.addLine(Html.fromHtml(line2.toString()));
}
builder.setStyle(inboxStyle);
} else {
builder.setContentText(line2);
}
if (builder != null) { notificationManager.notify(
notificationManager.notify( BugleNotifications.buildNotificationTag(
BugleNotifications.buildNotificationTag( PendingIntentConstants.MSG_SEND_ERROR, null),
PendingIntentConstants.MSG_SEND_ERROR, null), PendingIntentConstants.MSG_SEND_ERROR,
PendingIntentConstants.MSG_SEND_ERROR, builder.build());
builder.build());
}
} else { } else {
notificationManager.cancel( notificationManager.cancel(
BugleNotifications.buildNotificationTag( BugleNotifications.buildNotificationTag(
@@ -254,15 +254,6 @@ public class MessagingContentProvider extends ContentProvider {
@Override @Override
public Cursor query(@NonNull final Uri uri, final String[] projection, String selection, public Cursor query(@NonNull final Uri uri, final String[] projection, String selection,
final String[] selectionArgs, String sortOrder) { final String[] selectionArgs, String sortOrder) {
// Processes other than self are allowed to temporarily access the media
// scratch space; we grant uri read access on a case-by-case basis. Dialer app and
// contacts app would doQuery() on the vCard uri before trying to open the inputStream.
// There's nothing that we need to return for this uri so just No-Op.
//if (isMediaScratchSpaceUri(uri)) {
// return null;
//}
final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
String[] queryArgs = selectionArgs; String[] queryArgs = selectionArgs;
@@ -23,7 +23,6 @@ import android.os.Parcelable;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.DataModelException;
import com.android.messaging.datamodel.action.ActionMonitor.ActionCompletedListener; import com.android.messaging.datamodel.action.ActionMonitor.ActionCompletedListener;
import com.android.messaging.datamodel.action.ActionMonitor.ActionExecutedListener; import com.android.messaging.datamodel.action.ActionMonitor.ActionExecutedListener;
import com.android.messaging.util.LogUtil; import com.android.messaging.util.LogUtil;
@@ -96,11 +95,10 @@ public abstract class Action implements Parcelable {
/** /**
* Do work in a long running background worker thread. * Do work in a long running background worker thread.
* {@link #requestBackgroundWork} needs to be called for this method to * {@link #requestBackgroundWork} needs to be called for this method to
* be called. {@link #processBackgroundFailure} will be called on the Action service thread * be called.
* if this method throws {@link DataModelException}.
* @return response that is to be passed to {@link #processBackgroundResponse} * @return response that is to be passed to {@link #processBackgroundResponse}
*/ */
protected Bundle doBackgroundWork() throws DataModelException { protected Bundle doBackgroundWork() {
return null; return null;
} }
@@ -26,7 +26,6 @@ import androidx.core.app.JobIntentService;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.DataModelException;
import com.android.messaging.util.Assert; import com.android.messaging.util.Assert;
import com.android.messaging.util.LogUtil; import com.android.messaging.util.LogUtil;
import com.android.messaging.util.LoggingTimer; import com.android.messaging.util.LoggingTimer;
@@ -140,18 +139,9 @@ public class BackgroundWorkerService extends JobIntentService {
} catch (final Exception exception) { } catch (final Exception exception) {
final boolean retry = false; final boolean retry = false;
LogUtil.e(TAG, "Error in background worker", exception); LogUtil.e(TAG, "Error in background worker", exception);
if (!(exception instanceof DataModelException)) { Assert.fail("Unexpected error in background worker - abort");
// DataModelException is expected (sort-of) and handled in handleFailureFromWorker action.markBackgroundCompletionQueued();
// below, but other exceptions should crash ENG builds mHost.handleFailureFromBackgroundWorker(action, exception);
Assert.fail("Unexpected error in background worker - abort");
}
if (retry) {
action.markBackgroundWorkQueued();
startServiceWithAction(action, attempt + 1);
} else {
action.markBackgroundCompletionQueued();
mHost.handleFailureFromBackgroundWorker(action, exception);
}
} }
} }
} }
@@ -114,9 +114,6 @@ public class BugleActionToasts {
} }
} }
public static void onConversationDeleted() {
}
private static void showToast(final int messageResId) { private static void showToast(final int messageResId) {
ThreadUtil.getMainThreadHandler().post(() -> Toast.makeText(getApplicationContext(), ThreadUtil.getMainThreadHandler().post(() -> Toast.makeText(getApplicationContext(),
getApplicationContext().getString(messageResId), Toast.LENGTH_LONG).show()); getApplicationContext().getString(messageResId), Toast.LENGTH_LONG).show());
@@ -30,7 +30,6 @@ import com.android.messaging.Factory;
import com.android.messaging.datamodel.BugleDatabaseOperations; import com.android.messaging.datamodel.BugleDatabaseOperations;
import com.android.messaging.datamodel.BugleNotifications; import com.android.messaging.datamodel.BugleNotifications;
import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.DataModelException;
import com.android.messaging.datamodel.DatabaseHelper; import com.android.messaging.datamodel.DatabaseHelper;
import com.android.messaging.datamodel.DatabaseHelper.MessageColumns; import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
import com.android.messaging.datamodel.DatabaseWrapper; import com.android.messaging.datamodel.DatabaseWrapper;
@@ -71,7 +70,7 @@ public class DeleteConversationAction extends Action implements Parcelable {
// telephony database can sometimes be quite slow to delete conversations, so we delete from // telephony database can sometimes be quite slow to delete conversations, so we delete from
// the local DB first, notify the UI, and then delete from telephony. // the local DB first, notify the UI, and then delete from telephony.
@Override @Override
protected Bundle doBackgroundWork() throws DataModelException { protected Bundle doBackgroundWork() {
final DatabaseWrapper db = DataModel.get().getDatabase(); final DatabaseWrapper db = DataModel.get().getDatabase();
final String conversationId = actionParameters.getString(KEY_CONVERSATION_ID); final String conversationId = actionParameters.getString(KEY_CONVERSATION_ID);
@@ -85,8 +84,6 @@ public class DeleteConversationAction extends Action implements Parcelable {
LogUtil.i(TAG, "DeleteConversationAction: Deleted local conversation " LogUtil.i(TAG, "DeleteConversationAction: Deleted local conversation "
+ conversationId); + conversationId);
BugleActionToasts.onConversationDeleted();
// Remove notifications if necessary // Remove notifications if necessary
BugleNotifications.update(true /* silent */, null /* conversationId */, BugleNotifications.update(true /* silent */, null /* conversationId */,
BugleNotifications.UPDATE_MESSAGES); BugleNotifications.UPDATE_MESSAGES);
@@ -34,7 +34,6 @@ import com.android.messaging.Factory;
import com.android.messaging.datamodel.BugleDatabaseOperations; import com.android.messaging.datamodel.BugleDatabaseOperations;
import com.android.messaging.datamodel.BugleNotifications; import com.android.messaging.datamodel.BugleNotifications;
import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.DataModelException;
import com.android.messaging.datamodel.DatabaseWrapper; import com.android.messaging.datamodel.DatabaseWrapper;
import com.android.messaging.datamodel.MessagingContentProvider; import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.MmsFileProvider; import com.android.messaging.datamodel.MmsFileProvider;
@@ -211,7 +210,7 @@ public class ProcessDownloadedMmsAction extends Action {
} }
@Override @Override
protected Bundle doBackgroundWork() throws DataModelException { protected Bundle doBackgroundWork() {
final Context context = Factory.get().getApplicationContext(); final Context context = Factory.get().getApplicationContext();
final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
final String messageId = actionParameters.getString(KEY_MESSAGE_ID); final String messageId = actionParameters.getString(KEY_MESSAGE_ID);
@@ -28,7 +28,6 @@ import com.android.messaging.Factory;
import com.android.messaging.datamodel.BugleDatabaseOperations; import com.android.messaging.datamodel.BugleDatabaseOperations;
import com.android.messaging.datamodel.BugleNotifications; import com.android.messaging.datamodel.BugleNotifications;
import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.DataModelException;
import com.android.messaging.datamodel.DatabaseWrapper; import com.android.messaging.datamodel.DatabaseWrapper;
import com.android.messaging.datamodel.MessagingContentProvider; import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.SyncManager; import com.android.messaging.datamodel.SyncManager;
@@ -161,7 +160,7 @@ public class ReceiveMmsMessageAction extends Action implements Parcelable {
} }
@Override @Override
protected Bundle doBackgroundWork() throws DataModelException { protected Bundle doBackgroundWork() {
final Context context = Factory.get().getApplicationContext(); final Context context = Factory.get().getApplicationContext();
final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID); final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_ID);
final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID); final String transactionId = actionParameters.getString(KEY_TRANSACTION_ID);
@@ -55,10 +55,7 @@ import com.android.messaging.util.PhoneUtils;
import com.android.messaging.widget.WidgetConversationProvider; import com.android.messaging.widget.WidgetConversationProvider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
public class ConversationData extends BindableData { public class ConversationData extends BindableData {
@@ -67,40 +64,6 @@ public class ConversationData extends BindableData {
private static final long LAST_MESSAGE_TIMESTAMP_NaN = -1; private static final long LAST_MESSAGE_TIMESTAMP_NaN = -1;
private static final int MESSAGE_COUNT_NaN = -1; private static final int MESSAGE_COUNT_NaN = -1;
/**
* Takes a conversation id and a list of message ids and computes the positions
* for each message.
*/
public List<Integer> getPositions(final String conversationId, final List<Long> ids) {
final ArrayList<Integer> result = new ArrayList<>();
if (ids.isEmpty()) {
return result;
}
final Cursor c = new ConversationData.ReversedCursor(
DataModel.get().getDatabase().rawQuery(
ConversationMessageData.getConversationMessageIdsQuerySql(),
new String [] { conversationId }));
if (c != null) {
try {
final Set<Long> idsSet = new HashSet<>(ids);
if (c.moveToLast()) {
do {
final long messageId = c.getLong(0);
if (idsSet.contains(messageId)) {
result.add(c.getPosition());
}
} while (c.moveToPrevious());
}
} finally {
c.close();
}
}
Collections.sort(result);
return result;
}
public interface ConversationDataListener { public interface ConversationDataListener {
void onConversationMessagesCursorUpdated(ConversationData data, Cursor cursor, void onConversationMessagesCursorUpdated(ConversationData data, Cursor cursor,
@Nullable ConversationMessageData newestMessage, boolean isSync); @Nullable ConversationMessageData newestMessage, boolean isSync);
@@ -477,10 +477,6 @@ public class ConversationMessageData {
return mProtocol == (MessageData.PROTOCOL_SMS); return mProtocol == (MessageData.PROTOCOL_SMS);
} }
final int getProtocol() {
return mProtocol;
}
public final int getStatus() { public final int getStatus() {
return mStatus; return mStatus;
} }
@@ -639,14 +639,6 @@ public class MessageData implements Parcelable {
|| mProtocol == MessageData.PROTOCOL_MMS_PUSH_NOTIFICATION; || mProtocol == MessageData.PROTOCOL_MMS_PUSH_NOTIFICATION;
} }
public static boolean getIsMmsNotification(final int protocol) {
return (protocol == MessageData.PROTOCOL_MMS_PUSH_NOTIFICATION);
}
public final boolean getIsMmsNotification() {
return getIsMmsNotification(mProtocol);
}
public static boolean getIsSms(final int protocol) { public static boolean getIsSms(final int protocol) {
return protocol == (MessageData.PROTOCOL_SMS); return protocol == (MessageData.PROTOCOL_SMS);
} }
@@ -801,10 +793,6 @@ public class MessageData implements Parcelable {
} }
} }
public final void setRetryStartTimestamp(final long timestamp) {
mRetryStartTimestamp = timestamp;
}
public final void setRawTelephonyStatus(final int rawStatus) { public final void setRawTelephonyStatus(final int rawStatus) {
mRawStatus = rawStatus; mRawStatus = rawStatus;
} }
@@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -25,7 +26,6 @@ import com.android.messaging.util.ImageUtils;
import com.android.messaging.util.UriUtil; import com.android.messaging.util.UriUtil;
public class AvatarRequestDescriptor extends UriImageRequestDescriptor { public class AvatarRequestDescriptor extends UriImageRequestDescriptor {
final boolean isWearBackground;
public AvatarRequestDescriptor(final Uri uri, final int desiredWidth, public AvatarRequestDescriptor(final Uri uri, final int desiredWidth,
final int desiredHeight) { final int desiredHeight) {
@@ -45,7 +45,6 @@ public class AvatarRequestDescriptor extends UriImageRequestDescriptor {
ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */); ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */);
Assert.isTrue(uri == null || UriUtil.isLocalResourceUri(uri) || Assert.isTrue(uri == null || UriUtil.isLocalResourceUri(uri) ||
AvatarUriUtil.isAvatarUri(uri)); AvatarUriUtil.isAvatarUri(uri));
this.isWearBackground = isWearBackground;
} }
@Override @Override
@@ -79,12 +79,10 @@ public class NetworkUriImageRequest<D extends UriImageRequestDescriptor> extends
return false; return false;
} }
@SuppressWarnings("deprecation")
@Override @Override
public Bitmap loadBitmapInternal() throws IOException { public Bitmap loadBitmapInternal() {
Assert.isNotMainThread(); Assert.isNotMainThread();
InputStream inputStream = null;
Bitmap bitmap = null; Bitmap bitmap = null;
HttpURLConnection connection = null; HttpURLConnection connection = null;
try { try {
@@ -109,9 +107,6 @@ public class NetworkUriImageRequest<D extends UriImageRequestDescriptor> extends
"IOException trying to get inputStream for image with url: " "IOException trying to get inputStream for image with url: "
+ mDescriptor.uri, e); + mDescriptor.uri, e);
} finally { } finally {
if (inputStream != null) {
inputStream.close();
}
if (connection != null) { if (connection != null) {
connection.disconnect(); connection.disconnect();
} }
+2 -733
View File
@@ -16,8 +16,6 @@
package com.android.messaging.mmslib; package com.android.messaging.mmslib;
import android.app.DownloadManager;
import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.provider.BaseColumns; import android.provider.BaseColumns;
@@ -39,49 +37,8 @@ public final class Downloads {
* @hide * @hide
*/ */
public static final class Impl implements BaseColumns { public static final class Impl implements BaseColumns {
private Impl() {} private Impl() {
}
/**
* The permission to access the download manager
*/
public static final String PERMISSION_ACCESS = "android.permission.ACCESS_DOWNLOAD_MANAGER";
/**
* The permission to access the download manager's advanced functions
*/
public static final String PERMISSION_ACCESS_ADVANCED =
"android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED";
/**
* The permission to access the all the downloads in the manager.
*/
public static final String PERMISSION_ACCESS_ALL =
"android.permission.ACCESS_ALL_DOWNLOADS";
/**
* The permission to directly access the download manager's cache
* directory
*/
public static final String PERMISSION_CACHE = "android.permission.ACCESS_CACHE_FILESYSTEM";
/**
* The permission to send broadcasts on download completion
*/
public static final String PERMISSION_SEND_INTENTS =
"android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS";
/**
* The permission to download files to the cache partition that won't be automatically
* purged when space is needed.
*/
public static final String PERMISSION_CACHE_NON_PURGEABLE =
"android.permission.DOWNLOAD_CACHE_NON_PURGEABLE";
/**
* The permission to download files without any system notification being shown.
*/
public static final String PERMISSION_NO_NOTIFICATION =
"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION";
/** /**
* The content:// URI to access downloads owned by the caller's UID. * The content:// URI to access downloads owned by the caller's UID.
@@ -89,521 +46,6 @@ public final class Downloads {
public static final Uri CONTENT_URI = public static final Uri CONTENT_URI =
Uri.parse("content://downloads/my_downloads"); Uri.parse("content://downloads/my_downloads");
/**
* The content URI for accessing all downloads across all UIDs (requires the
* ACCESS_ALL_DOWNLOADS permission).
*/
public static final Uri ALL_DOWNLOADS_CONTENT_URI =
Uri.parse("content://downloads/all_downloads");
/** URI segment to access a publicly accessible downloaded file */
public static final String PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT = "public_downloads";
/**
* The content URI for accessing publicly accessible downloads (i.e., it requires no
* permissions to access this downloaded file)
*/
public static final Uri PUBLICLY_ACCESSIBLE_DOWNLOADS_URI =
Uri.parse("content://downloads/" + PUBLICLY_ACCESSIBLE_DOWNLOADS_URI_SEGMENT);
/**
* Broadcast Action: this is sent by the download manager to the app
* that had initiated a download when that download completes. The
* download's content: uri is specified in the intent's data.
*/
public static final String ACTION_DOWNLOAD_COMPLETED =
"android.intent.action.DOWNLOAD_COMPLETED";
/**
* Broadcast Action: this is sent by the download manager to the app
* that had initiated a download when the user selects the notification
* associated with that download. The download's content: uri is specified
* in the intent's data if the click is associated with a single download,
* or Downloads.CONTENT_URI if the notification is associated with
* multiple downloads.
* Note: this is not currently sent for downloads that have completed
* successfully.
*/
public static final String ACTION_NOTIFICATION_CLICKED =
"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
/**
* The name of the column containing the URI of the data being downloaded.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_URI = "uri";
/**
* The name of the column containing application-specific data.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
*/
public static final String COLUMN_APP_DATA = "entity";
/**
* The name of the column containing the flags that indicates whether
* the initiating application is capable of verifying the integrity of
* the downloaded file. When this flag is set, the download manager
* performs downloads and reports success even in some situations where
* it can't guarantee that the download has completed (e.g. when doing
* a byte-range request without an ETag, or when it can't determine
* whether a download fully completed).
* <P>Type: BOOLEAN</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_NO_INTEGRITY = "no_integrity";
/**
* The name of the column containing the filename that the initiating
* application recommends. When possible, the download manager will attempt
* to use this filename, or a variation, as the actual name for the file.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_FILE_NAME_HINT = "hint";
/**
* The name of the column containing the filename where the downloaded data
* was actually stored.
* <P>Type: TEXT</P>
* <P>Owner can Read</P>
*/
public static final String _DATA = "_data";
/**
* The name of the column containing the MIME type of the downloaded data.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_MIME_TYPE = "mimetype";
/**
* The name of the column containing the flag that controls the destination
* of the download. See the DESTINATION_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_DESTINATION = "destination";
/**
* The name of the column containing the flags that controls whether the
* download is displayed by the UI. See the VISIBILITY_* constants for
* a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Init/Read/Write</P>
*/
public static final String COLUMN_VISIBILITY = "visibility";
/**
* The name of the column containing the current control state of the download.
* Applications can write to this to control (pause/resume) the download.
* the CONTROL_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
public static final String COLUMN_CONTROL = "control";
/**
* The name of the column containing the current status of the download.
* Applications can read this to follow the progress of each download. See
* the STATUS_* constants for a list of legal values.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
public static final String COLUMN_STATUS = "status";
/**
* The name of the column containing the date at which some interesting
* status changed in the download. Stored as a System.currentTimeMillis()
* value.
* <P>Type: BIGINT</P>
* <P>Owner can Read</P>
*/
public static final String COLUMN_LAST_MODIFICATION = "lastmod";
/**
* The name of the column containing the package name of the application
* that initiating the download. The download manager will send
* notifications to a component in this package when the download completes.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
/**
* The name of the column containing the component name of the class that
* will receive notifications associated with the download. The
* package/class combination is passed to
* Intent.setClassName(String,String).
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
/**
* If extras are specified when requesting a download they will be provided in the intent
* that is sent to the specified class and package when a download has finished.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
/**
* The name of the column contain the values of the cookie to be used for
* the download. This is used directly as the value for the Cookie: HTTP
* header that gets sent with the request.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_COOKIE_DATA = "cookiedata";
/**
* The name of the column containing the user agent that the initiating
* application wants the download manager to use for this download.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_USER_AGENT = "useragent";
/**
* The name of the column containing the referer (sic) that the initiating
* application wants the download manager to use for this download.
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_REFERER = "referer";
/**
* The name of the column containing the total size of the file being
* downloaded.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
public static final String COLUMN_TOTAL_BYTES = "total_bytes";
/**
* The name of the column containing the size of the part of the file that
* has been downloaded so far.
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
public static final String COLUMN_CURRENT_BYTES = "current_bytes";
/**
* The name of the column where the initiating application can provide the
* UID of another application that is allowed to access this download. If
* multiple applications share the same UID, all those applications will be
* allowed to access this download. This column can be updated after the
* download is initiated. This requires the permission
* android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED.
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
*/
public static final String COLUMN_OTHER_UID = "otheruid";
/**
* The name of the column where the initiating application can provided the
* title of this download. The title will be displayed ito the user in the
* list of downloads.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
*/
public static final String COLUMN_TITLE = "title";
/**
* The name of the column where the initiating application can provide the
* description of this download. The description will be displayed to the
* user in the list of downloads.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
*/
public static final String COLUMN_DESCRIPTION = "description";
/**
* The name of the column indicating whether the download was requesting through the public
* API. This controls some differences in behavior.
* <P>Type: BOOLEAN</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_IS_PUBLIC_API = "is_public_api";
/**
* The name of the column holding a bitmask of allowed network types. This is only used for
* public API downloads.
* <P>Type: INTEGER</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_ALLOWED_NETWORK_TYPES = "allowed_network_types";
/**
* The name of the column indicating whether roaming connections can be used. This is only
* used for public API downloads.
* <P>Type: BOOLEAN</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_ALLOW_ROAMING = "allow_roaming";
/**
* The name of the column indicating whether metered connections can be used. This is only
* used for public API downloads.
* <P>Type: BOOLEAN</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_ALLOW_METERED = "allow_metered";
/**
* Whether or not this download should be displayed in the system's Downloads UI. Defaults
* to true.
* <P>Type: INTEGER</P>
* <P>Owner can Init/Read</P>
*/
public static final String COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI = "is_visible_in_downloads_ui";
/**
* If true, the user has confirmed that this download can proceed over the mobile network
* even though it exceeds the recommended maximum size.
* <P>Type: BOOLEAN</P>
*/
public static final String COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT =
"bypass_recommended_size_limit";
/**
* Set to true if this download is deleted. It is completely removed from the database
* when MediaProvider database also deletes the metadata asociated with this downloaded
* file.
* <P>Type: BOOLEAN</P>
* <P>Owner can Read</P>
*/
public static final String COLUMN_DELETED = "deleted";
/**
* The URI to the corresponding entry in MediaProvider for this downloaded entry. It is
* used to delete the entries from MediaProvider database when it is deleted from the
* downloaded list.
* <P>Type: TEXT</P>
* <P>Owner can Read</P>
*/
public static final String COLUMN_MEDIAPROVIDER_URI = "mediaprovider_uri";
/**
* The column that is used to remember whether the media scanner was invoked.
* It can take the values: null or 0(not scanned), 1(scanned), 2 (not scannable).
* <P>Type: TEXT</P>
*/
public static final String COLUMN_MEDIA_SCANNED = "scanned";
/**
* The column with errorMsg for a failed downloaded.
* Used only for debugging purposes.
* <P>Type: TEXT</P>
*/
public static final String COLUMN_ERROR_MSG = "errorMsg";
/**
* This column stores the source of the last update to this row.
* This column is only for internal use.
* Valid values are indicated by LAST_UPDATESRC_* constants.
* <P>Type: INT</P>
*/
public static final String COLUMN_LAST_UPDATESRC = "lastUpdateSrc";
/** The column that is used to count retries */
public static final String COLUMN_FAILED_CONNECTIONS = "numfailed";
/**
* default value for {@link #COLUMN_LAST_UPDATESRC}.
* This value is used when this column's value is not relevant.
*/
public static final int LAST_UPDATESRC_NOT_RELEVANT = 0;
/**
* One of the values taken by {@link #COLUMN_LAST_UPDATESRC}.
* This value is used when the update is NOT to be relayed to the DownloadService
* (and thus spare DownloadService from scanning the database when this change occurs)
*/
public static final int LAST_UPDATESRC_DONT_NOTIFY_DOWNLOADSVC = 1;
/*
* Lists the destinations that an application can specify for a download.
*/
/**
* This download will be saved to the external storage. This is the
* default behavior, and should be used for any file that the user
* can freely access, copy, delete. Even with that destination,
* unencrypted DRM files are saved in secure internal storage.
* Downloads to the external destination only write files for which
* there is a registered handler. The resulting files are accessible
* by filename to all applications.
*/
public static final int DESTINATION_EXTERNAL = 0;
/**
* This download will be saved to the download manager's private
* partition. This is the behavior used by applications that want to
* download private files that are used and deleted soon after they
* get downloaded. All file types are allowed, and only the initiating
* application can access the file (indirectly through a content
* provider). This requires the
* android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED permission.
*/
public static final int DESTINATION_CACHE_PARTITION = 1;
/**
* This download will be saved to the download manager's private
* partition and will be purged as necessary to make space. This is
* for private files (similar to CACHE_PARTITION) that aren't deleted
* immediately after they are used, and are kept around by the download
* manager as long as space is available.
*/
public static final int DESTINATION_CACHE_PARTITION_PURGEABLE = 2;
/**
* This download will be saved to the download manager's private
* partition, as with DESTINATION_CACHE_PARTITION, but the download
* will not proceed if the user is on a roaming data connection.
*/
public static final int DESTINATION_CACHE_PARTITION_NOROAMING = 3;
/**
* This download will be saved to the location given by the file URI in
* {@link #COLUMN_FILE_NAME_HINT}.
*/
public static final int DESTINATION_FILE_URI = 4;
/**
* This download will be saved to the system cache ("/cache")
* partition. This option is only used by system apps and so it requires
* android.permission.ACCESS_CACHE_FILESYSTEM permission.
*/
public static final int DESTINATION_SYSTEMCACHE_PARTITION = 5;
/**
* This download was completed by the caller (i.e., NOT downloadmanager)
* and caller wants to have this download displayed in Downloads App.
*/
public static final int DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD = 6;
/**
* This download is allowed to run.
*/
public static final int CONTROL_RUN = 0;
/**
* This download must pause at the first opportunity.
*/
public static final int CONTROL_PAUSED = 1;
/*
* Lists the states that the download manager can set on a download
* to notify applications of the download progress.
* The codes follow the HTTP families:<br>
* 1xx: informational<br>
* 2xx: success<br>
* 3xx: redirects (not used by the download manager)<br>
* 4xx: client errors<br>
* 5xx: server errors
*/
/**
* Returns whether the status is informational (i.e. 1xx).
*/
public static boolean isStatusInformational(int status) {
return (status >= 100 && status < 200);
}
/**
* Returns whether the status is a success (i.e. 2xx).
*/
public static boolean isStatusSuccess(int status) {
return (status >= 200 && status < 300);
}
/**
* Returns whether the status is an error (i.e. 4xx or 5xx).
*/
public static boolean isStatusError(int status) {
return (status >= 400 && status < 600);
}
/**
* Returns whether the status is a client error (i.e. 4xx).
*/
public static boolean isStatusClientError(int status) {
return (status >= 400 && status < 500);
}
/**
* Returns whether the status is a server error (i.e. 5xx).
*/
public static boolean isStatusServerError(int status) {
return (status >= 500 && status < 600);
}
/**
* this method determines if a notification should be displayed for a
* given {@link #COLUMN_VISIBILITY} value
* @param visibility the value of {@link #COLUMN_VISIBILITY}.
* @return true if the notification should be displayed. false otherwise.
*/
public static boolean isNotificationToBeDisplayed(int visibility) {
return visibility == DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED ||
visibility == DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION;
}
/**
* Returns whether the download has completed (either with success or
* error).
*/
public static boolean isStatusCompleted(int status) {
return (status >= 200 && status < 300) || (status >= 400 && status < 600);
}
/**
* This download hasn't stated yet
*/
public static final int STATUS_PENDING = 190;
/**
* This download has started
*/
public static final int STATUS_RUNNING = 192;
/**
* This download has been paused by the owning app.
*/
public static final int STATUS_PAUSED_BY_APP = 193;
/**
* This download encountered some network error and is waiting before retrying the request.
*/
public static final int STATUS_WAITING_TO_RETRY = 194;
/**
* This download is waiting for network connectivity to proceed.
*/
public static final int STATUS_WAITING_FOR_NETWORK = 195;
/**
* This download exceeded a size limit for mobile networks and is waiting for a Wi-Fi
* connection to proceed.
*/
public static final int STATUS_QUEUED_FOR_WIFI = 196;
/**
* This download couldn't be completed due to insufficient storage
* space. Typically, this is because the SD card is full.
*/
public static final int STATUS_INSUFFICIENT_SPACE_ERROR = 198;
/**
* This download couldn't be completed because no external storage
* device was found. Typically, this is because the SD card is not
* mounted.
*/
public static final int STATUS_DEVICE_NOT_FOUND_ERROR = 199;
/** /**
* This download has successfully completed. * This download has successfully completed.
* Warning: there might be other status values that indicate success * Warning: there might be other status values that indicate success
@@ -612,56 +54,12 @@ public final class Downloads {
*/ */
public static final int STATUS_SUCCESS = 200; public static final int STATUS_SUCCESS = 200;
/**
* This request couldn't be parsed. This is also used when processing
* requests with unknown/unsupported URI schemes.
*/
public static final int STATUS_BAD_REQUEST = 400;
/** /**
* This download can't be performed because the content type cannot be * This download can't be performed because the content type cannot be
* handled. * handled.
*/ */
public static final int STATUS_NOT_ACCEPTABLE = 406; public static final int STATUS_NOT_ACCEPTABLE = 406;
/**
* This download cannot be performed because the length cannot be
* determined accurately. This is the code for the HTTP error "Length
* Required", which is typically used when making requests that require
* a content length but don't have one, and it is also used in the
* client when a response is received whose length cannot be determined
* accurately (therefore making it impossible to know when a download
* completes).
*/
public static final int STATUS_LENGTH_REQUIRED = 411;
/**
* This download was interrupted and cannot be resumed.
* This is the code for the HTTP error "Precondition Failed", and it is
* also used in situations where the client doesn't have an ETag at all.
*/
public static final int STATUS_PRECONDITION_FAILED = 412;
/**
* The lowest-valued error status that is not an actual HTTP status code.
*/
public static final int MIN_ARTIFICIAL_ERROR_STATUS = 488;
/**
* The requested destination file already exists.
*/
public static final int STATUS_FILE_ALREADY_EXISTS_ERROR = 488;
/**
* Some possibly transient error occurred, but we can't resume the download.
*/
public static final int STATUS_CANNOT_RESUME = 489;
/**
* This download was canceled
*/
public static final int STATUS_CANCELED = 490;
/** /**
* This download has completed with an error. * This download has completed with an error.
* Warning: there will be other status values that indicate errors in * Warning: there will be other status values that indicate errors in
@@ -672,136 +70,7 @@ public final class Downloads {
/** /**
* This download couldn't be completed because of a storage issue. * This download couldn't be completed because of a storage issue.
* Typically, that's because the filesystem is missing or full. * Typically, that's because the filesystem is missing or full.
* Use the more specific {@link #STATUS_INSUFFICIENT_SPACE_ERROR}
* and {@link #STATUS_DEVICE_NOT_FOUND_ERROR} when appropriate.
*/ */
public static final int STATUS_FILE_ERROR = 492; public static final int STATUS_FILE_ERROR = 492;
/**
* This download couldn't be completed because of an HTTP
* redirect response that the download manager couldn't
* handle.
*/
public static final int STATUS_UNHANDLED_REDIRECT = 493;
/**
* This download couldn't be completed because of an
* unspecified unhandled HTTP code.
*/
public static final int STATUS_UNHANDLED_HTTP_CODE = 494;
/**
* This download couldn't be completed because of an
* error receiving or processing data at the HTTP level.
*/
public static final int STATUS_HTTP_DATA_ERROR = 495;
/**
* This download couldn't be completed because of an
* HttpException while setting up the request.
*/
public static final int STATUS_HTTP_EXCEPTION = 496;
/**
* This download couldn't be completed because there were
* too many redirects.
*/
public static final int STATUS_TOO_MANY_REDIRECTS = 497;
/**
* This download has failed because requesting application has been
* blocked by {@link NetworkPolicyManager}.
*
* @hide
* @deprecated since behavior now uses
* {@link #STATUS_WAITING_FOR_NETWORK}
*/
@Deprecated
public static final int STATUS_BLOCKED = 498;
/** {@hide} */
public static String statusToString(int status) {
switch (status) {
case STATUS_PENDING: return "PENDING";
case STATUS_RUNNING: return "RUNNING";
case STATUS_PAUSED_BY_APP: return "PAUSED_BY_APP";
case STATUS_WAITING_TO_RETRY: return "WAITING_TO_RETRY";
case STATUS_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK";
case STATUS_QUEUED_FOR_WIFI: return "QUEUED_FOR_WIFI";
case STATUS_INSUFFICIENT_SPACE_ERROR: return "INSUFFICIENT_SPACE_ERROR";
case STATUS_DEVICE_NOT_FOUND_ERROR: return "DEVICE_NOT_FOUND_ERROR";
case STATUS_SUCCESS: return "SUCCESS";
case STATUS_BAD_REQUEST: return "BAD_REQUEST";
case STATUS_NOT_ACCEPTABLE: return "NOT_ACCEPTABLE";
case STATUS_LENGTH_REQUIRED: return "LENGTH_REQUIRED";
case STATUS_PRECONDITION_FAILED: return "PRECONDITION_FAILED";
case STATUS_FILE_ALREADY_EXISTS_ERROR: return "FILE_ALREADY_EXISTS_ERROR";
case STATUS_CANNOT_RESUME: return "CANNOT_RESUME";
case STATUS_CANCELED: return "CANCELED";
case STATUS_UNKNOWN_ERROR: return "UNKNOWN_ERROR";
case STATUS_FILE_ERROR: return "FILE_ERROR";
case STATUS_UNHANDLED_REDIRECT: return "UNHANDLED_REDIRECT";
case STATUS_UNHANDLED_HTTP_CODE: return "UNHANDLED_HTTP_CODE";
case STATUS_HTTP_DATA_ERROR: return "HTTP_DATA_ERROR";
case STATUS_HTTP_EXCEPTION: return "HTTP_EXCEPTION";
case STATUS_TOO_MANY_REDIRECTS: return "TOO_MANY_REDIRECTS";
case STATUS_BLOCKED: return "BLOCKED";
default: return Integer.toString(status);
}
}
/**
* This download is visible but only shows in the notifications
* while it's in progress.
*/
public static final int VISIBILITY_VISIBLE = DownloadManager.Request.VISIBILITY_VISIBLE;
/**
* This download is visible and shows in the notifications while
* in progress and after completion.
*/
public static final int VISIBILITY_VISIBLE_NOTIFY_COMPLETED =
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
/**
* This download doesn't show in the UI or in the notifications.
*/
public static final int VISIBILITY_HIDDEN = DownloadManager.Request.VISIBILITY_HIDDEN;
/**
* Constants related to HTTP request headers associated with each download.
*/
public static class RequestHeaders {
public static final String HEADERS_DB_TABLE = "request_headers";
public static final String COLUMN_DOWNLOAD_ID = "download_id";
public static final String COLUMN_HEADER = "header";
public static final String COLUMN_VALUE = "value";
/**
* Path segment to add to a download URI to retrieve request headers
*/
public static final String URI_SEGMENT = "headers";
/**
* Prefix for ContentValues keys that contain HTTP header lines, to be passed to
* DownloadProvider.insert().
*/
public static final String INSERT_KEY_PREFIX = "http_header_";
}
}
/**
* Query where clause for general querying.
*/
private static final String QUERY_WHERE_CLAUSE = Impl.COLUMN_NOTIFICATION_PACKAGE + "=? AND "
+ Impl.COLUMN_NOTIFICATION_CLASS + "=?";
/**
* Delete all the downloads for a package/class pair.
*/
public static final void removeAllDownloadsByPackage(
Context context, String notificationPackage, String notificationClass) {
context.getContentResolver().delete(Impl.CONTENT_URI, QUERY_WHERE_CLAUSE,
new String[] { notificationPackage, notificationClass });
} }
} }
@@ -26,7 +26,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
/** /**
* Encoded-string-value = Text-string | Value-length Char-set Text-string * Encoded-string-value = Text-string | Value-length Char-set Text-string
@@ -239,43 +238,6 @@ public class EncodedStringValue implements Cloneable {
return ret; return ret;
} }
/**
* Extract an EncodedStringValue[] from a given String.
*/
public static EncodedStringValue[] extract(String src) {
String[] values = src.split(";");
ArrayList<EncodedStringValue> list = new ArrayList<>();
for (int i = 0; i < values.length; i++) {
if (values[i].length() > 0) {
list.add(new EncodedStringValue(values[i]));
}
}
int len = list.size();
if (len > 0) {
return list.toArray(new EncodedStringValue[len]);
} else {
return null;
}
}
/**
* Concatenate an EncodedStringValue[] into a single String.
*/
public static String concat(EncodedStringValue[] addr) {
StringBuilder sb = new StringBuilder();
int maxIndex = addr.length - 1;
for (int i = 0; i <= maxIndex; i++) {
sb.append(addr[i].getString());
if (i < maxIndex) {
sb.append(";");
}
}
return sb.toString();
}
public static EncodedStringValue copy(EncodedStringValue value) { public static EncodedStringValue copy(EncodedStringValue value) {
if (value == null) { if (value == null) {
return null; return null;
@@ -207,13 +207,6 @@ public class PduParser {
// or "application/vnd.wap.multipart.related" // or "application/vnd.wap.multipart.related"
// or "application/vnd.wap.multipart.alternative" // or "application/vnd.wap.multipart.alternative"
return retrieveConf; return retrieveConf;
} else if (ctTypeStr.equals(ContentType.MMS_MULTIPART_ALTERNATIVE)) {
// "application/vnd.wap.multipart.alternative"
// should take only the first part.
PduPart firstPart = mBody.getPart(0);
mBody.removeAll();
mBody.addPart(0, firstPart);
return retrieveConf;
} }
return null; return null;
case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
@@ -23,7 +23,6 @@ import android.content.ContentUris;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.net.Uri; import android.net.Uri;
import android.provider.MediaStore; import android.provider.MediaStore;
@@ -77,21 +76,6 @@ public class PduPersister {
public static final String TEMPORARY_DRM_OBJECT_URI = public static final String TEMPORARY_DRM_OBJECT_URI =
"content://mms/" + Long.MAX_VALUE + "/part"; "content://mms/" + Long.MAX_VALUE + "/part";
/**
* Indicate that we transiently failed to process a MM.
*/
public static final int PROC_STATUS_TRANSIENT_FAILURE = 1;
/**
* Indicate that we permanently failed to process a MM.
*/
public static final int PROC_STATUS_PERMANENTLY_FAILURE = 2;
/**
* Indicate that we have successfully processed a MM.
*/
public static final int PROC_STATUS_COMPLETED = 3;
public static final String BEGIN_VCARD = "BEGIN:VCARD"; public static final String BEGIN_VCARD = "BEGIN:VCARD";
private static PduPersister sPersister; private static PduPersister sPersister;
@@ -1046,244 +1030,6 @@ public class PduPersister {
return path; return path;
} }
private void updateAddress(
final long msgId, final int type, final EncodedStringValue[] array) {
// Delete old address information and then insert new ones.
SqliteWrapper.delete(mContext, mContentResolver,
Uri.parse("content://mms/" + msgId + "/addr"),
Addr.TYPE + "=" + type, null);
persistAddress(msgId, type, array);
}
/**
* Update headers of a SendReq.
*
* @param uri The PDU which need to be updated.
* @param pdu New headers.
* @throws MmsException Bad URI or updating failed.
*/
public void updateHeaders(final Uri uri, final SendReq sendReq) {
synchronized (PDU_CACHE_INSTANCE) {
// If the cache item is getting updated, wait until it's done updating before
// purging it.
if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
if (LOCAL_LOGV) {
LogUtil.v(TAG, "updateHeaders: " + uri + " blocked by isUpdating()");
}
try {
PDU_CACHE_INSTANCE.wait();
} catch (final InterruptedException e) {
Log.e(TAG, "updateHeaders: ", e);
}
}
}
PDU_CACHE_INSTANCE.purge(uri);
final ContentValues values = new ContentValues(10);
final byte[] contentType = sendReq.getContentType();
if (contentType != null) {
values.put(Mms.CONTENT_TYPE, toIsoString(contentType));
}
final long date = sendReq.getDate();
if (date != -1) {
values.put(Mms.DATE, date);
}
final int deliveryReport = sendReq.getDeliveryReport();
if (deliveryReport != 0) {
values.put(Mms.DELIVERY_REPORT, deliveryReport);
}
final long expiry = sendReq.getExpiry();
if (expiry != -1) {
values.put(Mms.EXPIRY, expiry);
}
final byte[] msgClass = sendReq.getMessageClass();
if (msgClass != null) {
values.put(Mms.MESSAGE_CLASS, toIsoString(msgClass));
}
final int priority = sendReq.getPriority();
if (priority != 0) {
values.put(Mms.PRIORITY, priority);
}
final int readReport = sendReq.getReadReport();
if (readReport != 0) {
values.put(Mms.READ_REPORT, readReport);
}
final byte[] transId = sendReq.getTransactionId();
if (transId != null) {
values.put(Mms.TRANSACTION_ID, toIsoString(transId));
}
final EncodedStringValue subject = sendReq.getSubject();
if (subject != null) {
values.put(Mms.SUBJECT, toIsoString(subject.getTextString()));
values.put(Mms.SUBJECT_CHARSET, subject.getCharacterSet());
} else {
values.put(Mms.SUBJECT, "");
}
final long messageSize = sendReq.getMessageSize();
if (messageSize > 0) {
values.put(Mms.MESSAGE_SIZE, messageSize);
}
final PduHeaders headers = sendReq.getPduHeaders();
final HashSet<String> recipients = new HashSet<>();
for (final int addrType : ADDRESS_FIELDS) {
EncodedStringValue[] array = null;
if (addrType == PduHeaders.FROM) {
final EncodedStringValue v = headers.getEncodedStringValue(addrType);
if (v != null) {
array = new EncodedStringValue[1];
array[0] = v;
}
} else {
array = headers.getEncodedStringValues(addrType);
}
if (array != null) {
final long msgId = ContentUris.parseId(uri);
updateAddress(msgId, addrType, array);
if (addrType == PduHeaders.TO) {
for (final EncodedStringValue v : array) {
if (v != null) {
recipients.add(v.getString());
}
}
}
}
}
if (!recipients.isEmpty()) {
final long threadId = MmsSmsUtils.Threads.getOrCreateThreadId(mContext, recipients);
values.put(Mms.THREAD_ID, threadId);
}
SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
}
private void updatePart(final Uri uri, final PduPart part,
final Map<Uri, InputStream> preOpenedFiles)
throws MmsException {
final ContentValues values = new ContentValues(7);
final int charset = part.getCharset();
if (charset != 0) {
values.put(Part.CHARSET, charset);
}
String contentType = null;
if (part.getContentType() != null) {
contentType = toIsoString(part.getContentType());
values.put(Part.CONTENT_TYPE, contentType);
} else {
throw new MmsException("MIME type of the part must be set.");
}
getValues(part, values);
SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
// Only update the data when:
// 1. New binary data supplied or
// 2. The Uri of the part is different from the current one.
if ((part.getData() != null)
|| (!uri.equals(part.getDataUri()))) {
persistData(part, uri, contentType, preOpenedFiles);
}
}
/**
* Update all parts of a PDU.
*
* @param uri The PDU which need to be updated.
* @param body New message body of the PDU.
* @param preOpenedFiles if not null, a map of preopened InputStreams for the parts.
* @throws MmsException Bad URI or updating failed.
*/
public void updateParts(final Uri uri, final PduBody body,
final Map<Uri, InputStream> preOpenedFiles)
throws MmsException {
try {
PduCacheEntry cacheEntry;
synchronized (PDU_CACHE_INSTANCE) {
if (PDU_CACHE_INSTANCE.isUpdating(uri)) {
if (LOCAL_LOGV) {
LogUtil.v(TAG, "updateParts: " + uri + " blocked by isUpdating()");
}
try {
PDU_CACHE_INSTANCE.wait();
} catch (final InterruptedException e) {
Log.e(TAG, "updateParts: ", e);
}
cacheEntry = PDU_CACHE_INSTANCE.get(uri);
if (cacheEntry != null) {
((MultimediaMessagePdu) cacheEntry.getPdu()).setBody(body);
}
}
// Tell the cache to indicate to other callers that this item
// is currently being updated.
PDU_CACHE_INSTANCE.setUpdating(uri, true);
}
final ArrayList<PduPart> toBeCreated = new ArrayList<>();
final ArrayMap<Uri, PduPart> toBeUpdated = new ArrayMap<>();
final int partsNum = body.getPartsNum();
final StringBuilder filter = new StringBuilder().append('(');
for (int i = 0; i < partsNum; i++) {
final PduPart part = body.getPart(i);
final Uri partUri = part.getDataUri();
if ((partUri == null) || TextUtils.isEmpty(partUri.getAuthority())
|| !partUri.getAuthority().startsWith("mms")) {
toBeCreated.add(part);
} else {
toBeUpdated.put(partUri, part);
// Don't use 'i > 0' to determine whether we should append
// 'AND' since 'i = 0' may be skipped in another branch.
if (filter.length() > 1) {
filter.append(" AND ");
}
filter.append(Part._ID);
filter.append("!=");
DatabaseUtils.appendEscapedSQLString(filter, partUri.getLastPathSegment());
}
}
filter.append(')');
final long msgId = ContentUris.parseId(uri);
// Remove the parts which doesn't exist anymore.
SqliteWrapper.delete(mContext, mContentResolver,
Uri.parse(Mms.CONTENT_URI + "/" + msgId + "/part"),
filter.length() > 2 ? filter.toString() : null, null);
// Create new parts which didn't exist before.
for (final PduPart part : toBeCreated) {
persistPart(part, msgId, preOpenedFiles);
}
// Update the modified parts.
for (final Map.Entry<Uri, PduPart> e : toBeUpdated.entrySet()) {
updatePart(e.getKey(), e.getValue(), preOpenedFiles);
}
} finally {
synchronized (PDU_CACHE_INSTANCE) {
PDU_CACHE_INSTANCE.setUpdating(uri, false);
PDU_CACHE_INSTANCE.notifyAll();
}
}
}
/** /**
* Persist a PDU object to specific location in the storage. * Persist a PDU object to specific location in the storage.
* *
@@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2012 The Android Open Source Project * Copyright (C) 2012 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,46 +18,11 @@
package com.android.messaging.mmslib.util; package com.android.messaging.mmslib.util;
import android.content.Context;
import android.drm.DrmManagerClient;
import android.util.Log;
public class DownloadDrmHelper { public class DownloadDrmHelper {
private static final String TAG = "DownloadDrmHelper";
/** The MIME type of special DRM files */ /** The MIME type of special DRM files */
public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message"; public static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
/** The extensions of special DRM files */
public static final String EXTENSION_DRM_MESSAGE = ".dm";
public static final String EXTENSION_INTERNAL_FWDL = ".fl";
/**
* Checks if the Media Type is a DRM Media Type
*
* @param drmManagerClient A DrmManagerClient
* @param mimetype Media Type to check
* @return True if the Media Type is DRM else false
*/
public static boolean isDrmMimeType(Context context, String mimetype) {
boolean result = false;
if (context != null) {
try {
DrmManagerClient drmClient = new DrmManagerClient(context);
if (drmClient != null && mimetype != null && mimetype.length() > 0) {
result = drmClient.canHandle("", mimetype);
}
} catch (IllegalArgumentException e) {
Log.w(TAG,
"DrmManagerClient instance could not be created, context is Illegal.");
} catch (IllegalStateException e) {
Log.w(TAG, "DrmManagerClient didn't initialize properly.");
}
}
return result;
}
/** /**
* Checks if the Media Type needs to be DRM converted * Checks if the Media Type needs to be DRM converted
* *
@@ -66,46 +32,4 @@ public class DownloadDrmHelper {
public static boolean isDrmConvertNeeded(String mimetype) { public static boolean isDrmConvertNeeded(String mimetype) {
return MIMETYPE_DRM_MESSAGE.equals(mimetype); return MIMETYPE_DRM_MESSAGE.equals(mimetype);
} }
/**
* Modifies the file extension for a DRM Forward Lock file NOTE: This
* function shouldn't be called if the file shouldn't be DRM converted
*/
public static String modifyDrmFwLockFileExtension(String filename) {
if (filename != null) {
int extensionIndex;
extensionIndex = filename.lastIndexOf(".");
if (extensionIndex != -1) {
filename = filename.substring(0, extensionIndex);
}
filename = filename.concat(EXTENSION_INTERNAL_FWDL);
}
return filename;
}
/**
* Gets the original mime type of DRM protected content.
*
* @param context The context
* @param path Path to the file
* @param containingMime The current mime type of of the file i.e. the
* containing mime type
* @return The original mime type of the file if DRM protected else the
* currentMime
*/
public static String getOriginalMimeType(Context context, String path, String containingMime) {
String result = containingMime;
DrmManagerClient drmClient = new DrmManagerClient(context);
try {
if (drmClient.canHandle(path, null)) {
result = drmClient.getOriginalMimeType(path);
}
} catch (IllegalArgumentException ex) {
Log.w(TAG,
"Can't get original mime type since path is null or empty string.");
} catch (IllegalStateException ex) {
Log.w(TAG, "DrmManagerClient didn't initialize properly.");
}
return result;
}
} }
@@ -39,7 +39,6 @@ import java.util.regex.Pattern;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.R; import com.android.messaging.R;
import com.android.messaging.datamodel.BugleNotifications; import com.android.messaging.datamodel.BugleNotifications;
import com.android.messaging.datamodel.MessageNotificationState;
import com.android.messaging.datamodel.NoConfirmationSmsSendService; import com.android.messaging.datamodel.NoConfirmationSmsSendService;
import com.android.messaging.datamodel.action.ReceiveSmsMessageAction; import com.android.messaging.datamodel.action.ReceiveSmsMessageAction;
import com.android.messaging.sms.MmsUtils; import com.android.messaging.sms.MmsUtils;
@@ -164,17 +163,6 @@ public final class SmsReceiver extends BroadcastReceiver {
} }
} }
private static class SecondaryUserNotificationState extends MessageNotificationState {
SecondaryUserNotificationState() {
super(null);
}
@Override
protected Style build(Builder builder) {
return null;
}
}
public static void postNewMessageSecondaryUserNotification() { public static void postNewMessageSecondaryUserNotification() {
final Context context = Factory.get().getApplicationContext(); final Context context = Factory.get().getApplicationContext();
final Resources resources = context.getResources(); final Resources resources = context.getResources();
+1 -171
View File
@@ -17,27 +17,19 @@
package com.android.messaging.sms; package com.android.messaging.sms;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.provider.Telephony; import android.provider.Telephony;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.android.messaging.R; import com.android.messaging.R;
import com.android.messaging.datamodel.data.ParticipantData; import com.android.messaging.datamodel.data.ParticipantData;
import com.android.messaging.util.LogUtil; import com.android.messaging.util.LogUtil;
import com.android.messaging.util.PhoneUtils;
import com.google.common.collect.Lists;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.List;
/* /*
* Database helper class for looking up APNs. This database has a single table * Database helper class for looking up APNs. This database has a single table
@@ -86,61 +78,7 @@ public class ApnDatabase extends SQLiteOpenHelper {
Telephony.Carriers.MVNO_MATCH_DATA + " TEXT," + Telephony.Carriers.MVNO_MATCH_DATA + " TEXT," +
Telephony.Carriers.SUBSCRIPTION_ID + " INTEGER DEFAULT " + Telephony.Carriers.SUBSCRIPTION_ID + " INTEGER DEFAULT " +
ParticipantData.DEFAULT_SELF_SUB_ID + ");"; ParticipantData.DEFAULT_SELF_SUB_ID + ");";
public static final int COLUMN_ID = 4;
public static final String[] APN_PROJECTION = {
Telephony.Carriers.TYPE, // 0
Telephony.Carriers.MMSC, // 1
Telephony.Carriers.MMSPROXY, // 2
Telephony.Carriers.MMSPORT, // 3
Telephony.Carriers._ID, // 4
Telephony.Carriers.CURRENT, // 5
Telephony.Carriers.NUMERIC, // 6
Telephony.Carriers.NAME, // 7
Telephony.Carriers.MCC, // 8
Telephony.Carriers.MNC, // 9
Telephony.Carriers.APN, // 10
Telephony.Carriers.SUBSCRIPTION_ID // 11
};
public static final int COLUMN_TYPE = 0;
public static final int COLUMN_MMSC = 1;
public static final int COLUMN_MMSPROXY = 2;
public static final int COLUMN_MMSPORT = 3;
public static final int COLUMN_ID = 4;
public static final int COLUMN_CURRENT = 5;
public static final int COLUMN_NUMERIC = 6;
public static final int COLUMN_NAME = 7;
public static final int COLUMN_MCC = 8;
public static final int COLUMN_MNC = 9;
public static final int COLUMN_APN = 10;
public static final int COLUMN_SUB_ID = 11;
public static final String[] APN_FULL_PROJECTION = {
Telephony.Carriers.NAME,
Telephony.Carriers.MCC,
Telephony.Carriers.MNC,
Telephony.Carriers.APN,
Telephony.Carriers.USER,
Telephony.Carriers.SERVER,
Telephony.Carriers.PASSWORD,
Telephony.Carriers.PROXY,
Telephony.Carriers.PORT,
Telephony.Carriers.MMSC,
Telephony.Carriers.MMSPROXY,
Telephony.Carriers.MMSPORT,
Telephony.Carriers.AUTH_TYPE,
Telephony.Carriers.TYPE,
Telephony.Carriers.PROTOCOL,
Telephony.Carriers.ROAMING_PROTOCOL,
Telephony.Carriers.CARRIER_ENABLED,
Telephony.Carriers.BEARER,
Telephony.Carriers.MVNO_TYPE,
Telephony.Carriers.MVNO_MATCH_DATA,
Telephony.Carriers.CURRENT,
Telephony.Carriers.SUBSCRIPTION_ID,
};
private static final String CURRENT_SELECTION = Telephony.Carriers.CURRENT + " NOT NULL";
/** /**
* ApnDatabase is initialized asynchronously from the application.onCreate * ApnDatabase is initialized asynchronously from the application.onCreate
@@ -178,105 +116,6 @@ public class ApnDatabase extends SQLiteOpenHelper {
rebuildTables(db); rebuildTables(db);
} }
/**
* Get a copy of user changes in the old table
*
* @return The list of user changed apns
*/
public static List<ContentValues> loadUserDataFromOldTable(final SQLiteDatabase db) {
try (Cursor cursor = db.query(APN_TABLE,
APN_FULL_PROJECTION, CURRENT_SELECTION,
null/*selectionArgs*/,
null/*groupBy*/, null/*having*/, null/*orderBy*/)) {
if (cursor != null) {
final List<ContentValues> result = Lists.newArrayList();
while (cursor.moveToNext()) {
final ContentValues row = cursorToValues(cursor);
if (row != null) {
result.add(row);
}
}
return result;
}
} catch (final SQLiteException e) {
LogUtil.w(TAG, "ApnDatabase.loadUserDataFromOldTable: no old user data: " + e, e);
}
return null;
}
private static final String[] ID_PROJECTION = new String[]{Telephony.Carriers._ID};
private static final String ID_SELECTION = Telephony.Carriers._ID + "=?";
/**
* Store use changes of old table into the new apn table
*
* @param data The user changes
*/
public static void saveUserDataFromOldTable(
final SQLiteDatabase db, final List<ContentValues> data) {
if (data == null || data.size() < 1) {
return;
}
for (final ContentValues row : data) {
// Build query from the row data. It is an exact match, column by column,
// except the CURRENT column
final StringBuilder selectionBuilder = new StringBuilder();
final ArrayList<String> selectionArgs = Lists.newArrayList();
for (final String key : row.keySet()) {
if (!Telephony.Carriers.CURRENT.equals(key)) {
if (selectionBuilder.length() > 0) {
selectionBuilder.append(" AND ");
}
final String value = row.getAsString(key);
if (TextUtils.isEmpty(value)) {
selectionBuilder.append(key).append(" IS NULL");
} else {
selectionBuilder.append(key).append("=?");
selectionArgs.add(value);
}
}
}
try (Cursor cursor = db.query(APN_TABLE,
ID_PROJECTION,
selectionBuilder.toString(),
selectionArgs.toArray(new String[0]),
null/*groupBy*/, null/*having*/, null/*orderBy*/)) {
/*groupBy*/
/*having*/
/*orderBy*/
if (cursor != null && cursor.moveToFirst()) {
db.update(APN_TABLE, row, ID_SELECTION, new String[]{cursor.getString(0)});
} else {
// User APN does not exist, insert into the new table
row.put(Telephony.Carriers.NUMERIC,
PhoneUtils.canonicalizeMccMnc(
row.getAsString(Telephony.Carriers.MCC),
row.getAsString(Telephony.Carriers.MNC))
);
db.insert(APN_TABLE, null/*nullColumnHack*/, row);
}
} catch (final SQLiteException e) {
LogUtil.e(TAG, "ApnDatabase.saveUserDataFromOldTable: query error " + e, e);
}
}
}
// Convert Cursor to ContentValues
private static ContentValues cursorToValues(final Cursor cursor) {
final int columnCount = cursor.getColumnCount();
if (columnCount > 0) {
final ContentValues result = new ContentValues();
for (int i = 0; i < columnCount; i++) {
final String name = cursor.getColumnName(i);
final String value = cursor.getString(i);
result.put(name, value);
}
return result;
}
return null;
}
@Override @Override
public void onOpen(final SQLiteDatabase db) { public void onOpen(final SQLiteDatabase db) {
super.onOpen(db); super.onOpen(db);
@@ -350,13 +189,4 @@ public class ApnDatabase extends SQLiteOpenHelper {
loadApnTable(db); loadApnTable(db);
} }
/**
* Clear all tables
*/
public static void clearTables() {
final SQLiteDatabase db = getApnDatabase().getWritableDatabase();
db.execSQL("DROP TABLE IF EXISTS " + APN_TABLE);
db.execSQL(APN_TABLE_SQL);
}
} }
@@ -595,19 +595,4 @@ public class BugleApnSettingsLoader implements ApnSettingsLoader {
} }
return false; return false;
} }
/**
* Get the ID of first APN to try
*/
public static String getFirstTryApn(final SQLiteDatabase database, final String mccMnc) {
String key = null;
try (Cursor cursor = queryLocalDatabase(database, mccMnc, null/*apnName*/)) {
if (cursor.moveToFirst()) {
key = cursor.getString(ApnDatabase.COLUMN_ID);
}
} catch (final Exception e) {
// Nothing to do
}
return key;
}
} }
@@ -20,7 +20,6 @@ package com.android.messaging.sms;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
@@ -734,32 +733,6 @@ public class DatabaseMessages {
} }
} }
/**
* Get media file size
*/
private long getMediaFileSize() {
final Context context = Factory.get().getApplicationContext();
final Uri uri = getDataUri();
AssetFileDescriptor fd = null;
try {
fd = context.getContentResolver().openAssetFileDescriptor(uri, "r");
if (fd != null) {
return fd.getParcelFileDescriptor().getStatSize();
}
} catch (final FileNotFoundException e) {
LogUtil.e(TAG, "DatabaseMessages.MmsPart: cound not find media file: " + e, e);
} finally {
if (fd != null) {
try {
fd.close();
} catch (final IOException e) {
LogUtil.e(TAG, "DatabaseMessages.MmsPart: failed to close " + e, e);
}
}
}
return 0L;
}
/** /**
* @return If the type is a text type that stores text embedded (i.e. in db table) * @return If the type is a text type that stores text embedded (i.e. in db table)
*/ */
-123
View File
@@ -25,7 +25,6 @@ import android.content.Intent;
import android.content.res.AssetFileDescriptor; import android.content.res.AssetFileDescriptor;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.media.MediaMetadataRetriever; import android.media.MediaMetadataRetriever;
import android.net.Uri; import android.net.Uri;
@@ -38,8 +37,6 @@ import android.provider.Telephony.Threads;
import android.telephony.SmsManager; import android.telephony.SmsManager;
import android.telephony.SmsMessage; import android.telephony.SmsMessage;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.R; import com.android.messaging.R;
@@ -1379,45 +1376,6 @@ public class MmsUtils {
null/*selectionArgs*/); null/*selectionArgs*/);
} }
/**
* Update the read status of a single MMS message by its URI
*
* @param mmsUri
* @param read
*/
public static void updateReadStatusForMmsMessage(final Uri mmsUri, final boolean read) {
final ContentResolver resolver = Factory.get().getApplicationContext().getContentResolver();
final ContentValues values = new ContentValues();
values.put(Mms.READ, read ? 1 : 0);
resolver.update(mmsUri, values, null/*where*/, null/*selectionArgs*/);
}
public static class AttachmentInfo {
public String mUrl;
public String mContentType;
public int mWidth;
public int mHeight;
}
/**
* Convert byte array to Java String using a charset name
*
* @param bytes
* @param charsetName
* @return
*/
public static String bytesToString(final byte[] bytes, final String charsetName) {
if (bytes == null) {
return null;
}
try {
return new String(bytes, charsetName);
} catch (final UnsupportedEncodingException e) {
LogUtil.e(TAG, "MmsUtils.bytesToString: " + e, e);
return new String(bytes);
}
}
/** /**
* Convert a Java String to byte array using a charset name * Convert a Java String to byte array using a charset name
* *
@@ -1510,25 +1468,6 @@ public class MmsUtils {
return sUseSystemApn; return sUseSystemApn;
} }
// For the internal debugger only
public static void setUseSystemApnTable(final boolean turnOn) {
if (!turnOn) {
// We're turning on local APNs on a device where we wouldn't normally have the
// local APN table. Build it here.
final SQLiteDatabase database = ApnDatabase.getApnDatabase().getWritableDatabase();
// Do we already have the table?
try (Cursor cursor = database.query(ApnDatabase.APN_TABLE,
ApnDatabase.APN_PROJECTION,
null, null, null, null, null, null)) {
} catch (final Exception e) {
// Apparently there's no table, create it now.
ApnDatabase.forceBuildAndLoadApnTables();
}
}
sUseSystemApn = turnOn;
}
public static final Uri MMS_PART_CONTENT_URI = Uri.parse("content://mms/part"); public static final Uri MMS_PART_CONTENT_URI = Uri.parse("content://mms/part");
/** /**
@@ -2045,28 +1984,6 @@ public class MmsUtils {
return null; return null;
} }
/**
* Try parse the address using RFC822 format. If it fails to parse, then return the
* original address
*
* @param address The MMS ind sender address to parse
* @return The real address. If in RFC822 format, returns the correct email.
*/
private static String parsePotentialRfc822EmailAddress(final String address) {
if (address == null || !address.contains("@") || !address.contains("<")) {
return address;
}
final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
if (tokens != null && tokens.length > 0) {
for (final Rfc822Token token : tokens) {
if (token != null && !TextUtils.isEmpty(token.getAddress())) {
return token.getAddress();
}
}
}
return address;
}
public static DatabaseMessages.MmsMessage processReceivedPdu(final Context context, public static DatabaseMessages.MmsMessage processReceivedPdu(final Context context,
final byte[] pushData, final int subId, final String subPhoneNumber) { final byte[] pushData, final int subId, final String subPhoneNumber) {
// Parse data // Parse data
@@ -2089,20 +2006,6 @@ public class MmsUtils {
switch (type) { switch (type) {
case PduHeaders.MESSAGE_TYPE_DELIVERY_IND: case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: { case PduHeaders.MESSAGE_TYPE_READ_ORIG_IND: {
// TODO: Should this be commented out?
// threadId = findThreadId(context, pdu, type);
// if (threadId == -1) {
// // The associated SendReq isn't found, therefore skip
// // processing this PDU.
// break;
// }
// Uri uri = p.persist(pdu, Inbox.CONTENT_URI, true,
// MessagingPreferenceActivity.getIsGroupMmsEnabled(mContext), null);
// // Update thread ID for ReadOrigInd & DeliveryInd.
// ContentValues values = new ContentValues(1);
// values.put(Mms.THREAD_ID, threadId);
// SqliteWrapper.update(mContext, cr, uri, values, null, null);
LogUtil.w(TAG, "Received unsupported WAP Push, type=" + type); LogUtil.w(TAG, "Received unsupported WAP Push, type=" + type);
break; break;
} }
@@ -2125,24 +2028,6 @@ public class MmsUtils {
} }
final String[] dups = getDupNotifications(context, nInd); final String[] dups = getDupNotifications(context, nInd);
if (dups == null) { if (dups == null) {
// TODO: Do we handle Rfc822 Email Addresses?
//final String contentLocation =
// MmsUtils.bytesToString(nInd.getContentLocation(), "UTF-8");
//final byte[] transactionId = nInd.getTransactionId();
//final long messageSize = nInd.getMessageSize();
//final long expiry = nInd.getExpiry();
//final String transactionIdString =
// MmsUtils.bytesToString(transactionId, "UTF-8");
//final EncodedStringValue fromEncoded = nInd.getFrom();
// An mms ind received from email address will have from address shown as
// "John Doe <johndoe@foobar.com>" but the actual received message will only
// have the email address. So let's try to parse the RFC822 format to get the
// real email. Otherwise we will create two conversations for the MMS
// notification and the actual MMS message if auto retrieve is disabled.
//final String from = parsePotentialRfc822EmailAddress(
// fromEncoded != null ? fromEncoded.getString() : null);
Uri inboxUri = null; Uri inboxUri = null;
try { try {
inboxUri = p.persist(pdu, Mms.Inbox.CONTENT_URI, subId, subPhoneNumber, inboxUri = p.persist(pdu, Mms.Inbox.CONTENT_URI, subId, subPhoneNumber,
@@ -2441,12 +2326,6 @@ public class MmsUtils {
switch (rawStatus) { switch (rawStatus) {
case PduHeaders.RESPONSE_STATUS_ERROR_SERVICE_DENIED: case PduHeaders.RESPONSE_STATUS_ERROR_SERVICE_DENIED:
case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED: case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_SERVICE_DENIED:
//case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_LIMITATIONS_NOT_MET:
//case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_REQUEST_NOT_ACCEPTED:
//case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_FORWARDING_DENIED:
//case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_REPLY_CHARGING_NOT_SUPPORTED:
//case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_ADDRESS_HIDING_NOT_SUPPORTED:
//case PduHeaders.RESPONSE_STATUS_ERROR_PERMANENT_LACK_OF_PREPAID:
stringResId = R.string.mms_failure_outgoing_service; stringResId = R.string.mms_failure_outgoing_service;
break; break;
case PduHeaders.RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED: case PduHeaders.RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED:
@@ -2463,8 +2342,6 @@ public class MmsUtils {
stringResId = R.string.mms_failure_outgoing_content; stringResId = R.string.mms_failure_outgoing_content;
break; break;
case PduHeaders.RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE: case PduHeaders.RESPONSE_STATUS_ERROR_UNSUPPORTED_MESSAGE:
//case PduHeaders.RESPONSE_STATUS_ERROR_MESSAGE_NOT_FOUND:
//case PduHeaders.RESPONSE_STATUS_ERROR_TRANSIENT_MESSAGE_NOT_FOUND:
stringResId = R.string.mms_failure_outgoing_unsupported; stringResId = R.string.mms_failure_outgoing_unsupported;
break; break;
case MessageData.RAW_TELEPHONY_STATUS_MESSAGE_TOO_BIG: case MessageData.RAW_TELEPHONY_STATUS_MESSAGE_TOO_BIG:
@@ -1,59 +0,0 @@
/*
* 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 com.android.messaging.sms;
/**
* A generic Exception for errors in sending SMS
*/
class SmsException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Creates a new SmsException.
*/
public SmsException() {
super();
}
/**
* Creates a new SmsException with the specified detail message.
*
* @param message the detail message.
*/
public SmsException(String message) {
super(message);
}
/**
* Creates a new SmsException with the specified cause.
*
* @param cause the cause.
*/
public SmsException(Throwable cause) {
super(cause);
}
/**
* Creates a new SmsException with the specified detail message and cause.
*
* @param message the detail message.
* @param cause the cause.
*/
public SmsException(String message, Throwable cause) {
super(message, cause);
}
}
+6 -6
View File
@@ -182,7 +182,7 @@ public class SmsSender {
// This should be called from a RequestWriter queue thread // This should be called from a RequestWriter queue thread
public static SendResult sendMessage(final Context context, final int subId, String dest, public static SendResult sendMessage(final Context context, final int subId, String dest,
String message, final String serviceCenter, final boolean requireDeliveryReport, String message, final String serviceCenter, final boolean requireDeliveryReport,
final Uri messageUri) throws SmsException { final Uri messageUri) throws Exception {
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) { if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
LogUtil.v(TAG, "SmsSender: sending message. " + LogUtil.v(TAG, "SmsSender: sending message. " +
"dest=" + dest + " message=" + message + "dest=" + dest + " message=" + message +
@@ -191,7 +191,7 @@ public class SmsSender {
" requestId=" + messageUri); " requestId=" + messageUri);
} }
if (TextUtils.isEmpty(message)) { if (TextUtils.isEmpty(message)) {
throw new SmsException("SmsSender: empty text message"); throw new Exception("SmsSender: empty text message");
} }
// Get the real dest and message for email or alias if dest is email or alias // Get the real dest and message for email or alias if dest is email or alias
// Or sanitize the dest if dest is a number // Or sanitize the dest if dest is a number
@@ -208,13 +208,13 @@ public class SmsSender {
dest = PhoneNumberUtils.stripSeparators(dest); dest = PhoneNumberUtils.stripSeparators(dest);
} }
if (TextUtils.isEmpty(dest)) { if (TextUtils.isEmpty(dest)) {
throw new SmsException("SmsSender: empty destination address"); throw new Exception("SmsSender: empty destination address");
} }
// Divide the input message by SMS length limit // Divide the input message by SMS length limit
final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager(); final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager();
final ArrayList<String> messages = smsManager.divideMessage(message); final ArrayList<String> messages = smsManager.divideMessage(message);
if (messages == null || messages.size() < 1) { if (messages == null || messages.size() < 1) {
throw new SmsException("SmsSender: fails to divide message"); throw new Exception("SmsSender: fails to divide message");
} }
// Prepare the send result, which collects the send status for each part // Prepare the send result, which collects the send status for each part
final SendResult pendingResult = new SendResult(messages.size()); final SendResult pendingResult = new SendResult(messages.size());
@@ -252,7 +252,7 @@ public class SmsSender {
// Actually sending the message using SmsManager // Actually sending the message using SmsManager
private static void sendInternal(final Context context, final int subId, String dest, private static void sendInternal(final Context context, final int subId, String dest,
final ArrayList<String> messages, final String serviceCenter, final ArrayList<String> messages, final String serviceCenter,
final boolean requireDeliveryReport, final Uri messageUri) throws SmsException { final boolean requireDeliveryReport, final Uri messageUri) throws Exception {
Assert.notNull(context); Assert.notNull(context);
final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager(); final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager();
final int messageCount = messages.size(); final int messageCount = messages.size();
@@ -295,7 +295,7 @@ public class SmsSender {
dest, serviceCenter, messages, sentIntents, deliveryIntents); dest, serviceCenter, messages, sentIntents, deliveryIntents);
} }
} catch (final Exception e) { } catch (final Exception e) {
throw new SmsException("SmsSender: caught exception in sending " + e); throw new Exception("SmsSender: caught exception in sending " + e);
} }
} }
@@ -1,38 +0,0 @@
/*
* 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 com.android.messaging.ui;
/**
* Defines a list of animation tags used as android:transitionName properties used for L's
* hero transitions.
*/
public class BugleAnimationTags {
/**
* The tag for the FAB in the conversation list activity.
*/
public static final String TAG_FABICON = "bugle:fabicon";
/**
* The tag for the content view of a conversation list item view.
*/
public static final String TAG_CLIVCONTENT = "bugle:clivcontent";
/**
* The tag for the action bar.
*/
public static final String TAG_ACTIONBAR = "bugle:actionbar";
}
@@ -33,7 +33,6 @@ import android.view.ViewOverlay;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import com.android.messaging.R; import com.android.messaging.R;
import com.android.messaging.util.ImageUtils;
import com.android.messaging.util.UiUtils; import com.android.messaging.util.UiUtils;
/** /**
@@ -192,9 +191,9 @@ public class ViewGroupItemVerticalExplodeAnimation {
// Strip the view of its background when taking a snapshot so that things like touch // Strip the view of its background when taking a snapshot so that things like touch
// feedback don't get accidentally snapshotted. // feedback don't get accidentally snapshotted.
final Drawable viewBackground = view.getBackground(); final Drawable viewBackground = view.getBackground();
ImageUtils.setBackgroundDrawableOnView(view, null); view.setBackground(null);
view.draw(new Canvas(viewBitmap)); view.draw(new Canvas(viewBitmap));
ImageUtils.setBackgroundDrawableOnView(view, viewBackground); view.setBackground(viewBackground);
return viewBitmap; return viewBitmap;
} }
} }
@@ -30,7 +30,6 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreferenceCompat;
import com.android.messaging.R; import com.android.messaging.R;
import com.android.messaging.ui.BugleActionBarActivity; import com.android.messaging.ui.BugleActionBarActivity;
@@ -82,14 +81,10 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
public static class ApplicationSettingsFragment extends PreferenceFragmentCompat { public static class ApplicationSettingsFragment extends PreferenceFragmentCompat {
private String mNotificationsPreferenceKey; private String mNotificationsPreferenceKey;
private Preference mNotificationsPreference;
private String mSmsDisabledPrefKey; private String mSmsDisabledPrefKey;
private Preference mSmsDisabledPreference; private Preference mSmsDisabledPreference;
private String mSmsEnabledPrefKey; private String mSmsEnabledPrefKey;
private Preference mSmsEnabledPreference; private Preference mSmsEnabledPreference;
private boolean mIsSmsPreferenceClicked;
private String mSwipeRightToDeleteConversationkey;
private SwitchPreferenceCompat mSwipeRightToDeleteConversationPreference;
public ApplicationSettingsFragment() { public ApplicationSettingsFragment() {
// Required empty constructor // Required empty constructor
@@ -103,16 +98,10 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
mNotificationsPreferenceKey = mNotificationsPreferenceKey =
getString(R.string.notifications_pref_key); getString(R.string.notifications_pref_key);
mNotificationsPreference = findPreference(mNotificationsPreferenceKey);
mSmsDisabledPrefKey = getString(R.string.sms_disabled_pref_key); mSmsDisabledPrefKey = getString(R.string.sms_disabled_pref_key);
mSmsDisabledPreference = findPreference(mSmsDisabledPrefKey); mSmsDisabledPreference = findPreference(mSmsDisabledPrefKey);
mSmsEnabledPrefKey = getString(R.string.sms_enabled_pref_key); mSmsEnabledPrefKey = getString(R.string.sms_enabled_pref_key);
mSmsEnabledPreference = findPreference(mSmsEnabledPrefKey); mSmsEnabledPreference = findPreference(mSmsEnabledPrefKey);
mSwipeRightToDeleteConversationkey = getString(
R.string.swipe_right_deletes_conversation_key);
mSwipeRightToDeleteConversationPreference =
(SwitchPreferenceCompat) findPreference(mSwipeRightToDeleteConversationkey);
mIsSmsPreferenceClicked = false;
final PreferenceScreen advancedScreen = (PreferenceScreen) findPreference( final PreferenceScreen advancedScreen = (PreferenceScreen) findPreference(
getString(R.string.advanced_pref_key)); getString(R.string.advanced_pref_key));
@@ -135,10 +124,6 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName()); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
startActivity(intent); startActivity(intent);
} }
if (preference.getKey() == mSmsDisabledPrefKey ||
preference.getKey() == mSmsEnabledPrefKey) {
mIsSmsPreferenceClicked = true;
}
return super.onPreferenceTreeClick(preference); return super.onPreferenceTreeClick(preference);
} }
@@ -152,7 +137,6 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
getPreferenceScreen().removePreference(mSmsEnabledPreference); getPreferenceScreen().removePreference(mSmsEnabledPreference);
mSmsDisabledPreference.setSummary(defaultSmsAppLabel); mSmsDisabledPreference.setSummary(defaultSmsAppLabel);
} }
mIsSmsPreferenceClicked = false;
} }
@Override @Override
@@ -152,7 +152,7 @@ public class ContactPickerFragment extends Fragment implements ContactPickerData
mRecipientTextView.setContactChipsListener(this); mRecipientTextView.setContactChipsListener(this);
mRecipientTextView.setDropdownChipLayouter(new ContactDropdownLayouter(inflater, mRecipientTextView.setDropdownChipLayouter(new ContactDropdownLayouter(inflater,
getActivity(), this)); getActivity(), this));
mRecipientTextView.setAdapter(new ContactRecipientAdapter(getActivity(), this)); mRecipientTextView.setAdapter(new ContactRecipientAdapter(getActivity()));
mRecipientTextView.addTextChangedListener(new TextWatcher() { mRecipientTextView.addTextChangedListener(new TextWatcher() {
@Override @Override
public void onTextChanged(final CharSequence s, final int start, final int before, public void onTextChanged(final CharSequence s, final int start, final int before,
@@ -68,15 +68,14 @@ public final class ContactRecipientAdapter extends BaseRecipientAdapter {
*/ */
private static final int ENTRY_TYPE_DIRECTORY = RecipientEntry.ENTRY_TYPE_SIZE; private static final int ENTRY_TYPE_DIRECTORY = RecipientEntry.ENTRY_TYPE_SIZE;
public ContactRecipientAdapter(final Context context, public ContactRecipientAdapter(final Context context) {
final ContactListItemView.HostInterface clivHost) { this(context, Integer.MAX_VALUE, QUERY_TYPE_PHONE);
this(context, Integer.MAX_VALUE, QUERY_TYPE_PHONE, clivHost);
} }
public ContactRecipientAdapter(final Context context, final int preferredMaxResultCount, public ContactRecipientAdapter(final Context context, final int preferredMaxResultCount,
final int queryMode, final ContactListItemView.HostInterface clivHost) { final int queryMode) {
super(context, preferredMaxResultCount, queryMode); super(context, preferredMaxResultCount, queryMode);
setPhotoManager(new ContactRecipientPhotoManager(context, clivHost)); setPhotoManager(new ContactRecipientPhotoManager(context));
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} }
@@ -59,7 +59,6 @@ public class ContactRecipientAutoCompleteView extends RecipientEditTextView {
void onEntryComplete(); void onEntryComplete();
} }
private final int mTextHeight;
private ContactChipsChangeListener mChipsChangeListener; private ContactChipsChangeListener mChipsChangeListener;
/** /**
@@ -110,7 +109,6 @@ public class ContactRecipientAutoCompleteView extends RecipientEditTextView {
final Rect textBounds = new Rect(0, 0, 0, 0); final Rect textBounds = new Rect(0, 0, 0, 0);
final TextPaint paint = getPaint(); final TextPaint paint = getPaint();
paint.getTextBounds(TEXT_HEIGHT_SAMPLE, 0, TEXT_HEIGHT_SAMPLE.length(), textBounds); paint.getTextBounds(TEXT_HEIGHT_SAMPLE, 0, TEXT_HEIGHT_SAMPLE.length(), textBounds);
mTextHeight = textBounds.height();
setTokenizer(new Rfc822Tokenizer()); setTokenizer(new Rfc822Tokenizer());
addTextChangedListener(new ContactChipsWatcher()); addTextChangedListener(new ContactChipsWatcher());
@@ -42,15 +42,12 @@ public class ContactRecipientPhotoManager implements PhotoManager {
private static final String IMAGE_BYTES_REQUEST_STATIC_BINDING_ID = "imagebytes"; private static final String IMAGE_BYTES_REQUEST_STATIC_BINDING_ID = "imagebytes";
private final Context mContext; private final Context mContext;
private final int mIconSize; private final int mIconSize;
private final ContactListItemView.HostInterface mClivHostInterface;
public ContactRecipientPhotoManager(final Context context, public ContactRecipientPhotoManager(final Context context) {
final ContactListItemView.HostInterface clivHostInterface) {
mContext = context; mContext = context;
mIconSize = context.getResources().getDimensionPixelSize( mIconSize = context.getResources().getDimensionPixelSize(
R.dimen.compose_message_chip_height) - context.getResources().getDimensionPixelSize( R.dimen.compose_message_chip_height) - context.getResources().getDimensionPixelSize(
R.dimen.compose_message_chip_padding) * 2; R.dimen.compose_message_chip_padding) * 2;
mClivHostInterface = clivHostInterface;
} }
/** /**
@@ -90,7 +90,6 @@ public class ComposeMessageView extends LinearLayout
void sendMessage(MessageData message); void sendMessage(MessageData message);
void onComposeEditTextFocused(); void onComposeEditTextFocused();
void onAttachmentsCleared(); void onAttachmentsCleared();
void onAttachmentsChanged(final boolean haveAttachments);
void displayPhoto(Uri photoUri, Rect imageBounds, boolean isDraft); void displayPhoto(Uri photoUri, Rect imageBounds, boolean isDraft);
void promptForSelfPhoneNumber(); void promptForSelfPhoneNumber();
boolean isReadyForAction(); boolean isReadyForAction();
@@ -301,10 +300,8 @@ public class ComposeMessageView extends LinearLayout
} }
final boolean haveAttachments = mBinding.getData().hasAttachments(); final boolean haveAttachments = mBinding.getData().hasAttachments();
if (simPickerVisible && haveAttachments) { if (simPickerVisible && haveAttachments) {
mHost.onAttachmentsChanged(false);
mAttachmentPreview.hideAttachmentPreview(); mAttachmentPreview.hideAttachmentPreview();
} else { } else {
mHost.onAttachmentsChanged(haveAttachments);
mAttachmentPreview.onAttachmentsChanged(mBinding.getData()); mAttachmentPreview.onAttachmentsChanged(mBinding.getData());
} }
} }
@@ -474,8 +471,7 @@ public class ComposeMessageView extends LinearLayout
if ((changeFlags & DraftMessageData.ATTACHMENTS_CHANGED) == if ((changeFlags & DraftMessageData.ATTACHMENTS_CHANGED) ==
DraftMessageData.ATTACHMENTS_CHANGED) { DraftMessageData.ATTACHMENTS_CHANGED) {
final boolean haveAttachments = mAttachmentPreview.onAttachmentsChanged(data); mAttachmentPreview.onAttachmentsChanged(data);
mHost.onAttachmentsChanged(haveAttachments);
hasAttachmentsChanged = true; hasAttachmentsChanged = true;
} }
@@ -165,13 +165,6 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
private ConversationFragmentHost mHost; private ConversationFragmentHost mHost;
protected List<Integer> mFilterResults;
// The minimum scrolling distance between RecyclerView's scroll change event beyong which
// a fling motion is considered fast, in which case we'll delay load image attachments for
// perf optimization.
private int mFastFlingThreshold;
// ConversationMessageView that is currently selected // ConversationMessageView that is currently selected
private ConversationMessageView mSelectedMessage; private ConversationMessageView mSelectedMessage;
@@ -412,8 +405,6 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mFastFlingThreshold = getResources().getDimensionPixelOffset(
R.dimen.conversation_fast_fling_threshold);
mAdapter = new ConversationMessageAdapter(getActivity(), null, this, mAdapter = new ConversationMessageAdapter(getActivity(), null, this,
null, null,
// Sets the item click listener on the Recycler item views. // Sets the item click listener on the Recycler item views.
@@ -1579,11 +1570,6 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
return -1; // don't override the color return -1; // don't override the color
} }
@Override
public void onAttachmentsChanged(final boolean haveAttachments) {
// no-op for now
}
@Override @Override
public void onDraftChanged(final DraftMessageData data, final int changeFlags) { public void onDraftChanged(final DraftMessageData data, final int changeFlags) {
mDraftMessageDataModel.ensureBound(data); mDraftMessageDataModel.ensureBound(data);
@@ -40,7 +40,6 @@ import com.android.messaging.ui.mediapicker.MediaPicker.MediaPickerListener;
import com.android.messaging.util.Assert; import com.android.messaging.util.Assert;
import com.android.messaging.util.ImeUtil; import com.android.messaging.util.ImeUtil;
import com.android.messaging.util.ImeUtil.ImeStateHost; import com.android.messaging.util.ImeUtil.ImeStateHost;
import com.google.common.annotations.VisibleForTesting;
import java.util.Collection; import java.util.Collection;
@@ -236,26 +235,6 @@ public class ConversationInputManager implements ConversationInput.ConversationI
return false; return false;
} }
@VisibleForTesting
boolean isMediaPickerVisible() {
return mMediaInput.mShowing;
}
@VisibleForTesting
boolean isSimSelectorVisible() {
return mSimInput.mShowing;
}
@VisibleForTesting
boolean isImeKeyboardVisible() {
return mImeInput.mShowing;
}
@VisibleForTesting
void testNotifyImeStateChanged(final boolean imeOpen) {
mImeStateObserver.onImeStateChanged(imeOpen);
}
/** /**
* returns true if the state of the visibility was actually changed * returns true if the state of the visibility was actually changed
*/ */
@@ -737,7 +737,7 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
R.dimen.message_metadata_top_padding); R.dimen.message_metadata_top_padding);
// Update the message text/info views // Update the message text/info views
ImageUtils.setBackgroundDrawableOnView(mMessageTextAndInfoView, textBackground); mMessageTextAndInfoView.setBackground(textBackground);
mMessageTextAndInfoView.setMinimumHeight(textMinHeight); mMessageTextAndInfoView.setMinimumHeight(textMinHeight);
final LinearLayout.LayoutParams textAndInfoLayoutParams = final LinearLayout.LayoutParams textAndInfoLayoutParams =
(LinearLayout.LayoutParams) mMessageTextAndInfoView.getLayoutParams(); (LinearLayout.LayoutParams) mMessageTextAndInfoView.getLayoutParams();
@@ -36,7 +36,6 @@ import android.widget.AbsListView;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.core.view.ViewGroupCompat; import androidx.core.view.ViewGroupCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager; import androidx.loader.app.LoaderManager;
@@ -51,7 +50,6 @@ import com.android.messaging.datamodel.binding.BindingBase;
import com.android.messaging.datamodel.data.ConversationListData; import com.android.messaging.datamodel.data.ConversationListData;
import com.android.messaging.datamodel.data.ConversationListData.ConversationListDataListener; import com.android.messaging.datamodel.data.ConversationListData.ConversationListDataListener;
import com.android.messaging.datamodel.data.ConversationListItemData; import com.android.messaging.datamodel.data.ConversationListItemData;
import com.android.messaging.ui.BugleAnimationTags;
import com.android.messaging.ui.ListEmptyView; import com.android.messaging.ui.ListEmptyView;
import com.android.messaging.ui.SnackBarInteraction; import com.android.messaging.ui.SnackBarInteraction;
import com.android.messaging.ui.UIIntents; import com.android.messaging.ui.UIIntents;
@@ -232,7 +230,6 @@ public class ConversationListFragment extends Fragment implements ConversationLi
mStartNewConversationButton.setOnClickListener(clickView -> mStartNewConversationButton.setOnClickListener(clickView ->
mHost.onCreateConversationClick()); mHost.onCreateConversationClick());
} }
ViewCompat.setTransitionName(mStartNewConversationButton, BugleAnimationTags.TAG_FABICON);
// The root view has a non-null background, which by default is deemed by the framework // The root view has a non-null background, which by default is deemed by the framework
// to be a "transition group," where all child views are animated together during an // to be a "transition group," where all child views are animated together during an
@@ -417,15 +414,6 @@ public class ConversationListFragment extends Fragment implements ConversationLi
}); });
} }
public View getHeroElementForTransition() {
return mArchiveMode ? null : mStartNewConversationButton;
}
@VisibleForAnimation
public RecyclerView getRecyclerView() {
return mRecyclerView;
}
@Override @Override
public void startFullScreenPhotoViewer( public void startFullScreenPhotoViewer(
final Uri initialPhoto, final Rect initialPhotoBounds, final Uri photosUri) { final Uri initialPhoto, final Rect initialPhotoBounds, final Uri photosUri) {
@@ -79,8 +79,6 @@ public class ConversationListItemView extends FrameLayout implements OnClickList
private static String sPlusOneString; private static String sPlusOneString;
private static String sPlusNString; private static String sPlusNString;
private static final int SWIPE_DIRECTION_RIGHT = 2;
public interface HostInterface { public interface HostInterface {
boolean isConversationSelected(final String conversationId); boolean isConversationSelected(final String conversationId);
void onConversationClicked(final ConversationListItemData conversationListItemData, void onConversationClicked(final ConversationListItemData conversationListItemData,
@@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -27,47 +28,16 @@ import javax.annotation.concurrent.ThreadSafe;
@ThreadSafe @ThreadSafe
public class AudioLevelSource { public class AudioLevelSource {
private volatile int mSpeechLevel; private volatile int mSpeechLevel;
private volatile Listener mListener;
public static final int LEVEL_UNKNOWN = -1; public static final int LEVEL_UNKNOWN = -1;
public interface Listener {
void onSpeechLevel(int speechLevel);
}
public void setSpeechLevel(int speechLevel) { public void setSpeechLevel(int speechLevel) {
Preconditions.checkArgument(speechLevel >= 0 && speechLevel <= 100 || Preconditions.checkArgument(speechLevel >= 0 && speechLevel <= 100 ||
speechLevel == LEVEL_UNKNOWN); speechLevel == LEVEL_UNKNOWN);
mSpeechLevel = speechLevel; mSpeechLevel = speechLevel;
maybeNotify();
} }
public int getSpeechLevel() { public int getSpeechLevel() {
return mSpeechLevel; return mSpeechLevel;
} }
public void reset() {
setSpeechLevel(LEVEL_UNKNOWN);
}
public boolean isValid() {
return mSpeechLevel > 0;
}
private void maybeNotify() {
final Listener l = mListener;
if (l != null) {
l.onSpeechLevel(mSpeechLevel);
}
}
public synchronized void setListener(Listener listener) {
mListener = listener;
}
public synchronized void clearListener(Listener listener) {
if (mListener == listener) {
mListener = null;
}
}
} }
@@ -111,11 +111,6 @@ public class AudioRecordView extends FrameLayout implements
mHostInterface = hostInterface; mHostInterface = hostInterface;
} }
@VisibleForTesting
public void testSetMediaRecorder(final LevelTrackingMediaRecorder recorder) {
mMediaRecorder = recorder;
}
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
super.onFinishInflate(); super.onFinishInflate();
@@ -17,7 +17,6 @@
package com.android.messaging.ui.mediapicker.camerafocus; package com.android.messaging.ui.mediapicker.camerafocus;
import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@@ -47,7 +46,6 @@ public class PieItem {
private List<PieItem> mItems; private List<PieItem> mItems;
private Path mPath; private Path mPath;
private OnClickListener mOnClickListener; private OnClickListener mOnClickListener;
private float mAlpha;
// Gray out the view when disabled // Gray out the view when disabled
private static final float ENABLED_ALPHA = 1; private static final float ENABLED_ALPHA = 1;
@@ -87,12 +85,7 @@ public class PieItem {
return mPath; return mPath;
} }
public void setChangeAlphaWhenDisabled (boolean enable) {
mChangeAlphaWhenDisabled = enable;
}
public void setAlpha(float alpha) { public void setAlpha(float alpha) {
mAlpha = alpha;
mDrawable.setAlpha((int) (255 * alpha)); mDrawable.setAlpha((int) (255 * alpha));
} }
@@ -138,11 +131,6 @@ public class PieItem {
outer = outside; outer = outside;
} }
public void setFixedSlice(float center, float sweep) {
mCenter = center;
this.sweep = sweep;
}
public float getCenter() { public float getCenter() {
return mCenter; return mCenter;
} }
@@ -192,12 +180,4 @@ public class PieItem {
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
mDrawable.draw(canvas); mDrawable.draw(canvas);
} }
public void setImageResource(Context context, int resId) {
Drawable d = context.getResources().getDrawable(resId).mutate();
d.setBounds(mDrawable.getBounds());
mDrawable = d;
setAlpha(mAlpha);
}
} }
@@ -38,19 +38,6 @@ public class AccessibilityUtil {
return accessibilityManager.isTouchExplorationEnabled(); return accessibilityManager.isTouchExplorationEnabled();
} }
public static StringBuilder appendContentDescription(final Context context,
final StringBuilder contentDescription, final String val) {
if (sContentDescriptionDivider == null) {
sContentDescriptionDivider =
context.getResources().getString(R.string.enumeration_comma);
}
if (contentDescription.length() != 0) {
contentDescription.append(sContentDescriptionDivider);
}
contentDescription.append(val);
return contentDescription;
}
public static void announceForAccessibilityCompat( public static void announceForAccessibilityCompat(
final View view, @Nullable final AccessibilityManager accessibilityManager, final View view, @Nullable final AccessibilityManager accessibilityManager,
final int textResourceId) { final int textResourceId) {
@@ -18,8 +18,6 @@ package com.android.messaging.util;
import android.os.Looper; import android.os.Looper;
import java.util.Arrays;
public final class Assert { public final class Assert {
public @interface RunsOnMainThread {} public @interface RunsOnMainThread {}
public @interface DoesNotRunOnMainThread {} public @interface DoesNotRunOnMainThread {}
@@ -46,17 +44,6 @@ public final class Assert {
setIfEngBuild(); setIfEngBuild();
} }
/**
* Halt execution if this is not an eng build.
* <p>Intended for use in code paths that should be run only for tests and never on
* a real build.
* <p>Note that this will crash on a user build even though asserts don't normally
* crash on a user build.
*/
public static void isEngBuild() {
isTrueReleaseCheck(sIsEngBuild);
}
/** /**
* Halt execution if this isn't the case. * Halt execution if this isn't the case.
*/ */
@@ -75,15 +62,6 @@ public final class Assert {
} }
} }
/**
* Halt execution even in release builds if this isn't the case.
*/
public static void isTrueReleaseCheck(final boolean condition) {
if (!condition) {
fail("Expected condition to be true", true);
}
}
public static void equals(final int expected, final int actual) { public static void equals(final int expected, final int actual) {
if (expected != actual) { if (expected != actual) {
fail("Expected " + expected + " but got " + actual, false); fail("Expected " + expected + " but got " + actual, false);
@@ -103,15 +81,6 @@ public final class Assert {
} }
} }
public static void oneOf(final int actual, final int ...expected) {
for (int value : expected) {
if (actual == value) {
return;
}
}
fail("Expected value to be one of " + Arrays.toString(expected) + " but was " + actual);
}
public static void inRange( public static void inRange(
final int val, final int rangeMinInclusive, final int rangeMaxInclusive) { final int val, final int rangeMinInclusive, final int rangeMaxInclusive) {
if (val < rangeMinInclusive || val > rangeMaxInclusive) { if (val < rangeMinInclusive || val > rangeMaxInclusive) {
@@ -16,7 +16,6 @@
*/ */
package com.android.messaging.util; package com.android.messaging.util;
import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.net.Uri.Builder; import android.net.Uri.Builder;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -85,11 +84,6 @@ public class AvatarUriUtil {
public static final Uri DEFAULT_BACKGROUND_AVATAR = new Uri.Builder().scheme(SCHEME) public static final Uri DEFAULT_BACKGROUND_AVATAR = new Uri.Builder().scheme(SCHEME)
.authority(AUTHORITY).appendPath(TYPE_DEFAULT_BACKGROUND_URI).build(); .authority(AUTHORITY).appendPath(TYPE_DEFAULT_BACKGROUND_URI).build();
private static final Uri BLANK_SIM_INDICATOR_INCOMING_URI = createSimIconUri("",
false /* selected */, Color.TRANSPARENT, true /* incoming */);
private static final Uri BLANK_SIM_INDICATOR_OUTGOING_URI = createSimIconUri("",
false /* selected */, Color.TRANSPARENT, false /* incoming */);
/** /**
* Creates an avatar uri based on a list of ParticipantData. The list of participants may not * Creates an avatar uri based on a list of ParticipantData. The list of participants may not
* be null or empty. Depending on the size of the list either a group avatar uri will be create * be null or empty. Depending on the size of the list either a group avatar uri will be create
@@ -217,10 +211,6 @@ public class AvatarUriUtil {
return builder.build(); return builder.build();
} }
public static Uri getBlankSimIndicatorUri(final boolean incoming) {
return incoming ? BLANK_SIM_INDICATOR_INCOMING_URI : BLANK_SIM_INDICATOR_OUTGOING_URI;
}
/** /**
* Creates an avatar uri from the given local resource Uri, followed by a fallback Uri in case * Creates an avatar uri from the given local resource Uri, followed by a fallback Uri in case
* the local resource one could not be loaded. * the local resource one could not be loaded.
@@ -36,8 +36,6 @@ import com.android.messaging.ui.conversationlist.ConversationListActivity;
*/ */
public class BugleActivityUtil { public class BugleActivityUtil {
private static final int REQUEST_GOOGLE_PLAY_SERVICES = 0;
/** /**
* Determine if the requirements for the app to run are met. Log any Activity startup * Determine if the requirements for the app to run are met. Log any Activity startup
* analytics. * analytics.
@@ -1,112 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS 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 com.android.messaging.util;
/**
* Very simple circular array implementation.
*
* @param <E> The element type of this list.
* @LibraryInternal
*/
public class CircularArray<E> {
private int mNextWriter;
private boolean mHasWrapped;
private final int mMaxCount;
Object mList[];
/**
* Constructor for CircularArray.
*
* @param count Max elements to hold in the list.
*/
public CircularArray(int count) {
mMaxCount = count;
clear();
}
/**
* Reset the list.
*/
public void clear() {
mNextWriter = 0;
mHasWrapped = false;
mList = new Object[mMaxCount];
}
/**
* Add an element to the end of the list.
*
* @param object The object to add.
*/
public void add(E object) {
mList[mNextWriter] = object;
++mNextWriter;
if (mNextWriter == mMaxCount) {
mNextWriter = 0;
mHasWrapped = true;
}
}
/**
* Get the number of elements in the list. This will be 0 <= returned count <= max count
*
* @return Elements in the circular list.
*/
public int count() {
if (mHasWrapped) {
return mMaxCount;
} else {
return mNextWriter;
}
}
/**
* Return null if the list hasn't wrapped yet. Otherwise return the next object that would be
* overwritten. Can be useful to avoid extra allocations.
*
* @return
*/
@SuppressWarnings("unchecked")
public E getFree() {
if (!mHasWrapped) {
return null;
} else {
return (E) mList[mNextWriter];
}
}
/**
* Get the object at index. Index 0 is the oldest item inserted into the list. Index (count() -
* 1) is the newest.
*
* @param index Index to retrieve.
* @return Object at index.
*/
@SuppressWarnings("unchecked")
public E get(int index) {
if (mHasWrapped) {
int wrappedIndex = index + mNextWriter;
if (wrappedIndex >= mMaxCount) {
wrappedIndex -= mMaxCount;
}
return (E) mList[wrappedIndex];
} else {
return (E) mList[index];
}
}
}
@@ -45,10 +45,6 @@ public class ConnectivityUtil {
.createForSubscriptionId(subId); .createForSubscriptionId(subId);
} }
public int getCurrentServiceState() {
return mCurrentServiceState;
}
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override @Override
public void onServiceStateChanged(final ServiceState serviceState) { public void onServiceStateChanged(final ServiceState serviceState) {
@@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,7 +24,6 @@ import com.android.ex.chips.RecipientEntry;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.R; import com.android.messaging.R;
import com.android.messaging.datamodel.BugleRecipientEntry; import com.android.messaging.datamodel.BugleRecipientEntry;
import com.android.messaging.datamodel.data.ParticipantData;
/** /**
* Provides utility methods around creating RecipientEntry instance specific to Bugle's needs. * Provides utility methods around creating RecipientEntry instance specific to Bugle's needs.
@@ -105,11 +105,4 @@ public class ContactRecipientEntryUtils {
public static boolean isSendToDestinationContact(final RecipientEntry entry) { public static boolean isSendToDestinationContact(final RecipientEntry entry) {
return entry.getContactId() == CONTACT_ID_SENDTO_DESTINATION; return entry.getContactId() == CONTACT_ID_SENDTO_DESTINATION;
} }
/**
* Returns true if the given participant is a special send to number item.
*/
public static boolean isSendToDestinationContact(final ParticipantData participant) {
return participant.getContactId() == CONTACT_ID_SENDTO_DESTINATION;
}
} }
@@ -58,30 +58,19 @@ public final class ContentType {
public static final String IMAGE_PNG = "image/png"; public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp"; public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp";
public static final String AUDIO_UNSPECIFIED = "audio/*";
public static final String AUDIO_AAC = "audio/aac"; public static final String AUDIO_AAC = "audio/aac";
public static final String AUDIO_AMR = "audio/amr"; public static final String AUDIO_AMR = "audio/amr";
public static final String AUDIO_IMELODY = "audio/imelody";
public static final String AUDIO_MID = "audio/mid"; public static final String AUDIO_MID = "audio/mid";
public static final String AUDIO_MIDI = "audio/midi"; public static final String AUDIO_MIDI = "audio/midi";
public static final String AUDIO_MP3 = "audio/mp3"; public static final String AUDIO_MP3 = "audio/mp3";
public static final String AUDIO_MPEG3 = "audio/mpeg3";
public static final String AUDIO_MPEG = "audio/mpeg";
public static final String AUDIO_MPG = "audio/mpg";
public static final String AUDIO_MP4 = "audio/mp4"; public static final String AUDIO_MP4 = "audio/mp4";
public static final String AUDIO_MP4_LATM = "audio/mp4-latm";
public static final String AUDIO_X_MID = "audio/x-mid"; public static final String AUDIO_X_MID = "audio/x-mid";
public static final String AUDIO_X_MIDI = "audio/x-midi"; public static final String AUDIO_X_MIDI = "audio/x-midi";
public static final String AUDIO_X_MP3 = "audio/x-mp3"; public static final String AUDIO_X_MP3 = "audio/x-mp3";
public static final String AUDIO_X_MPEG3 = "audio/x-mpeg3";
public static final String AUDIO_X_MPEG = "audio/x-mpeg";
public static final String AUDIO_X_MPG = "audio/x-mpg";
public static final String AUDIO_3GPP = "audio/3gpp"; public static final String AUDIO_3GPP = "audio/3gpp";
public static final String AUDIO_X_WAV = "audio/x-wav"; public static final String AUDIO_X_WAV = "audio/x-wav";
public static final String AUDIO_OGG = "application/ogg"; public static final String AUDIO_OGG = "application/ogg";
public static final String MULTIPART_MIXED = "multipart/mixed";
public static final String VIDEO_UNSPECIFIED = "video/*"; public static final String VIDEO_UNSPECIFIED = "video/*";
public static final String VIDEO_3GP = "video/3gp"; public static final String VIDEO_3GP = "video/3gp";
public static final String VIDEO_3GPP = "video/3gpp"; public static final String VIDEO_3GPP = "video/3gpp";
@@ -95,10 +84,6 @@ public final class ContentType {
public static final String APP_SMIL = "application/smil"; public static final String APP_SMIL = "application/smil";
public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml"; public static final String APP_WAP_XHTML = "application/vnd.wap.xhtml+xml";
public static final String APP_XHTML = "application/xhtml+xml";
public static final String APP_DRM_CONTENT = "application/vnd.oma.drm.content";
public static final String APP_DRM_MESSAGE = "application/vnd.oma.drm.message";
// This class should never be instantiated. // This class should never be instantiated.
private ContentType() { private ContentType() {
@@ -136,16 +121,6 @@ public final class ContentType {
|| contentType.equalsIgnoreCase(TEXT_VCARD)); || contentType.equalsIgnoreCase(TEXT_VCARD));
} }
public static boolean isDrmType(final String contentType) {
return (null != contentType)
&& (contentType.equals(APP_DRM_CONTENT)
|| contentType.equals(APP_DRM_MESSAGE));
}
public static boolean isUnspecified(final String contentType) {
return (null != contentType) && contentType.endsWith("*");
}
/** /**
* If the content type is a type which can be displayed in the conversation list as a preview. * If the content type is a type which can be displayed in the conversation list as a preview.
*/ */
@@ -25,7 +25,6 @@ import android.text.TextUtils;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.R; import com.android.messaging.R;
import com.google.common.io.Files;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -71,55 +70,6 @@ public class FileUtil {
return getNewFile(directory, fileExtension, fileNameFormat); return getNewFile(directory, fileExtension, fileNameFormat);
} }
/** Delete everything below and including root */
public static void removeFileOrDirectory(File root) {
removeFileOrDirectoryExcept(root, null);
}
/** Delete everything below and including root except for the given file */
public static void removeFileOrDirectoryExcept(File root, File exclude) {
if (root.exists()) {
if (root.isDirectory()) {
for (File file : root.listFiles()) {
if (exclude == null || !file.equals(exclude)) {
removeFileOrDirectoryExcept(file, exclude);
}
}
root.delete();
} else if (root.isFile()) {
root.delete();
}
}
}
/**
* Move all files and folders under a directory into the target.
*/
public static void moveAllContentUnderDirectory(File sourceDir, File targetDir) {
if (sourceDir.isDirectory() && targetDir.isDirectory()) {
if (isSameOrSubDirectory(sourceDir, targetDir)) {
LogUtil.e(LogUtil.BUGLE_TAG, "Can't move directory content since the source " +
"directory is a parent of the target");
return;
}
for (File file : sourceDir.listFiles()) {
if (file.isDirectory()) {
final File dirTarget = new File(targetDir, file.getName());
dirTarget.mkdirs();
moveAllContentUnderDirectory(file, dirTarget);
} else {
try {
final File fileTarget = new File(targetDir, file.getName());
Files.move(file, fileTarget);
} catch (IOException e) {
LogUtil.e(LogUtil.BUGLE_TAG, "Failed to move files", e);
// Try proceed with the next file.
}
}
}
}
}
// Checks if the file is in /data, and don't allow any app to send personal information. // Checks if the file is in /data, and don't allow any app to send personal information.
// We're told it's possible to create world readable hardlinks to other apps private data // We're told it's possible to create world readable hardlinks to other apps private data
// so we ban all /data file uris. // so we ban all /data file uris.
@@ -35,7 +35,6 @@ import android.net.Uri;
import android.provider.MediaStore; import android.provider.MediaStore;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.datamodel.MediaScratchFileProvider; import com.android.messaging.datamodel.MediaScratchFileProvider;
@@ -169,15 +168,6 @@ public class ImageUtils {
} }
} }
/**
* Sets a drawable to the background of a view. setBackgroundDrawable() is deprecated since
* JB and replaced by setBackground().
*/
@SuppressWarnings("deprecation")
public static void setBackgroundDrawableOnView(final View view, final Drawable drawable) {
view.setBackground(drawable);
}
/** /**
* Based on the input bitmap bounds given by BitmapFactory.Options, compute the required * Based on the input bitmap bounds given by BitmapFactory.Options, compute the required
* sub-sampling size for loading a scaled down version of the bitmap to the required size * sub-sampling size for loading a scaled down version of the bitmap to the required size
@@ -1,60 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
* Copyright (C) 2024 The LineageOS 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 com.android.messaging.util;
import androidx.collection.LongSparseArray;
/**
* A space saving set for long values using v4 compat LongSparseArray
*/
public class LongSparseSet {
private static final Object THE_ONLY_VALID_VALUE = new Object();
private final LongSparseArray<Object> mSet = new LongSparseArray<>();
public LongSparseSet() {
}
/**
* @param key The element to check
* @return True if the element is in the set, false otherwise
*/
public boolean contains(long key) {
if (mSet.get(key, null/*default*/) == THE_ONLY_VALID_VALUE) {
return true;
}
return false;
}
/**
* Add an element to the set
*
* @param key The element to add
*/
public void add(long key) {
mSet.put(key, THE_ONLY_VALID_VALUE);
}
/**
* Remove an element from the set
*
* @param key The element to remove
*/
public void remove(long key) {
mSet.delete(key);
}
}
@@ -1,27 +0,0 @@
/*
* 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 com.android.messaging.util;
public class MaterialPalette{
public final int mPrimaryColor;
public final int mSecondaryColor;
public MaterialPalette(final int primaryColor, final int secondaryColor) {
mPrimaryColor = primaryColor;
mSecondaryColor = secondaryColor;
}
}
@@ -23,18 +23,4 @@ public class PendingIntentConstants {
public static final int SMS_NOTIFICATION_ID = 0; public static final int SMS_NOTIFICATION_ID = 0;
public static final int SMS_SECONDARY_USER_NOTIFICATION_ID = 1; public static final int SMS_SECONDARY_USER_NOTIFICATION_ID = 1;
public static final int MSG_SEND_ERROR = 2; public static final int MSG_SEND_ERROR = 2;
// Request codes
public static final int UPDATE_NOTIFICATIONS_ALARM_ACTION_ID = 100;
public static final int MIN_ASSIGNED_REQUEST_CODE = 1001;
// Logging
private static final String TAG = LogUtil.BUGLE_TAG;
private static final boolean VERBOSE = false;
// Internal Constants
private static final String NOTIFICATION_REQUEST_CODE_PREFS = "notificationRequestCodes.v1";
private static final String REQUEST_CODE_DELIMITER = "|";
private static final String MAX_REQUEST_CODE_KEY = "maxRequestCode";
} }
@@ -105,15 +105,6 @@ public class PhoneUtils {
return null; return null;
} }
/**
* Get number of SIM slots
*
* @return the SIM slot count
*/
public int getSimSlotCount() {
return mSubscriptionManager.getActiveSubscriptionInfoCountMax();
}
/** /**
* Get SIM's carrier name * Get SIM's carrier name
* *
@@ -134,15 +125,6 @@ public class PhoneUtils {
return null; return null;
} }
/**
* Check if there is SIM inserted on the device
*
* @return true if there is SIM inserted, false otherwise
*/
public boolean hasSim() {
return mSubscriptionManager.getActiveSubscriptionInfoCount() > 0;
}
/** /**
* Check if the SIM is roaming * Check if the SIM is roaming
* *
@@ -168,16 +150,6 @@ public class PhoneUtils {
return new int[]{mcc, mnc}; return new int[]{mcc, mnc};
} }
/**
* Get the mcc/mnc string
*
* @return the text of mccmnc string
*/
public String getSimOperatorNumeric() {
// For L_MR1 we return the canonicalized (xxxxxx) string
return getMccMncString(getMccMnc());
}
/** /**
* Get the SIM's self raw number, i.e. not canonicalized * Get the SIM's self raw number, i.e. not canonicalized
* *
@@ -581,39 +553,6 @@ public class PhoneUtils {
return getCanonicalBySimLocale(selfNumber); return getCanonicalBySimLocale(selfNumber);
} }
/**
* Get the SIM's phone number in NATIONAL format with only digits, used in sending
* as LINE1NOCOUNTRYCODE macro in mms_config
*
* @return all digits national format number of the SIM
*/
public String getSimNumberNoCountryCode() {
String selfNumber = null;
try {
selfNumber = getSelfRawNumber(false/*allowOverride*/);
} catch (IllegalStateException e) {
// continue
}
if (selfNumber == null) {
selfNumber = "";
}
final String country = getSimCountry();
final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
try {
final PhoneNumber phoneNumber = phoneNumberUtil.parse(selfNumber, country);
if (phoneNumber != null && phoneNumberUtil.isValidNumber(phoneNumber)) {
return phoneNumberUtil
.format(phoneNumber, PhoneNumberFormat.NATIONAL)
.replaceAll("\\D", "");
}
} catch (final NumberParseException e) {
LogUtil.e(TAG, "PhoneUtils.getSimNumberNoCountryCode(): Not able to parse phone number "
+ LogUtil.sanitizePII(selfNumber) + " for country " + country);
}
return selfNumber;
}
/** /**
* Format a phone number for displaying, using system locale country. * Format a phone number for displaying, using system locale country.
* If the country code matches between the system locale and the input phone number, * If the country code matches between the system locale and the input phone number,
@@ -1,129 +0,0 @@
/*
* 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 com.android.messaging.util;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import androidx.appcompat.graphics.drawable.DrawableWrapperCompat;
import androidx.appcompat.widget.SwitchCompat;
import android.util.TypedValue;
/* Most methods in this file are copied from
* v7/appcompat/src/androidx.appcompat.internal/widget/TintManager.java. It would be better if
* we could have just extended the TintManager but this is a final class that we do not have
* access to. */
/**
* Util methods for the SwitchCompat widget
*/
public class SwitchCompatUtils {
/**
* Given a color and a SwitchCompat view, updates the SwitchCompat to appear with the appropiate
* color when enabled and checked
*/
public static void updateSwitchCompatColor(SwitchCompat switchCompat, final int color) {
final Context context = switchCompat.getContext();
final TypedValue typedValue = new TypedValue();
switchCompat.setThumbDrawable(getColorTintedDrawable(switchCompat.getThumbDrawable(),
getSwitchThumbColorStateList(context, color, typedValue),
PorterDuff.Mode.MULTIPLY));
switchCompat.setTrackDrawable(getColorTintedDrawable(switchCompat.getTrackDrawable(),
getSwitchTrackColorStateList(context, color, typedValue), PorterDuff.Mode.SRC_IN));
}
private static Drawable getColorTintedDrawable(Drawable oldDrawable,
final ColorStateList colorStateList, final PorterDuff.Mode mode) {
final int[] thumbState = oldDrawable.isStateful() ? oldDrawable.getState() : null;
if (oldDrawable instanceof DrawableWrapperCompat) {
oldDrawable = ((DrawableWrapperCompat) oldDrawable).getDrawable();
}
final Drawable newDrawable = new TintDrawableWrapper(oldDrawable, colorStateList, mode);
if (thumbState != null) {
newDrawable.setState(thumbState);
}
return newDrawable;
}
private static ColorStateList getSwitchThumbColorStateList(final Context context,
final int color, final TypedValue typedValue) {
final int[][] states = new int[3][];
final int[] colors = new int[3];
int i = 0;
// Disabled state
states[i] = new int[] { -android.R.attr.state_enabled };
colors[i] = getColor(Color.parseColor("#ffbdbdbd"), 1f);
i++;
states[i] = new int[] { android.R.attr.state_checked };
colors[i] = color;
i++;
// Default enabled state
states[i] = new int[0];
colors[i] = getThemeAttrColor(context, typedValue,
androidx.appcompat.R.attr.colorSwitchThumbNormal);
i++;
return new ColorStateList(states, colors);
}
private static ColorStateList getSwitchTrackColorStateList(final Context context,
final int color, final TypedValue typedValue) {
final int[][] states = new int[3][];
final int[] colors = new int[3];
int i = 0;
// Disabled state
states[i] = new int[] { -android.R.attr.state_enabled };
colors[i] = getThemeAttrColor(context, typedValue, android.R.attr.colorForeground, 0.1f);
i++;
states[i] = new int[] { android.R.attr.state_checked };
colors[i] = getColor(color, 0.3f);
i++;
// Default enabled state
states[i] = new int[0];
colors[i] = getThemeAttrColor(context, typedValue, android.R.attr.colorForeground, 0.3f);
i++;
return new ColorStateList(states, colors);
}
private static int getThemeAttrColor(final Context context, final TypedValue typedValue,
final int attr) {
if (context.getTheme().resolveAttribute(attr, typedValue, true)) {
if (typedValue.type >= TypedValue.TYPE_FIRST_INT
&& typedValue.type <= TypedValue.TYPE_LAST_INT) {
return typedValue.data;
} else if (typedValue.type == TypedValue.TYPE_STRING) {
return context.getResources().getColor(typedValue.resourceId);
}
}
return 0;
}
private static int getThemeAttrColor(final Context context, final TypedValue typedValue,
final int attr, final float alpha) {
final int color = getThemeAttrColor(context, typedValue, attr);
return getColor(color, alpha);
}
private static int getColor(int color, float alpha) {
final int originalAlpha = Color.alpha(color);
// Return the color, multiplying the original alpha by the disabled value
return (color & 0x00ffffff) | (Math.round(originalAlpha * alpha) << 24);
}
}
+3 -105
View File
@@ -20,22 +20,12 @@ package com.android.messaging.util;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.text.Html;
import android.text.Spanned;
import android.text.TextPaint; import android.text.TextPaint;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.URLSpan;
import android.view.Gravity; import android.view.Gravity;
import android.view.Surface;
import android.view.View; import android.view.View;
import android.view.View.OnLayoutChangeListener; import android.view.View.OnLayoutChangeListener;
import android.view.animation.Animation; import android.view.animation.Animation;
@@ -45,16 +35,17 @@ import android.view.animation.ScaleAnimation;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.R; import com.android.messaging.R;
import com.android.messaging.ui.SnackBar; import com.android.messaging.ui.SnackBar;
import com.android.messaging.ui.SnackBar.Placement; import com.android.messaging.ui.SnackBar.Placement;
import com.android.messaging.ui.conversationlist.ConversationListActivity;
import com.android.messaging.ui.SnackBarInteraction; import com.android.messaging.ui.SnackBarInteraction;
import com.android.messaging.ui.SnackBarManager; import com.android.messaging.ui.SnackBarManager;
import com.android.messaging.ui.UIIntents; import com.android.messaging.ui.UIIntents;
import java.lang.reflect.Field;
import java.util.List; import java.util.List;
public class UiUtils { public class UiUtils {
@@ -62,10 +53,6 @@ public class UiUtils {
public static final int MEDIAPICKER_TRANSITION_DURATION = public static final int MEDIAPICKER_TRANSITION_DURATION =
getApplicationContext().getResources().getInteger( getApplicationContext().getResources().getInteger(
R.integer.mediapicker_transition_duration); R.integer.mediapicker_transition_duration);
/** Short transition duration in ms */
public static final int ASYNCIMAGE_TRANSITION_DURATION =
getApplicationContext().getResources().getInteger(
R.integer.asyncimage_transition_duration);
/** Compose transition duration in ms */ /** Compose transition duration in ms */
public static final int COMPOSE_TRANSITION_DURATION = public static final int COMPOSE_TRANSITION_DURATION =
getApplicationContext().getResources().getInteger( getApplicationContext().getResources().getInteger(
@@ -272,34 +259,6 @@ public class UiUtils {
Color.rgb(blendedRed, blendedGreen, blendedBlue)); Color.rgb(blendedRed, blendedGreen, blendedBlue));
} }
public static void lockOrientation(final Activity activity) {
final int orientation = activity.getResources().getConfiguration().orientation;
final int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
// rotation tracks the rotation of the device from its natural orientation
// orientation tracks whether the screen is landscape or portrait.
// It is possible to have a rotation of 0 (device in its natural orientation) in portrait
// (phone), or in landscape (tablet), so we have to check both values to determine what to
// pass to setRequestedOrientation.
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
} else if (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270) {
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}
}
}
public static void unlockOrientation(final Activity activity) {
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
public static boolean isRtlMode() { public static boolean isRtlMode() {
return Factory.get().getApplicationContext().getResources() return Factory.get().getApplicationContext().getResources()
.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; .getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
@@ -350,67 +309,6 @@ public class UiUtils {
return phoneUtils.isDefaultSmsApp(); return phoneUtils.isDefaultSmsApp();
} }
/*
* Removes all html markup from the text and replaces links with the the text and a text version
* of the href.
* @param htmlText HTML markup text
* @return Sanitized string with link hrefs inlined
*/
public static String stripHtml(final String htmlText) {
final StringBuilder result = new StringBuilder();
final Spanned markup = Html.fromHtml(htmlText);
final String strippedText = markup.toString();
final URLSpan[] links = markup.getSpans(0, markup.length() - 1, URLSpan.class);
int currentIndex = 0;
for (final URLSpan link : links) {
final int spanStart = markup.getSpanStart(link);
final int spanEnd = markup.getSpanEnd(link);
if (spanStart > currentIndex) {
result.append(strippedText, currentIndex, spanStart);
}
final String displayText = strippedText.substring(spanStart, spanEnd);
final String linkText = link.getURL();
result.append(getApplicationContext().getString(R.string.link_display_format,
displayText, linkText));
currentIndex = spanEnd;
}
if (strippedText.length() > currentIndex) {
result.append(strippedText, currentIndex, strippedText.length());
}
return result.toString();
}
public static void setActionBarShadowVisibility(final AppCompatActivity activity, final boolean visible) {
final ActionBar actionBar = activity.getSupportActionBar();
actionBar.setElevation(visible ?
activity.getResources().getDimensionPixelSize(R.dimen.action_bar_elevation) :
0);
final View actionBarView = activity.getWindow().getDecorView().findViewById(
androidx.appcompat.R.id.decor_content_parent);
if (actionBarView != null) {
// AppCompatActionBar has one drawable Field, which is the shadow for the action bar
// set the alpha on that drawable manually
final Field[] fields = actionBarView.getClass().getDeclaredFields();
try {
for (final Field field : fields) {
if (field.getType().equals(Drawable.class)) {
field.setAccessible(true);
final Drawable shadowDrawable = (Drawable) field.get(actionBarView);
if (shadowDrawable != null) {
shadowDrawable.setAlpha(visible ? 255 : 0);
actionBarView.invalidate();
return;
}
}
}
} catch (final IllegalAccessException ex) {
// Not expected, we should avoid this via field.setAccessible(true) above
LogUtil.e(LogUtil.BUGLE_TAG, "Error setting shadow visibility", ex);
}
}
}
/** /**
* Get the activity that's hosting the view, typically casting view.getContext() as an Activity * Get the activity that's hosting the view, typically casting view.getContext() as an Activity
* is sufficient, but sometimes the context is a context wrapper, in which case we need to case * is sufficient, but sometimes the context is a context wrapper, in which case we need to case