Messaging: Remove unused code
Change-Id: I8093bb3e305ae323f7069bf42f9e83f8c8647cc7
This commit is contained in:
@@ -46,17 +46,7 @@
|
||||
<string name="wireless_alerts_key" translatable="false">buglesub_wireless_alerts_key</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="asyncimage_transition_duration">300</integer><!-- ms -->
|
||||
<integer name="compose_transition_duration">300</integer><!-- ms -->
|
||||
<integer name="camera_shutter_duration">200</integer><!-- ms -->
|
||||
<fraction name="camera_shutter_max_alpha">70%</fraction>
|
||||
|
||||
@@ -116,7 +116,6 @@
|
||||
|
||||
<dimen name="vcard_detail_group_indicator_width">40dp</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_top_margin">20dp</dimen>
|
||||
<dimen name="list_empty_text_left_right_margin">60dp</dimen>
|
||||
|
||||
@@ -480,24 +480,4 @@ public class MmsHttpClient {
|
||||
}
|
||||
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
|
||||
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
|
||||
*
|
||||
|
||||
@@ -98,23 +98,11 @@ abstract class MmsRequest implements Parcelable {
|
||||
// Thread pool for transferring PDU with MMS apps
|
||||
protected final ExecutorService mPduTransferExecutor = Executors.newCachedThreadPool();
|
||||
|
||||
// Whether this request should acquire wake lock
|
||||
private boolean mUseWakeLock;
|
||||
|
||||
protected MmsRequest(final String locationUrl, final Uri pduUri,
|
||||
final PendingIntent pendingIntent) {
|
||||
mLocationUrl = locationUrl;
|
||||
mPduUri = pduUri;
|
||||
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
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeByte((byte) (mUseWakeLock ? 1 : 0));
|
||||
parcel.writeString(mLocationUrl);
|
||||
parcel.writeParcelable(mPduUri, 0);
|
||||
parcel.writeParcelable(mPendingIntent, 0);
|
||||
@@ -384,7 +371,6 @@ abstract class MmsRequest implements Parcelable {
|
||||
|
||||
protected MmsRequest(final Parcel in) {
|
||||
final ClassLoader classLoader = MmsRequest.class.getClassLoader();
|
||||
mUseWakeLock = in.readByte() != 0;
|
||||
mLocationUrl = in.readString();
|
||||
mPduUri = in.readParcelable(classLoader);
|
||||
mPendingIntent = in.readParcelable(classLoader);
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.telephony.SmsManager;
|
||||
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_MYPID = "mypid";
|
||||
|
||||
private static final String WAKELOCK_ID = "mmslib_wakelock";
|
||||
|
||||
/**
|
||||
* Thread pool size for each request queue
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -74,25 +64,6 @@ public class MmsService extends Service {
|
||||
*/
|
||||
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
|
||||
*
|
||||
@@ -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
|
||||
private static volatile int sMyPid = -1;
|
||||
|
||||
@@ -253,28 +178,6 @@ public class MmsService extends Service {
|
||||
// Service stop task
|
||||
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
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@@ -331,9 +234,6 @@ public class MmsService extends Service {
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Unexpected execution failure", e);
|
||||
} finally {
|
||||
if (request.getUseWakeLock()) {
|
||||
releaseWakeLock();
|
||||
}
|
||||
releaseService();
|
||||
}
|
||||
});
|
||||
@@ -344,9 +244,6 @@ public class MmsService extends Service {
|
||||
Log.w(TAG, "Executing request failed " + e);
|
||||
request.returnResult(this, SmsManager.MMS_ERROR_UNSPECIFIED,
|
||||
null/*response*/, 0/*httpStatusCode*/);
|
||||
if (request.getUseWakeLock()) {
|
||||
releaseWakeLock();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Empty request");
|
||||
@@ -434,7 +331,6 @@ public class MmsService extends Service {
|
||||
if (stopped != null) {
|
||||
if (stopped) {
|
||||
Log.i(TAG, "Service successfully stopped");
|
||||
verifyWakeLockNotHeld();
|
||||
} else {
|
||||
Log.i(TAG, "Service stopping cancelled");
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Encoded-string-value = Text-string | Value-length Char-set Text-string
|
||||
@@ -224,43 +223,6 @@ public class EncodedStringValue implements Cloneable {
|
||||
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) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
|
||||
@@ -116,7 +116,6 @@ public class BugleApplication extends Application implements UncaughtExceptionHa
|
||||
MmsManager.setApnSettingsLoader(new BugleApnSettingsLoader(context));
|
||||
MmsManager.setCarrierConfigValuesLoader(carrierConfigValuesLoader);
|
||||
MmsManager.setUserAgentInfoLoader(new BugleUserAgentInfoLoader(context));
|
||||
MmsManager.setUseWakeLock(true);
|
||||
}
|
||||
|
||||
public static void updateAppConfig(final Context context) {
|
||||
|
||||
@@ -57,7 +57,6 @@ class FactoryImpl extends Factory {
|
||||
private MediaResourceManager mMediaResourceManager;
|
||||
private MediaCacheManager mMediaCacheManager;
|
||||
private ContactContentObserver mContactContentObserver;
|
||||
private PhoneUtils mPhoneUtils;
|
||||
private MediaUtil mMediaUtil;
|
||||
private SparseArray<BugleSubscriptionPrefs> mSubscriptionPrefs;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees up memory associated with phone number to participant id matching.
|
||||
*/
|
||||
@DoesNotRunOnMainThread
|
||||
public static void clearParticipantIdCache() {
|
||||
Assert.isNotMainThread();
|
||||
synchronized (sNormalizedPhoneNumberToParticipantIdCache) {
|
||||
sNormalizedPhoneNumberToParticipantIdCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@DoesNotRunOnMainThread
|
||||
public static ArrayList<String> getRecipientsForConversation(final DatabaseWrapper dbWrapper,
|
||||
final String conversationId) {
|
||||
@@ -940,21 +929,6 @@ public class BugleDatabaseOperations {
|
||||
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
|
||||
public static MessageData readMessageData(final DatabaseWrapper dbWrapper,
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
final CharSequence text) {
|
||||
if (text == null) {
|
||||
@@ -1166,7 +1143,6 @@ public abstract class MessageNotificationState extends NotificationState {
|
||||
final ArrayList<Integer> failedMessages = new ArrayList<>();
|
||||
|
||||
int cursorPosition = -1;
|
||||
final long when = 0;
|
||||
|
||||
messageDataCursor.moveToPosition(-1);
|
||||
while (messageDataCursor.moveToNext()) {
|
||||
@@ -1195,7 +1171,6 @@ public abstract class MessageNotificationState extends NotificationState {
|
||||
|
||||
CharSequence line1;
|
||||
CharSequence line2;
|
||||
final boolean isRichContent = false;
|
||||
ConversationIdSet conversationIds = null;
|
||||
PendingIntent destinationIntent;
|
||||
if (failedMessages.size() == 1) {
|
||||
@@ -1222,12 +1197,6 @@ public abstract class MessageNotificationState extends NotificationState {
|
||||
}
|
||||
line1 = resources.getString(failureStringId);
|
||||
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 {
|
||||
// We have notifications for multiple conversation, go to the conversation
|
||||
// list.
|
||||
@@ -1265,29 +1234,18 @@ public abstract class MessageNotificationState extends NotificationState {
|
||||
builder
|
||||
.setContentTitle(line1)
|
||||
.setTicker(line1)
|
||||
.setWhen(when > 0 ? when : System.currentTimeMillis())
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setSmallIcon(R.drawable.ic_failed_light)
|
||||
.setDeleteIntent(pendingIntentForDelete)
|
||||
.setContentIntent(destinationIntent)
|
||||
.setSound(UriUtil.getUriForResourceId(context, R.raw.message_failure));
|
||||
if (isRichContent && !TextUtils.isEmpty(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(
|
||||
BugleNotifications.buildNotificationTag(
|
||||
PendingIntentConstants.MSG_SEND_ERROR, null),
|
||||
PendingIntentConstants.MSG_SEND_ERROR,
|
||||
builder.build());
|
||||
}
|
||||
} else {
|
||||
notificationManager.cancel(
|
||||
BugleNotifications.buildNotificationTag(
|
||||
|
||||
@@ -254,15 +254,6 @@ public class MessagingContentProvider extends ContentProvider {
|
||||
@Override
|
||||
public Cursor query(@NonNull final Uri uri, final String[] projection, String selection,
|
||||
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();
|
||||
|
||||
String[] queryArgs = selectionArgs;
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
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.ActionExecutedListener;
|
||||
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.
|
||||
* {@link #requestBackgroundWork} needs to be called for this method to
|
||||
* be called. {@link #processBackgroundFailure} will be called on the Action service thread
|
||||
* if this method throws {@link DataModelException}.
|
||||
* be called.
|
||||
* @return response that is to be passed to {@link #processBackgroundResponse}
|
||||
*/
|
||||
protected Bundle doBackgroundWork() throws DataModelException {
|
||||
protected Bundle doBackgroundWork() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ import androidx.core.app.JobIntentService;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.datamodel.DataModel;
|
||||
import com.android.messaging.datamodel.DataModelException;
|
||||
import com.android.messaging.util.Assert;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.LoggingTimer;
|
||||
@@ -140,18 +139,9 @@ public class BackgroundWorkerService extends JobIntentService {
|
||||
} catch (final Exception exception) {
|
||||
final boolean retry = false;
|
||||
LogUtil.e(TAG, "Error in background worker", exception);
|
||||
if (!(exception instanceof DataModelException)) {
|
||||
// DataModelException is expected (sort-of) and handled in handleFailureFromWorker
|
||||
// below, but other exceptions should crash ENG builds
|
||||
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) {
|
||||
ThreadUtil.getMainThreadHandler().post(() -> Toast.makeText(getApplicationContext(),
|
||||
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.BugleNotifications;
|
||||
import com.android.messaging.datamodel.DataModel;
|
||||
import com.android.messaging.datamodel.DataModelException;
|
||||
import com.android.messaging.datamodel.DatabaseHelper;
|
||||
import com.android.messaging.datamodel.DatabaseHelper.MessageColumns;
|
||||
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
|
||||
// the local DB first, notify the UI, and then delete from telephony.
|
||||
@Override
|
||||
protected Bundle doBackgroundWork() throws DataModelException {
|
||||
protected Bundle doBackgroundWork() {
|
||||
final DatabaseWrapper db = DataModel.get().getDatabase();
|
||||
|
||||
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 "
|
||||
+ conversationId);
|
||||
|
||||
BugleActionToasts.onConversationDeleted();
|
||||
|
||||
// Remove notifications if necessary
|
||||
BugleNotifications.update(true /* silent */, null /* conversationId */,
|
||||
BugleNotifications.UPDATE_MESSAGES);
|
||||
|
||||
@@ -34,7 +34,6 @@ import com.android.messaging.Factory;
|
||||
import com.android.messaging.datamodel.BugleDatabaseOperations;
|
||||
import com.android.messaging.datamodel.BugleNotifications;
|
||||
import com.android.messaging.datamodel.DataModel;
|
||||
import com.android.messaging.datamodel.DataModelException;
|
||||
import com.android.messaging.datamodel.DatabaseWrapper;
|
||||
import com.android.messaging.datamodel.MessagingContentProvider;
|
||||
import com.android.messaging.datamodel.MmsFileProvider;
|
||||
@@ -211,7 +210,7 @@ public class ProcessDownloadedMmsAction extends Action {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doBackgroundWork() throws DataModelException {
|
||||
protected Bundle doBackgroundWork() {
|
||||
final Context context = Factory.get().getApplicationContext();
|
||||
final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_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.BugleNotifications;
|
||||
import com.android.messaging.datamodel.DataModel;
|
||||
import com.android.messaging.datamodel.DataModelException;
|
||||
import com.android.messaging.datamodel.DatabaseWrapper;
|
||||
import com.android.messaging.datamodel.MessagingContentProvider;
|
||||
import com.android.messaging.datamodel.SyncManager;
|
||||
@@ -161,7 +160,7 @@ public class ReceiveMmsMessageAction extends Action implements Parcelable {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bundle doBackgroundWork() throws DataModelException {
|
||||
protected Bundle doBackgroundWork() {
|
||||
final Context context = Factory.get().getApplicationContext();
|
||||
final int subId = actionParameters.getInt(KEY_SUB_ID, ParticipantData.DEFAULT_SELF_SUB_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 java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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 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 {
|
||||
void onConversationMessagesCursorUpdated(ConversationData data, Cursor cursor,
|
||||
@Nullable ConversationMessageData newestMessage, boolean isSync);
|
||||
|
||||
@@ -477,10 +477,6 @@ public class ConversationMessageData {
|
||||
return mProtocol == (MessageData.PROTOCOL_SMS);
|
||||
}
|
||||
|
||||
final int getProtocol() {
|
||||
return mProtocol;
|
||||
}
|
||||
|
||||
public final int getStatus() {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
@@ -639,14 +639,6 @@ public class MessageData implements Parcelable {
|
||||
|| 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) {
|
||||
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) {
|
||||
mRawStatus = rawStatus;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
@@ -25,7 +26,6 @@ import com.android.messaging.util.ImageUtils;
|
||||
import com.android.messaging.util.UriUtil;
|
||||
|
||||
public class AvatarRequestDescriptor extends UriImageRequestDescriptor {
|
||||
final boolean isWearBackground;
|
||||
|
||||
public AvatarRequestDescriptor(final Uri uri, final int desiredWidth,
|
||||
final int desiredHeight) {
|
||||
@@ -45,7 +45,6 @@ public class AvatarRequestDescriptor extends UriImageRequestDescriptor {
|
||||
ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */);
|
||||
Assert.isTrue(uri == null || UriUtil.isLocalResourceUri(uri) ||
|
||||
AvatarUriUtil.isAvatarUri(uri));
|
||||
this.isWearBackground = isWearBackground;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,12 +79,10 @@ public class NetworkUriImageRequest<D extends UriImageRequestDescriptor> extends
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Bitmap loadBitmapInternal() throws IOException {
|
||||
public Bitmap loadBitmapInternal() {
|
||||
Assert.isNotMainThread();
|
||||
|
||||
InputStream inputStream = null;
|
||||
Bitmap bitmap = null;
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
@@ -109,9 +107,6 @@ public class NetworkUriImageRequest<D extends UriImageRequestDescriptor> extends
|
||||
"IOException trying to get inputStream for image with url: "
|
||||
+ mDescriptor.uri, e);
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package com.android.messaging.mmslib;
|
||||
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.provider.BaseColumns;
|
||||
|
||||
@@ -39,49 +37,8 @@ public final class Downloads {
|
||||
* @hide
|
||||
*/
|
||||
public static final class Impl implements BaseColumns {
|
||||
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";
|
||||
private Impl() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 =
|
||||
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.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* handled.
|
||||
*/
|
||||
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.
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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.UnsupportedEncodingException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Encoded-string-value = Text-string | Value-length Char-set Text-string
|
||||
@@ -239,43 +238,6 @@ public class EncodedStringValue implements Cloneable {
|
||||
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) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
|
||||
@@ -207,13 +207,6 @@ public class PduParser {
|
||||
// or "application/vnd.wap.multipart.related"
|
||||
// or "application/vnd.wap.multipart.alternative"
|
||||
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;
|
||||
case PduHeaders.MESSAGE_TYPE_DELIVERY_IND:
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
@@ -77,21 +76,6 @@ public class PduPersister {
|
||||
public static final String TEMPORARY_DRM_OBJECT_URI =
|
||||
"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";
|
||||
|
||||
private static PduPersister sPersister;
|
||||
@@ -1046,244 +1030,6 @@ public class PduPersister {
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
@@ -17,46 +18,11 @@
|
||||
|
||||
package com.android.messaging.mmslib.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.drm.DrmManagerClient;
|
||||
import android.util.Log;
|
||||
|
||||
public class DownloadDrmHelper {
|
||||
private static final String TAG = "DownloadDrmHelper";
|
||||
|
||||
/** The MIME type of special DRM files */
|
||||
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
|
||||
*
|
||||
@@ -66,46 +32,4 @@ public class DownloadDrmHelper {
|
||||
public static boolean isDrmConvertNeeded(String 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.R;
|
||||
import com.android.messaging.datamodel.BugleNotifications;
|
||||
import com.android.messaging.datamodel.MessageNotificationState;
|
||||
import com.android.messaging.datamodel.NoConfirmationSmsSendService;
|
||||
import com.android.messaging.datamodel.action.ReceiveSmsMessageAction;
|
||||
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() {
|
||||
final Context context = Factory.get().getApplicationContext();
|
||||
final Resources resources = context.getResources();
|
||||
|
||||
@@ -17,27 +17,19 @@
|
||||
|
||||
package com.android.messaging.sms;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.provider.Telephony;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.data.ParticipantData;
|
||||
import com.android.messaging.util.LogUtil;
|
||||
import com.android.messaging.util.PhoneUtils;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
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
|
||||
@@ -86,61 +78,7 @@ public class ApnDatabase extends SQLiteOpenHelper {
|
||||
Telephony.Carriers.MVNO_MATCH_DATA + " TEXT," +
|
||||
Telephony.Carriers.SUBSCRIPTION_ID + " INTEGER DEFAULT " +
|
||||
ParticipantData.DEFAULT_SELF_SUB_ID + ");";
|
||||
|
||||
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
|
||||
@@ -178,105 +116,6 @@ public class ApnDatabase extends SQLiteOpenHelper {
|
||||
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
|
||||
public void onOpen(final SQLiteDatabase db) {
|
||||
super.onOpen(db);
|
||||
@@ -350,13 +189,4 @@ public class ApnDatabase extends SQLiteOpenHelper {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
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)
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.content.Intent;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.content.res.Resources;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.net.Uri;
|
||||
@@ -38,8 +37,6 @@ import android.provider.Telephony.Threads;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.text.TextUtils;
|
||||
import android.text.util.Rfc822Token;
|
||||
import android.text.util.Rfc822Tokenizer;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.R;
|
||||
@@ -1379,45 +1376,6 @@ public class MmsUtils {
|
||||
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
|
||||
*
|
||||
@@ -1510,25 +1468,6 @@ public class MmsUtils {
|
||||
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");
|
||||
|
||||
/**
|
||||
@@ -2045,28 +1984,6 @@ public class MmsUtils {
|
||||
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,
|
||||
final byte[] pushData, final int subId, final String subPhoneNumber) {
|
||||
// Parse data
|
||||
@@ -2089,20 +2006,6 @@ public class MmsUtils {
|
||||
switch (type) {
|
||||
case PduHeaders.MESSAGE_TYPE_DELIVERY_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);
|
||||
break;
|
||||
}
|
||||
@@ -2125,24 +2028,6 @@ public class MmsUtils {
|
||||
}
|
||||
final String[] dups = getDupNotifications(context, nInd);
|
||||
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;
|
||||
try {
|
||||
inboxUri = p.persist(pdu, Mms.Inbox.CONTENT_URI, subId, subPhoneNumber,
|
||||
@@ -2441,12 +2326,6 @@ public class MmsUtils {
|
||||
switch (rawStatus) {
|
||||
case PduHeaders.RESPONSE_STATUS_ERROR_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;
|
||||
break;
|
||||
case PduHeaders.RESPONSE_STATUS_ERROR_SENDING_ADDRESS_UNRESOLVED:
|
||||
@@ -2463,8 +2342,6 @@ public class MmsUtils {
|
||||
stringResId = R.string.mms_failure_outgoing_content;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,7 @@ public class SmsSender {
|
||||
// This should be called from a RequestWriter queue thread
|
||||
public static SendResult sendMessage(final Context context, final int subId, String dest,
|
||||
String message, final String serviceCenter, final boolean requireDeliveryReport,
|
||||
final Uri messageUri) throws SmsException {
|
||||
final Uri messageUri) throws Exception {
|
||||
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
|
||||
LogUtil.v(TAG, "SmsSender: sending message. " +
|
||||
"dest=" + dest + " message=" + message +
|
||||
@@ -191,7 +191,7 @@ public class SmsSender {
|
||||
" requestId=" + messageUri);
|
||||
}
|
||||
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
|
||||
// Or sanitize the dest if dest is a number
|
||||
@@ -208,13 +208,13 @@ public class SmsSender {
|
||||
dest = PhoneNumberUtils.stripSeparators(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
|
||||
final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager();
|
||||
final ArrayList<String> messages = smsManager.divideMessage(message);
|
||||
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
|
||||
final SendResult pendingResult = new SendResult(messages.size());
|
||||
@@ -252,7 +252,7 @@ public class SmsSender {
|
||||
// Actually sending the message using SmsManager
|
||||
private static void sendInternal(final Context context, final int subId, String dest,
|
||||
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);
|
||||
final SmsManager smsManager = PhoneUtils.get(subId).getSmsManager();
|
||||
final int messageCount = messages.size();
|
||||
@@ -295,7 +295,7 @@ public class SmsSender {
|
||||
dest, serviceCenter, messages, sentIntents, deliveryIntents);
|
||||
}
|
||||
} 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 com.android.messaging.R;
|
||||
import com.android.messaging.util.ImageUtils;
|
||||
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
|
||||
// feedback don't get accidentally snapshotted.
|
||||
final Drawable viewBackground = view.getBackground();
|
||||
ImageUtils.setBackgroundDrawableOnView(view, null);
|
||||
view.setBackground(null);
|
||||
view.draw(new Canvas(viewBitmap));
|
||||
ImageUtils.setBackgroundDrawableOnView(view, viewBackground);
|
||||
view.setBackground(viewBackground);
|
||||
return viewBitmap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.ui.BugleActionBarActivity;
|
||||
@@ -82,14 +81,10 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
|
||||
public static class ApplicationSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private String mNotificationsPreferenceKey;
|
||||
private Preference mNotificationsPreference;
|
||||
private String mSmsDisabledPrefKey;
|
||||
private Preference mSmsDisabledPreference;
|
||||
private String mSmsEnabledPrefKey;
|
||||
private Preference mSmsEnabledPreference;
|
||||
private boolean mIsSmsPreferenceClicked;
|
||||
private String mSwipeRightToDeleteConversationkey;
|
||||
private SwitchPreferenceCompat mSwipeRightToDeleteConversationPreference;
|
||||
|
||||
public ApplicationSettingsFragment() {
|
||||
// Required empty constructor
|
||||
@@ -103,16 +98,10 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
|
||||
|
||||
mNotificationsPreferenceKey =
|
||||
getString(R.string.notifications_pref_key);
|
||||
mNotificationsPreference = findPreference(mNotificationsPreferenceKey);
|
||||
mSmsDisabledPrefKey = getString(R.string.sms_disabled_pref_key);
|
||||
mSmsDisabledPreference = findPreference(mSmsDisabledPrefKey);
|
||||
mSmsEnabledPrefKey = getString(R.string.sms_enabled_pref_key);
|
||||
mSmsEnabledPreference = findPreference(mSmsEnabledPrefKey);
|
||||
mSwipeRightToDeleteConversationkey = getString(
|
||||
R.string.swipe_right_deletes_conversation_key);
|
||||
mSwipeRightToDeleteConversationPreference =
|
||||
(SwitchPreferenceCompat) findPreference(mSwipeRightToDeleteConversationkey);
|
||||
mIsSmsPreferenceClicked = false;
|
||||
|
||||
final PreferenceScreen advancedScreen = (PreferenceScreen) findPreference(
|
||||
getString(R.string.advanced_pref_key));
|
||||
@@ -135,10 +124,6 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
|
||||
startActivity(intent);
|
||||
}
|
||||
if (preference.getKey() == mSmsDisabledPrefKey ||
|
||||
preference.getKey() == mSmsEnabledPrefKey) {
|
||||
mIsSmsPreferenceClicked = true;
|
||||
}
|
||||
return super.onPreferenceTreeClick(preference);
|
||||
}
|
||||
|
||||
@@ -152,7 +137,6 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
|
||||
getPreferenceScreen().removePreference(mSmsEnabledPreference);
|
||||
mSmsDisabledPreference.setSummary(defaultSmsAppLabel);
|
||||
}
|
||||
mIsSmsPreferenceClicked = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -152,7 +152,7 @@ public class ContactPickerFragment extends Fragment implements ContactPickerData
|
||||
mRecipientTextView.setContactChipsListener(this);
|
||||
mRecipientTextView.setDropdownChipLayouter(new ContactDropdownLayouter(inflater,
|
||||
getActivity(), this));
|
||||
mRecipientTextView.setAdapter(new ContactRecipientAdapter(getActivity(), this));
|
||||
mRecipientTextView.setAdapter(new ContactRecipientAdapter(getActivity()));
|
||||
mRecipientTextView.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
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;
|
||||
|
||||
public ContactRecipientAdapter(final Context context,
|
||||
final ContactListItemView.HostInterface clivHost) {
|
||||
this(context, Integer.MAX_VALUE, QUERY_TYPE_PHONE, clivHost);
|
||||
public ContactRecipientAdapter(final Context context) {
|
||||
this(context, Integer.MAX_VALUE, QUERY_TYPE_PHONE);
|
||||
}
|
||||
|
||||
public ContactRecipientAdapter(final Context context, final int preferredMaxResultCount,
|
||||
final int queryMode, final ContactListItemView.HostInterface clivHost) {
|
||||
final int queryMode) {
|
||||
super(context, preferredMaxResultCount, queryMode);
|
||||
setPhotoManager(new ContactRecipientPhotoManager(context, clivHost));
|
||||
setPhotoManager(new ContactRecipientPhotoManager(context));
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,6 @@ public class ContactRecipientAutoCompleteView extends RecipientEditTextView {
|
||||
void onEntryComplete();
|
||||
}
|
||||
|
||||
private final int mTextHeight;
|
||||
private ContactChipsChangeListener mChipsChangeListener;
|
||||
|
||||
/**
|
||||
@@ -110,7 +109,6 @@ public class ContactRecipientAutoCompleteView extends RecipientEditTextView {
|
||||
final Rect textBounds = new Rect(0, 0, 0, 0);
|
||||
final TextPaint paint = getPaint();
|
||||
paint.getTextBounds(TEXT_HEIGHT_SAMPLE, 0, TEXT_HEIGHT_SAMPLE.length(), textBounds);
|
||||
mTextHeight = textBounds.height();
|
||||
|
||||
setTokenizer(new Rfc822Tokenizer());
|
||||
addTextChangedListener(new ContactChipsWatcher());
|
||||
|
||||
@@ -42,15 +42,12 @@ public class ContactRecipientPhotoManager implements PhotoManager {
|
||||
private static final String IMAGE_BYTES_REQUEST_STATIC_BINDING_ID = "imagebytes";
|
||||
private final Context mContext;
|
||||
private final int mIconSize;
|
||||
private final ContactListItemView.HostInterface mClivHostInterface;
|
||||
|
||||
public ContactRecipientPhotoManager(final Context context,
|
||||
final ContactListItemView.HostInterface clivHostInterface) {
|
||||
public ContactRecipientPhotoManager(final Context context) {
|
||||
mContext = context;
|
||||
mIconSize = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.compose_message_chip_height) - context.getResources().getDimensionPixelSize(
|
||||
R.dimen.compose_message_chip_padding) * 2;
|
||||
mClivHostInterface = clivHostInterface;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,7 +90,6 @@ public class ComposeMessageView extends LinearLayout
|
||||
void sendMessage(MessageData message);
|
||||
void onComposeEditTextFocused();
|
||||
void onAttachmentsCleared();
|
||||
void onAttachmentsChanged(final boolean haveAttachments);
|
||||
void displayPhoto(Uri photoUri, Rect imageBounds, boolean isDraft);
|
||||
void promptForSelfPhoneNumber();
|
||||
boolean isReadyForAction();
|
||||
@@ -301,10 +300,8 @@ public class ComposeMessageView extends LinearLayout
|
||||
}
|
||||
final boolean haveAttachments = mBinding.getData().hasAttachments();
|
||||
if (simPickerVisible && haveAttachments) {
|
||||
mHost.onAttachmentsChanged(false);
|
||||
mAttachmentPreview.hideAttachmentPreview();
|
||||
} else {
|
||||
mHost.onAttachmentsChanged(haveAttachments);
|
||||
mAttachmentPreview.onAttachmentsChanged(mBinding.getData());
|
||||
}
|
||||
}
|
||||
@@ -474,8 +471,7 @@ public class ComposeMessageView extends LinearLayout
|
||||
|
||||
if ((changeFlags & DraftMessageData.ATTACHMENTS_CHANGED) ==
|
||||
DraftMessageData.ATTACHMENTS_CHANGED) {
|
||||
final boolean haveAttachments = mAttachmentPreview.onAttachmentsChanged(data);
|
||||
mHost.onAttachmentsChanged(haveAttachments);
|
||||
mAttachmentPreview.onAttachmentsChanged(data);
|
||||
hasAttachmentsChanged = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,13 +165,6 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
|
||||
|
||||
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
|
||||
private ConversationMessageView mSelectedMessage;
|
||||
|
||||
@@ -412,8 +405,6 @@ public class ConversationFragment extends Fragment implements ConversationDataLi
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mFastFlingThreshold = getResources().getDimensionPixelOffset(
|
||||
R.dimen.conversation_fast_fling_threshold);
|
||||
mAdapter = new ConversationMessageAdapter(getActivity(), null, this,
|
||||
null,
|
||||
// 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
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachmentsChanged(final boolean haveAttachments) {
|
||||
// no-op for now
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraftChanged(final DraftMessageData data, final int changeFlags) {
|
||||
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.ImeUtil;
|
||||
import com.android.messaging.util.ImeUtil.ImeStateHost;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -236,26 +235,6 @@ public class ConversationInputManager implements ConversationInput.ConversationI
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -737,7 +737,7 @@ public class ConversationMessageView extends FrameLayout implements View.OnClick
|
||||
R.dimen.message_metadata_top_padding);
|
||||
|
||||
// Update the message text/info views
|
||||
ImageUtils.setBackgroundDrawableOnView(mMessageTextAndInfoView, textBackground);
|
||||
mMessageTextAndInfoView.setBackground(textBackground);
|
||||
mMessageTextAndInfoView.setMinimumHeight(textMinHeight);
|
||||
final LinearLayout.LayoutParams textAndInfoLayoutParams =
|
||||
(LinearLayout.LayoutParams) mMessageTextAndInfoView.getLayoutParams();
|
||||
|
||||
@@ -36,7 +36,6 @@ import android.widget.AbsListView;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.ViewGroupCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
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.ConversationListDataListener;
|
||||
import com.android.messaging.datamodel.data.ConversationListItemData;
|
||||
import com.android.messaging.ui.BugleAnimationTags;
|
||||
import com.android.messaging.ui.ListEmptyView;
|
||||
import com.android.messaging.ui.SnackBarInteraction;
|
||||
import com.android.messaging.ui.UIIntents;
|
||||
@@ -232,7 +230,6 @@ public class ConversationListFragment extends Fragment implements ConversationLi
|
||||
mStartNewConversationButton.setOnClickListener(clickView ->
|
||||
mHost.onCreateConversationClick());
|
||||
}
|
||||
ViewCompat.setTransitionName(mStartNewConversationButton, BugleAnimationTags.TAG_FABICON);
|
||||
|
||||
// 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
|
||||
@@ -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
|
||||
public void startFullScreenPhotoViewer(
|
||||
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 sPlusNString;
|
||||
|
||||
private static final int SWIPE_DIRECTION_RIGHT = 2;
|
||||
|
||||
public interface HostInterface {
|
||||
boolean isConversationSelected(final String conversationId);
|
||||
void onConversationClicked(final ConversationListItemData conversationListItemData,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
@@ -27,47 +28,16 @@ import javax.annotation.concurrent.ThreadSafe;
|
||||
@ThreadSafe
|
||||
public class AudioLevelSource {
|
||||
private volatile int mSpeechLevel;
|
||||
private volatile Listener mListener;
|
||||
|
||||
public static final int LEVEL_UNKNOWN = -1;
|
||||
|
||||
public interface Listener {
|
||||
void onSpeechLevel(int speechLevel);
|
||||
}
|
||||
|
||||
public void setSpeechLevel(int speechLevel) {
|
||||
Preconditions.checkArgument(speechLevel >= 0 && speechLevel <= 100 ||
|
||||
speechLevel == LEVEL_UNKNOWN);
|
||||
mSpeechLevel = speechLevel;
|
||||
maybeNotify();
|
||||
}
|
||||
|
||||
public int getSpeechLevel() {
|
||||
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;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void testSetMediaRecorder(final LevelTrackingMediaRecorder recorder) {
|
||||
mMediaRecorder = recorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
package com.android.messaging.ui.mediapicker.camerafocus;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -47,7 +46,6 @@ public class PieItem {
|
||||
private List<PieItem> mItems;
|
||||
private Path mPath;
|
||||
private OnClickListener mOnClickListener;
|
||||
private float mAlpha;
|
||||
|
||||
// Gray out the view when disabled
|
||||
private static final float ENABLED_ALPHA = 1;
|
||||
@@ -87,12 +85,7 @@ public class PieItem {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
public void setChangeAlphaWhenDisabled (boolean enable) {
|
||||
mChangeAlphaWhenDisabled = enable;
|
||||
}
|
||||
|
||||
public void setAlpha(float alpha) {
|
||||
mAlpha = alpha;
|
||||
mDrawable.setAlpha((int) (255 * alpha));
|
||||
}
|
||||
|
||||
@@ -138,11 +131,6 @@ public class PieItem {
|
||||
outer = outside;
|
||||
}
|
||||
|
||||
public void setFixedSlice(float center, float sweep) {
|
||||
mCenter = center;
|
||||
this.sweep = sweep;
|
||||
}
|
||||
|
||||
public float getCenter() {
|
||||
return mCenter;
|
||||
}
|
||||
@@ -192,12 +180,4 @@ public class PieItem {
|
||||
public void draw(Canvas 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();
|
||||
}
|
||||
|
||||
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(
|
||||
final View view, @Nullable final AccessibilityManager accessibilityManager,
|
||||
final int textResourceId) {
|
||||
|
||||
@@ -18,8 +18,6 @@ package com.android.messaging.util;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class Assert {
|
||||
public @interface RunsOnMainThread {}
|
||||
public @interface DoesNotRunOnMainThread {}
|
||||
@@ -46,17 +44,6 @@ public final class Assert {
|
||||
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.
|
||||
*/
|
||||
@@ -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) {
|
||||
if (expected != actual) {
|
||||
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(
|
||||
final int val, final int rangeMinInclusive, final int rangeMaxInclusive) {
|
||||
if (val < rangeMinInclusive || val > rangeMaxInclusive) {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
package com.android.messaging.util;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.net.Uri.Builder;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -85,11 +84,6 @@ public class AvatarUriUtil {
|
||||
public static final Uri DEFAULT_BACKGROUND_AVATAR = new Uri.Builder().scheme(SCHEME)
|
||||
.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
|
||||
* 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();
|
||||
}
|
||||
|
||||
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
|
||||
* the local resource one could not be loaded.
|
||||
|
||||
@@ -36,8 +36,6 @@ import com.android.messaging.ui.conversationlist.ConversationListActivity;
|
||||
*/
|
||||
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
|
||||
* 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);
|
||||
}
|
||||
|
||||
public int getCurrentServiceState() {
|
||||
return mCurrentServiceState;
|
||||
}
|
||||
|
||||
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onServiceStateChanged(final ServiceState serviceState) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
@@ -23,7 +24,6 @@ import com.android.ex.chips.RecipientEntry;
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.R;
|
||||
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.
|
||||
@@ -105,11 +105,4 @@ public class ContactRecipientEntryUtils {
|
||||
public static boolean isSendToDestinationContact(final RecipientEntry entry) {
|
||||
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_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_AMR = "audio/amr";
|
||||
public static final String AUDIO_IMELODY = "audio/imelody";
|
||||
public static final String AUDIO_MID = "audio/mid";
|
||||
public static final String AUDIO_MIDI = "audio/midi";
|
||||
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_LATM = "audio/mp4-latm";
|
||||
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_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_X_WAV = "audio/x-wav";
|
||||
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_3GP = "video/3gp";
|
||||
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_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.
|
||||
private ContentType() {
|
||||
@@ -136,16 +121,6 @@ public final class ContentType {
|
||||
|| 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.
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.text.TextUtils;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.R;
|
||||
import com.google.common.io.Files;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -71,55 +70,6 @@ public class FileUtil {
|
||||
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.
|
||||
// We're told it's possible to create world readable hardlinks to other apps private data
|
||||
// so we ban all /data file uris.
|
||||
|
||||
@@ -35,7 +35,6 @@ import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
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
|
||||
* 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_SECONDARY_USER_NOTIFICATION_ID = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of SIM slots
|
||||
*
|
||||
* @return the SIM slot count
|
||||
*/
|
||||
public int getSimSlotCount() {
|
||||
return mSubscriptionManager.getActiveSubscriptionInfoCountMax();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SIM's carrier name
|
||||
*
|
||||
@@ -134,15 +125,6 @@ public class PhoneUtils {
|
||||
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
|
||||
*
|
||||
@@ -168,16 +150,6 @@ public class PhoneUtils {
|
||||
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
|
||||
*
|
||||
@@ -581,39 +553,6 @@ public class PhoneUtils {
|
||||
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.
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -20,22 +20,12 @@ package com.android.messaging.util;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
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.TextUtils;
|
||||
import android.text.style.URLSpan;
|
||||
import android.view.Gravity;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.View.OnLayoutChangeListener;
|
||||
import android.view.animation.Animation;
|
||||
@@ -45,16 +35,17 @@ import android.view.animation.ScaleAnimation;
|
||||
import android.widget.RemoteViews;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.messaging.Factory;
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.ui.SnackBar;
|
||||
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.SnackBarManager;
|
||||
import com.android.messaging.ui.UIIntents;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
public class UiUtils {
|
||||
@@ -62,10 +53,6 @@ public class UiUtils {
|
||||
public static final int MEDIAPICKER_TRANSITION_DURATION =
|
||||
getApplicationContext().getResources().getInteger(
|
||||
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 */
|
||||
public static final int COMPOSE_TRANSITION_DURATION =
|
||||
getApplicationContext().getResources().getInteger(
|
||||
@@ -272,34 +259,6 @@ public class UiUtils {
|
||||
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() {
|
||||
return Factory.get().getApplicationContext().getResources()
|
||||
.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
|
||||
@@ -350,67 +309,6 @@ public class UiUtils {
|
||||
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
|
||||
* is sufficient, but sometimes the context is a context wrapper, in which case we need to case
|
||||
|
||||
Reference in New Issue
Block a user