package org.thoughtcrime.securesms.jobs.requirements; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; import org.thoughtcrime.securesms.attachments.Attachment; import org.thoughtcrime.securesms.attachments.AttachmentId; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ServiceUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.jobqueue.dependencies.ContextDependent; import org.whispersystems.jobqueue.requirements.Requirement; import java.util.Collections; import java.util.Set; public class MediaNetworkRequirement implements Requirement, ContextDependent { private static final long serialVersionUID = 0L; private static final String TAG = MediaNetworkRequirement.class.getSimpleName(); private transient Context context; private final long messageId; private final long partRowId; private final long partUniqueId; public MediaNetworkRequirement(Context context, long messageId, AttachmentId attachmentId) { this.context = context; this.messageId = messageId; this.partRowId = attachmentId.getRowId(); this.partUniqueId = attachmentId.getUniqueId(); } @Override public void setContext(Context context) { this.context = context; } private NetworkInfo getNetworkInfo() { return ServiceUtil.getConnectivityManager(context).getActiveNetworkInfo(); } public boolean isConnectedWifi() { final NetworkInfo info = getNetworkInfo(); return info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI; } public boolean isConnectedMobile() { final NetworkInfo info = getNetworkInfo(); return info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_MOBILE; } public boolean isConnectedRoaming() { final NetworkInfo info = getNetworkInfo(); return info != null && info.isConnected() && info.isRoaming() && info.getType() == ConnectivityManager.TYPE_MOBILE; } private @NonNull Set<String> getAllowedAutoDownloadTypes() { if (isConnectedWifi()) { return TextSecurePreferences.getWifiMediaDownloadAllowed(context); } else if (isConnectedRoaming()) { return TextSecurePreferences.getRoamingMediaDownloadAllowed(context); } else if (isConnectedMobile()) { return TextSecurePreferences.getMobileMediaDownloadAllowed(context); } else { return Collections.emptySet(); } } @Override public boolean isPresent() { final AttachmentId attachmentId = new AttachmentId(partRowId, partUniqueId); final AttachmentDatabase db = DatabaseFactory.getAttachmentDatabase(context); final Attachment attachment = db.getAttachment(null, attachmentId); if (attachment == null) { Log.w(TAG, "attachment was null, returning vacuous true"); return true; } Log.w(TAG, "part transfer progress is " + attachment.getTransferState()); switch (attachment.getTransferState()) { case AttachmentDatabase.TRANSFER_PROGRESS_STARTED: return true; case AttachmentDatabase.TRANSFER_PROGRESS_AUTO_PENDING: final Set<String> allowedTypes = getAllowedAutoDownloadTypes(); final String contentType = attachment.getContentType(); boolean isAllowed; if (attachment.isVoiceNote() || (MediaUtil.isAudio(attachment) && TextUtils.isEmpty(attachment.getFileName()))) { isAllowed = isConnectedWifi() || isConnectedMobile(); } else if (isNonDocumentType(contentType)) { isAllowed = allowedTypes.contains(MediaUtil.getDiscreteMimeType(contentType)); } else { isAllowed = allowedTypes.contains("documents"); } /// XXX WTF -- This is *hella* gross. A requirement shouldn't have the side effect of // *modifying the database* just by calling isPresent(). if (isAllowed) db.setTransferState(messageId, attachmentId, AttachmentDatabase.TRANSFER_PROGRESS_STARTED); return isAllowed; default: return false; } } private boolean isNonDocumentType(String contentType) { return MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType) || MediaUtil.isAudioType(contentType); } }