remove android 4 compatibility code
|
@ -28,11 +28,6 @@ public class FcmReceiveService extends FirebaseMessagingService {
|
||||||
private static volatile String prefixedToken;
|
private static volatile String prefixedToken;
|
||||||
|
|
||||||
public static void register(Context context) {
|
public static void register(Context context) {
|
||||||
if (Build.VERSION.SDK_INT < 19) {
|
|
||||||
Log.w(TAG, "FCM not available on SDK < 19");
|
|
||||||
triedRegistering = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FcmReceiveService.prefixedToken != null) {
|
if (FcmReceiveService.prefixedToken != null) {
|
||||||
Log.i(TAG, "FCM already registered");
|
Log.i(TAG, "FCM already registered");
|
||||||
|
|
|
@ -36,7 +36,6 @@ import org.thoughtcrime.securesms.connect.KeepAliveService;
|
||||||
import org.thoughtcrime.securesms.connect.NetworkStateReceiver;
|
import org.thoughtcrime.securesms.connect.NetworkStateReceiver;
|
||||||
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
import org.thoughtcrime.securesms.crypto.DatabaseSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
|
import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider;
|
||||||
import org.thoughtcrime.securesms.crypto.PRNGFixes;
|
|
||||||
import org.thoughtcrime.securesms.geolocation.DcLocationManager;
|
import org.thoughtcrime.securesms.geolocation.DcLocationManager;
|
||||||
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
import org.thoughtcrime.securesms.jobmanager.JobManager;
|
||||||
import org.thoughtcrime.securesms.notifications.FcmReceiveService;
|
import org.thoughtcrime.securesms.notifications.FcmReceiveService;
|
||||||
|
@ -178,7 +177,6 @@ public class ApplicationContext extends MultiDexApplication {
|
||||||
|
|
||||||
KeepAliveService.maybeStartSelf(this);
|
KeepAliveService.maybeStartSelf(this);
|
||||||
|
|
||||||
initializeRandomNumberFix();
|
|
||||||
initializeLogging();
|
initializeLogging();
|
||||||
initializeJobManager();
|
initializeJobManager();
|
||||||
InChatSounds.getInstance(this);
|
InChatSounds.getInstance(this);
|
||||||
|
@ -226,10 +224,6 @@ public class ApplicationContext extends MultiDexApplication {
|
||||||
return jobManager;
|
return jobManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeRandomNumberFix() {
|
|
||||||
PRNGFixes.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeLogging() {
|
private void initializeLogging() {
|
||||||
SignalProtocolLoggerProvider.setProvider(new AndroidSignalProtocolLogger());
|
SignalProtocolLoggerProvider.setProvider(new AndroidSignalProtocolLogger());
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,10 +152,6 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActionBarA
|
||||||
this.findPreference(PREFERENCE_CATEGORY_HELP)
|
this.findPreference(PREFERENCE_CATEGORY_HELP)
|
||||||
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
|
.setOnPreferenceClickListener(new CategoryClickListener(PREFERENCE_CATEGORY_HELP));
|
||||||
|
|
||||||
if (VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
tintIcons(getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
DcHelper.getEventCenter(getActivity()).addObserver(DcContext.DC_EVENT_CONNECTIVITY_CHANGED, this);
|
DcHelper.getEventCenter(getActivity()).addObserver(DcContext.DC_EVENT_CONNECTIVITY_CHANGED, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,9 +33,6 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
onPreCreate();
|
onPreCreate();
|
||||||
if (BaseActivity.isMenuWorkaroundRequired()) {
|
|
||||||
forceOverflowMenu();
|
|
||||||
}
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,20 +43,6 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
|
||||||
dynamicTheme.onResume(this);
|
dynamicTheme.onResume(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
||||||
return (keyCode == KeyEvent.KEYCODE_MENU && BaseActivity.isMenuWorkaroundRequired()) || super.onKeyDown(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_MENU && BaseActivity.isMenuWorkaroundRequired()) {
|
|
||||||
openOptionsMenu();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onKeyUp(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeScreenshotSecurity() {
|
private void initializeScreenshotSecurity() {
|
||||||
if (Prefs.isScreenSecurityEnabled(this)) {
|
if (Prefs.isScreenSecurityEnabled(this)) {
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package org.thoughtcrime.securesms;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import android.os.Build.VERSION_CODES;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
|
|
||||||
public abstract class BaseActivity extends FragmentActivity {
|
|
||||||
@Override
|
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
||||||
return (keyCode == KeyEvent.KEYCODE_MENU && isMenuWorkaroundRequired()) || super.onKeyDown(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_MENU && isMenuWorkaroundRequired()) {
|
|
||||||
openOptionsMenu();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.onKeyUp(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isMenuWorkaroundRequired() {
|
|
||||||
return VERSION.SDK_INT < VERSION_CODES.KITKAT && ("LGE".equalsIgnoreCase(Build.MANUFACTURER) || "E6710".equalsIgnoreCase(Build.DEVICE));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -364,9 +364,7 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||||
|
|
||||||
mode.setTitle("1");
|
mode.setTitle("1");
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
requireActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||||
requireActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -395,11 +393,9 @@ public abstract class BaseConversationListFragment extends Fragment implements A
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
getListAdapter().initializeBatchMode(false);
|
getListAdapter().initializeBatchMode(false);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
TypedArray color = requireActivity().getTheme().obtainStyledAttributes(new int[]{android.R.attr.statusBarColor});
|
||||||
TypedArray color = requireActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor});
|
requireActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||||
requireActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
color.recycle();
|
||||||
color.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
|
|
|
@ -136,9 +136,7 @@ public class ContactSelectionListFragment extends Fragment
|
||||||
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
|
||||||
MenuInflater inflater = getActivity().getMenuInflater();
|
MenuInflater inflater = getActivity().getMenuInflater();
|
||||||
inflater.inflate(R.menu.contact_list, menu);
|
inflater.inflate(R.menu.contact_list, menu);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||||
getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
|
||||||
}
|
|
||||||
setCorrectMenuVisibility(menu);
|
setCorrectMenuVisibility(menu);
|
||||||
actionMode.setTitle("1");
|
actionMode.setTitle("1");
|
||||||
return true;
|
return true;
|
||||||
|
@ -170,11 +168,9 @@ public class ContactSelectionListFragment extends Fragment
|
||||||
ContactSelectionListFragment.this.actionMode = null;
|
ContactSelectionListFragment.this.actionMode = null;
|
||||||
getContactSelectionListAdapter().resetActionModeSelection();
|
getContactSelectionListAdapter().resetActionModeSelection();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[]{android.R.attr.statusBarColor});
|
||||||
TypedArray color = getActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.statusBarColor});
|
getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
||||||
getActivity().getWindow().setStatusBarColor(color.getColor(0, Color.BLACK));
|
color.recycle();
|
||||||
color.recycle();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -940,12 +940,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
||||||
case AttachmentTypeSelector.TAKE_PHOTO:
|
case AttachmentTypeSelector.TAKE_PHOTO:
|
||||||
attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
|
attachmentManager.capturePhoto(this, TAKE_PHOTO); break;
|
||||||
case AttachmentTypeSelector.RECORD_VIDEO:
|
case AttachmentTypeSelector.RECORD_VIDEO:
|
||||||
if(VideoRecoder.canRecode()) {
|
attachmentManager.captureVideo(this, RECORD_VIDEO);
|
||||||
attachmentManager.captureVideo(this, RECORD_VIDEO);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Toast.makeText(this, "This device does not support video-compression (requires Android 4.4 KitKat)", Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -893,11 +893,9 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||||
|
|
||||||
mode.setTitle("1");
|
mode.setTitle("1");
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
Window window = getActivity().getWindow();
|
||||||
Window window = getActivity().getWindow();
|
statusBarColor = window.getStatusBarColor();
|
||||||
statusBarColor = window.getStatusBarColor();
|
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
|
||||||
}
|
|
||||||
|
|
||||||
setCorrectMenuVisibility(menu);
|
setCorrectMenuVisibility(menu);
|
||||||
ConversationAdaptiveActionsToolbar.adjustMenuActions(menu, 10, requireActivity().getWindow().getDecorView().getMeasuredWidth());
|
ConversationAdaptiveActionsToolbar.adjustMenuActions(menu, 10, requireActivity().getWindow().getDecorView().getMeasuredWidth());
|
||||||
|
@ -914,9 +912,7 @@ public class ConversationFragment extends MessageSelectorFragment
|
||||||
((ConversationAdapter)list.getAdapter()).clearSelection();
|
((ConversationAdapter)list.getAdapter()).clearSelection();
|
||||||
list.getAdapter().notifyDataSetChanged();
|
list.getAdapter().notifyDataSetChanged();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
getActivity().getWindow().setStatusBarColor(statusBarColor);
|
||||||
getActivity().getWindow().setStatusBarColor(statusBarColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
hideAddReactionView();
|
hideAddReactionView();
|
||||||
|
|
|
@ -495,9 +495,7 @@ public class ConversationItem extends BaseConversationItem
|
||||||
audioViewStub.get().setAudio(new AudioSlide(context, messageRecord), duration);
|
audioViewStub.get().setAudio(new AudioSlide(context, messageRecord), duration);
|
||||||
audioViewStub.get().setOnClickListener(passthroughClickListener);
|
audioViewStub.get().setOnClickListener(passthroughClickListener);
|
||||||
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
|
audioViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
audioViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||||
audioViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
@ -515,9 +513,7 @@ public class ConversationItem extends BaseConversationItem
|
||||||
documentViewStub.get().setDocument(new DocumentSlide(context, messageRecord));
|
documentViewStub.get().setDocument(new DocumentSlide(context, messageRecord));
|
||||||
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
|
documentViewStub.get().setDocumentClickListener(new ThumbnailClickListener());
|
||||||
documentViewStub.get().setOnLongClickListener(passthroughClickListener);
|
documentViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
documentViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||||
documentViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
@ -534,9 +530,7 @@ public class ConversationItem extends BaseConversationItem
|
||||||
webxdcViewStub.get().setWebxdc(messageRecord, context.getString(R.string.webxdc_app));
|
webxdcViewStub.get().setWebxdc(messageRecord, context.getString(R.string.webxdc_app));
|
||||||
webxdcViewStub.get().setWebxdcClickListener(new ThumbnailClickListener());
|
webxdcViewStub.get().setWebxdcClickListener(new ThumbnailClickListener());
|
||||||
webxdcViewStub.get().setOnLongClickListener(passthroughClickListener);
|
webxdcViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
webxdcViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||||
webxdcViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
@ -554,9 +548,7 @@ public class ConversationItem extends BaseConversationItem
|
||||||
vcardViewStub.get().setVcardClickListener(new ThumbnailClickListener());
|
vcardViewStub.get().setVcardClickListener(new ThumbnailClickListener());
|
||||||
vcardViewStub.get().setOnLongClickListener(passthroughClickListener);
|
vcardViewStub.get().setOnLongClickListener(passthroughClickListener);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
vcardViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||||
vcardViewStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
@ -592,9 +584,7 @@ public class ConversationItem extends BaseConversationItem
|
||||||
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
|
mediaThumbnailStub.get().setOnLongClickListener(passthroughClickListener);
|
||||||
mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
|
mediaThumbnailStub.get().setOnClickListener(passthroughClickListener);
|
||||||
mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getText()));
|
mediaThumbnailStub.get().showShade(TextUtils.isEmpty(messageRecord.getText()));
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
mediaThumbnailStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||||
mediaThumbnailStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
setThumbnailOutlineCorners(messageRecord, showSender);
|
setThumbnailOutlineCorners(messageRecord, showSender);
|
||||||
|
|
||||||
|
@ -617,9 +607,7 @@ public class ConversationItem extends BaseConversationItem
|
||||||
stickerStub.get().setThumbnailClickListener(new StickerClickListener());
|
stickerStub.get().setThumbnailClickListener(new StickerClickListener());
|
||||||
stickerStub.get().setOnLongClickListener(passthroughClickListener);
|
stickerStub.get().setOnLongClickListener(passthroughClickListener);
|
||||||
stickerStub.get().setOnClickListener(passthroughClickListener);
|
stickerStub.get().setOnClickListener(passthroughClickListener);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
stickerStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||||
stickerStub.get().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(bodyText, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
ViewUtil.updateLayoutParams(groupSenderHolder, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
|
|
@ -174,9 +174,7 @@ class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
||||||
vibrate(viewHolder.itemView.getContext());
|
vibrate(viewHolder.itemView.getContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
recyclerView.cancelPendingInputEvents();
|
||||||
recyclerView.cancelPendingInputEvents();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void resetProgress(RecyclerView.ViewHolder viewHolder) {
|
private static void resetProgress(RecyclerView.ViewHolder viewHolder) {
|
||||||
|
@ -199,11 +197,7 @@ class ConversationItemSwipeCallback extends ItemTouchHelper.SimpleCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float getSignFromDirection(@NonNull View view) {
|
private static float getSignFromDirection(@NonNull View view) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -1f : 1f;
|
||||||
return view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? -1f : 1f;
|
|
||||||
} else {
|
|
||||||
return 1f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean sameSign(float dX, float sign) {
|
private static boolean sameSign(float dX, float sign) {
|
||||||
|
|
|
@ -29,7 +29,6 @@ public class ConversationListItemInboxZero extends LinearLayout implements Binda
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public ConversationListItemInboxZero(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public ConversationListItemInboxZero(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,12 +197,11 @@ public class LogViewFragment extends Fragment {
|
||||||
asMegs(info.maxMemory()));
|
asMegs(info.maxMemory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.KITKAT)
|
|
||||||
public static String getMemoryClass(Context context) {
|
public static String getMemoryClass(Context context) {
|
||||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
String lowMem = "";
|
String lowMem = "";
|
||||||
|
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) {
|
if (activityManager.isLowRamDevice()) {
|
||||||
lowMem = ", low-mem device";
|
lowMem = ", low-mem device";
|
||||||
}
|
}
|
||||||
return activityManager.getMemoryClass() + lowMem;
|
return activityManager.getMemoryClass() + lowMem;
|
||||||
|
@ -252,10 +251,8 @@ public class LogViewFragment extends Fragment {
|
||||||
|
|
||||||
Locale locale = Util.getLocale();
|
Locale locale = Util.getLocale();
|
||||||
builder.append("lang=").append(locale.toString()).append("\n");
|
builder.append("lang=").append(locale.toString()).append("\n");
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
boolean isRtl = Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL;
|
||||||
boolean isRtl = Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL;
|
builder.append("rtl=").append(isRtl).append("\n");
|
||||||
builder.append("rtl=").append(isRtl).append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
boolean notifPermGranted = PermissionChecker.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PermissionChecker.PERMISSION_GRANTED;
|
boolean notifPermGranted = PermissionChecker.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) == PermissionChecker.PERMISSION_GRANTED;
|
||||||
|
|
|
@ -228,11 +228,9 @@ public class ProfileDocumentsFragment
|
||||||
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
||||||
mode.setTitle("1");
|
mode.setTitle("1");
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
Window window = getActivity().getWindow();
|
||||||
Window window = getActivity().getWindow();
|
originalStatusBarColor = window.getStatusBarColor();
|
||||||
originalStatusBarColor = window.getStatusBarColor();
|
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
|
||||||
}
|
|
||||||
setCorrectMenuVisibility(menu);
|
setCorrectMenuVisibility(menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -282,9 +280,7 @@ public class ProfileDocumentsFragment
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
getListAdapter().clearSelection();
|
getListAdapter().clearSelection();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,11 +219,9 @@ public class ProfileGalleryFragment
|
||||||
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
mode.getMenuInflater().inflate(R.menu.profile_context, menu);
|
||||||
mode.setTitle("1");
|
mode.setTitle("1");
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
Window window = getActivity().getWindow();
|
||||||
Window window = getActivity().getWindow();
|
originalStatusBarColor = window.getStatusBarColor();
|
||||||
originalStatusBarColor = window.getStatusBarColor();
|
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
|
||||||
}
|
|
||||||
setCorrectMenuVisibility(menu);
|
setCorrectMenuVisibility(menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -269,9 +267,7 @@ public class ProfileGalleryFragment
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
getListAdapter().clearSelection();
|
getListAdapter().clearSelection();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,11 +268,9 @@ public class ProfileSettingsFragment extends Fragment
|
||||||
menu.findItem(R.id.menu_select_all).setVisible(false);
|
menu.findItem(R.id.menu_select_all).setVisible(false);
|
||||||
mode.setTitle("1");
|
mode.setTitle("1");
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
Window window = getActivity().getWindow();
|
||||||
Window window = getActivity().getWindow();
|
originalStatusBarColor = window.getStatusBarColor();
|
||||||
originalStatusBarColor = window.getStatusBarColor();
|
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
||||||
window.setStatusBarColor(getResources().getColor(R.color.action_mode_status_bar));
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,9 +312,7 @@ public class ProfileSettingsFragment extends Fragment
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
actionMode = null;
|
actionMode = null;
|
||||||
adapter.clearSelection();
|
adapter.clearSelection();
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
||||||
getActivity().getWindow().setStatusBarColor(originalStatusBarColor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,14 +100,10 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||||
|
|
||||||
public static void openWebxdcActivity(Context context, int msgId, boolean hideActionBar) {
|
public static void openWebxdcActivity(Context context, int msgId, boolean hideActionBar) {
|
||||||
if (!Util.isClickedRecently()) {
|
if (!Util.isClickedRecently()) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Prefs.isDeveloperModeEnabled(context)) {
|
||||||
if (Prefs.isDeveloperModeEnabled(context)) {
|
WebView.setWebContentsDebuggingEnabled(true);
|
||||||
WebView.setWebContentsDebuggingEnabled(true);
|
|
||||||
}
|
|
||||||
context.startActivity(getWebxdcIntent(context, msgId, hideActionBar));
|
|
||||||
} else {
|
|
||||||
Toast.makeText(context, "At least Android 5.0 (Lollipop) required for Webxdc.", Toast.LENGTH_LONG).show();
|
|
||||||
}
|
}
|
||||||
|
context.startActivity(getWebxdcIntent(context, msgId, hideActionBar));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,7 +325,7 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||||
res = new WebResourceResponse("text/plain", "UTF-8", targetStream);
|
res = new WebResourceResponse("text/plain", "UTF-8", targetStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !internetAccess) {
|
if (!internetAccess) {
|
||||||
Map<String, String> headers = new HashMap<>();
|
Map<String, String> headers = new HashMap<>();
|
||||||
headers.put("Content-Security-Policy",
|
headers.put("Content-Security-Policy",
|
||||||
"default-src 'self'; "
|
"default-src 'self'; "
|
||||||
|
@ -348,12 +344,10 @@ public class WebxdcActivity extends WebViewActivity implements DcEventCenter.DcE
|
||||||
}
|
}
|
||||||
|
|
||||||
private void callJavaScriptFunction(String func) {
|
private void callJavaScriptFunction(String func) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (internetAccess) {
|
||||||
if (internetAccess) {
|
webView.evaluateJavascript("window." + func + ";", null);
|
||||||
webView.evaluateJavascript("window." + func + ";", null);
|
} else {
|
||||||
} else {
|
webView.evaluateJavascript("document.getElementById('frame').contentWindow." + func + ";", null);
|
||||||
webView.evaluateJavascript("document.getElementById('frame').contentWindow." + func + ";", null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,7 @@ public class WelcomeActivity extends BaseActionBarActivity implements DcEventCen
|
||||||
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
.withPermanentDenialDialog(getString(R.string.perm_explain_access_to_storage_denied))
|
||||||
.onAllGranted(() -> {
|
.onAllGranted(() -> {
|
||||||
File imexDir = DcHelper.getImexDir();
|
File imexDir = DcHelper.getImexDir();
|
||||||
if (Build.VERSION.SDK_INT >= 30) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
AttachmentManager.selectMediaType(this, "application/x-tar", null, PICK_BACKUP, StorageUtil.getDownloadUri());
|
AttachmentManager.selectMediaType(this, "application/x-tar", null, PICK_BACKUP, StorageUtil.getDownloadUri());
|
||||||
} else {
|
} else {
|
||||||
final String backupFile = dcContext.imexHasBackup(imexDir.getAbsolutePath());
|
final String backupFile = dcContext.imexHasBackup(imexDir.getAbsolutePath());
|
||||||
|
|
|
@ -130,33 +130,23 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||||
public void onGlobalLayout() {
|
public void onGlobalLayout() {
|
||||||
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
animateWindowInCircular(anchor, getContentView());
|
||||||
animateWindowInCircular(anchor, getContentView());
|
|
||||||
} else {
|
|
||||||
animateWindowInTranslate(getContentView());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
animateButtonIn(cameraButton, ANIMATION_DURATION / 2);
|
||||||
animateButtonIn(cameraButton, ANIMATION_DURATION / 2);
|
animateButtonIn(videoButton, ANIMATION_DURATION / 2);
|
||||||
animateButtonIn(videoButton, ANIMATION_DURATION / 2);
|
animateButtonIn(imageButton, ANIMATION_DURATION / 3);
|
||||||
animateButtonIn(imageButton, ANIMATION_DURATION / 3);
|
animateButtonIn(contactButton, ANIMATION_DURATION / 3);
|
||||||
animateButtonIn(contactButton, ANIMATION_DURATION / 3);
|
animateButtonIn(locationButton, ANIMATION_DURATION / 4);
|
||||||
animateButtonIn(locationButton, ANIMATION_DURATION / 4);
|
animateButtonIn(documentButton, ANIMATION_DURATION / 4);
|
||||||
animateButtonIn(documentButton, ANIMATION_DURATION / 4);
|
animateButtonIn(videoChatButton, 0);
|
||||||
animateButtonIn(videoChatButton, 0);
|
animateButtonIn(closeButton, 0);
|
||||||
animateButtonIn(closeButton, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dismiss() {
|
public void dismiss() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
animateWindowOutCircular(currentAnchor, getContentView());
|
||||||
animateWindowOutCircular(currentAnchor, getContentView());
|
|
||||||
} else {
|
|
||||||
animateWindowOutTranslate(getContentView());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(@Nullable AttachmentClickedListener listener) {
|
public void setListener(@Nullable AttachmentClickedListener listener) {
|
||||||
|
@ -186,7 +176,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||||
button.startAnimation(animation);
|
button.startAnimation(animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) {
|
private void animateWindowInCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(contentView,
|
Animator animator = ViewAnimationUtils.createCircularReveal(contentView,
|
||||||
|
@ -205,7 +194,6 @@ public class AttachmentTypeSelector extends PopupWindow {
|
||||||
getContentView().startAnimation(animation);
|
getContentView().startAnimation(animation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
|
private void animateWindowOutCircular(@Nullable View anchor, @NonNull View contentView) {
|
||||||
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
Pair<Integer, Integer> coordinates = getClickOrigin(anchor, contentView);
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
|
Animator animator = ViewAnimationUtils.createCircularReveal(getContentView(),
|
||||||
|
|
|
@ -71,12 +71,10 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||||
this.pauseButton.setOnClickListener(new PauseClickedListener());
|
this.pauseButton.setOnClickListener(new PauseClickedListener());
|
||||||
this.seekBar.setOnSeekBarChangeListener(new SeekBarModifiedListener());
|
this.seekBar.setOnSeekBarChangeListener(new SeekBarModifiedListener());
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
this.playButton.setImageDrawable(context.getDrawable(R.drawable.play_icon));
|
||||||
this.playButton.setImageDrawable(context.getDrawable(R.drawable.play_icon));
|
this.pauseButton.setImageDrawable(context.getDrawable(R.drawable.pause_icon));
|
||||||
this.pauseButton.setImageDrawable(context.getDrawable(R.drawable.pause_icon));
|
this.playButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
||||||
this.playButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
this.pauseButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
||||||
this.pauseButton.setBackground(context.getDrawable(R.drawable.ic_circle_fill_white_48dp));
|
|
||||||
}
|
|
||||||
|
|
||||||
setTint(getContext().getResources().getColor(R.color.audio_icon));
|
setTint(getContext().getResources().getColor(R.color.audio_icon));
|
||||||
}
|
}
|
||||||
|
@ -192,13 +190,8 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTint(int foregroundTint) {
|
public void setTint(int foregroundTint) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
this.playButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
||||||
this.playButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
this.pauseButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
||||||
this.pauseButton.setBackgroundTintList(ColorStateList.valueOf(foregroundTint));
|
|
||||||
} else {
|
|
||||||
this.playButton.setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
|
|
||||||
this.pauseButton.setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.seekBar.getProgressDrawable().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
|
this.seekBar.getProgressDrawable().setColorFilter(foregroundTint, PorterDuff.Mode.SRC_IN);
|
||||||
|
|
||||||
|
@ -220,25 +213,20 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||||
private void togglePlayToPause() {
|
private void togglePlayToPause() {
|
||||||
controlToggle.displayQuick(pauseButton);
|
controlToggle.displayQuick(pauseButton);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
AnimatedVectorDrawable playToPauseDrawable = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.play_to_pause_animation);
|
||||||
AnimatedVectorDrawable playToPauseDrawable = (AnimatedVectorDrawable)getContext().getDrawable(R.drawable.play_to_pause_animation);
|
pauseButton.setImageDrawable(playToPauseDrawable);
|
||||||
pauseButton.setImageDrawable(playToPauseDrawable);
|
playToPauseDrawable.start();
|
||||||
playToPauseDrawable.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void togglePauseToPlay() {
|
private void togglePauseToPlay() {
|
||||||
controlToggle.displayQuick(playButton);
|
controlToggle.displayQuick(playButton);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
AnimatedVectorDrawable pauseToPlayDrawable = (AnimatedVectorDrawable) getContext().getDrawable(R.drawable.pause_to_play_animation);
|
||||||
AnimatedVectorDrawable pauseToPlayDrawable = (AnimatedVectorDrawable)getContext().getDrawable(R.drawable.pause_to_play_animation);
|
playButton.setImageDrawable(pauseToPlayDrawable);
|
||||||
playButton.setImageDrawable(pauseToPlayDrawable);
|
pauseToPlayDrawable.start();
|
||||||
pauseToPlayDrawable.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PlayClickedListener implements View.OnClickListener {
|
private class PlayClickedListener implements View.OnClickListener {
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
try {
|
try {
|
||||||
|
@ -279,7 +267,6 @@ public class AudioView extends FrameLayout implements AudioSlidePlayer.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PauseClickedListener implements View.OnClickListener {
|
private class PauseClickedListener implements View.OnClickListener {
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Log.w(TAG, "pausebutton onClick");
|
Log.w(TAG, "pausebutton onClick");
|
||||||
|
|
|
@ -122,7 +122,6 @@ public class ComposeText extends AppCompatEditText {
|
||||||
editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
|
editorInfo.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 21) return inputConnection;
|
|
||||||
if (mediaListener == null) return inputConnection;
|
if (mediaListener == null) return inputConnection;
|
||||||
if (inputConnection == null) return null;
|
if (inputConnection == null) return null;
|
||||||
|
|
||||||
|
|
|
@ -120,15 +120,10 @@ public class ConversationItemThumbnail extends FrameLayout {
|
||||||
@SuppressWarnings("SuspiciousNameCombination")
|
@SuppressWarnings("SuspiciousNameCombination")
|
||||||
@Override
|
@Override
|
||||||
protected void dispatchDraw(Canvas canvas) {
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
if (cornerMask.isLegacy()) {
|
|
||||||
cornerMask.mask(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
super.dispatchDraw(canvas);
|
super.dispatchDraw(canvas);
|
||||||
|
|
||||||
if (!cornerMask.isLegacy()) {
|
cornerMask.mask(canvas);
|
||||||
cornerMask.mask(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
final float halfStrokeWidth = outlinePaint.getStrokeWidth() / 2;
|
final float halfStrokeWidth = outlinePaint.getStrokeWidth() / 2;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import android.graphics.Path;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffXfermode;
|
import android.graphics.PorterDuffXfermode;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.os.Build;
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
@ -20,11 +20,7 @@ public class CornerMask {
|
||||||
private final RectF bounds = new RectF();
|
private final RectF bounds = new RectF();
|
||||||
|
|
||||||
public CornerMask(@NonNull View view) {
|
public CornerMask(@NonNull View view) {
|
||||||
if (isLegacy()) {
|
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||||
view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
|
||||||
} else {
|
|
||||||
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearPaint.setColor(Color.BLACK);
|
clearPaint.setColor(Color.BLACK);
|
||||||
clearPaint.setStyle(Paint.Style.FILL);
|
clearPaint.setStyle(Paint.Style.FILL);
|
||||||
|
@ -44,19 +40,10 @@ public class CornerMask {
|
||||||
// Note: There's a bug in the P beta where most PorterDuff modes aren't working. But CLEAR does.
|
// Note: There's a bug in the P beta where most PorterDuff modes aren't working. But CLEAR does.
|
||||||
// So we find and inverse path and use Mode.CLEAR for versions that support Path.op().
|
// So we find and inverse path and use Mode.CLEAR for versions that support Path.op().
|
||||||
// See issue https://issuetracker.google.com/issues/111394085.
|
// See issue https://issuetracker.google.com/issues/111394085.
|
||||||
if (!isLegacy()) {
|
outline.reset();
|
||||||
outline.reset();
|
outline.addRect(bounds, Path.Direction.CW);
|
||||||
outline.addRect(bounds, Path.Direction.CW);
|
outline.op(corners, Path.Op.DIFFERENCE);
|
||||||
outline.op(corners, Path.Op.DIFFERENCE);
|
canvas.drawPath(outline, clearPaint);
|
||||||
canvas.drawPath(outline, clearPaint);
|
|
||||||
} else {
|
|
||||||
corners.addRoundRect(bounds, radii, Path.Direction.CW);
|
|
||||||
canvas.clipPath(corners);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLegacy() {
|
|
||||||
return Build.VERSION.SDK_INT < 19;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRadius(int radius) {
|
public void setRadius(int radius) {
|
||||||
|
|
|
@ -104,7 +104,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateKeyboardState() {
|
private void updateKeyboardState() {
|
||||||
if (viewInset == 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) viewInset = getViewInset();
|
if (viewInset == 0) viewInset = getViewInset();
|
||||||
|
|
||||||
getWindowVisibleDisplayFrame(rect);
|
getWindowVisibleDisplayFrame(rect);
|
||||||
|
|
||||||
|
@ -148,7 +148,6 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
|
||||||
private int getViewInset() {
|
private int getViewInset() {
|
||||||
try {
|
try {
|
||||||
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
Field attachInfoField = View.class.getDeclaredField("mAttachInfo");
|
||||||
|
@ -219,11 +218,7 @@ public class KeyboardAwareLinearLayout extends LinearLayoutCompat {
|
||||||
return Surface.ROTATION_0;
|
return Surface.ROTATION_0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
return ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRotation();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 30) {
|
|
||||||
getContext().getDisplay().getRealMetrics(displayMetrics);
|
getContext().getDisplay().getRealMetrics(displayMetrics);
|
||||||
} else {
|
} else {
|
||||||
ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRealMetrics(displayMetrics);
|
ServiceUtil.getWindowManager(getContext()).getDefaultDisplay().getRealMetrics(displayMetrics);
|
||||||
|
|
|
@ -40,7 +40,6 @@ public class MediaView extends FrameLayout {
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public MediaView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public MediaView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
initialize();
|
initialize();
|
||||||
|
|
|
@ -78,7 +78,6 @@ public class QuoteView extends FrameLayout implements RecipientForeverObserver {
|
||||||
initialize(attrs);
|
initialize(attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public QuoteView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public QuoteView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
initialize(attrs);
|
initialize(attrs);
|
||||||
|
|
|
@ -29,7 +29,6 @@ public class RepeatableImageKey extends ImageButton {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.LOLLIPOP)
|
|
||||||
public RepeatableImageKey(Context context, AttributeSet attrs, int defStyleAttr,
|
public RepeatableImageKey(Context context, AttributeSet attrs, int defStyleAttr,
|
||||||
int defStyleRes)
|
int defStyleRes)
|
||||||
{
|
{
|
||||||
|
|
|
@ -130,15 +130,11 @@ public class SearchToolbar extends LinearLayout {
|
||||||
|
|
||||||
searchItem.expandActionView();
|
searchItem.expandActionView();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int) x, (int) y, 0, getWidth());
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, 0, getWidth());
|
animator.setDuration(400);
|
||||||
animator.setDuration(400);
|
|
||||||
|
|
||||||
setVisibility(View.VISIBLE);
|
setVisibility(View.VISIBLE);
|
||||||
animator.start();
|
animator.start();
|
||||||
} else {
|
|
||||||
setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,19 +149,15 @@ public class SearchToolbar extends LinearLayout {
|
||||||
|
|
||||||
if (listener != null) listener.onSearchClosed();
|
if (listener != null) listener.onSearchClosed();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int) x, (int) y, getWidth(), 0);
|
||||||
Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, getWidth(), 0);
|
animator.setDuration(400);
|
||||||
animator.setDuration(400);
|
animator.addListener(new AnimationCompleteListener() {
|
||||||
animator.addListener(new AnimationCompleteListener() {
|
@Override
|
||||||
@Override
|
public void onAnimationEnd(Animator animation) {
|
||||||
public void onAnimationEnd(Animator animation) {
|
setVisibility(View.INVISIBLE);
|
||||||
setVisibility(View.INVISIBLE);
|
}
|
||||||
}
|
});
|
||||||
});
|
animator.start();
|
||||||
animator.start();
|
|
||||||
} else {
|
|
||||||
setVisibility(View.INVISIBLE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ public class SwitchPreferenceCompat extends CheckBoxPreference {
|
||||||
setLayoutRes();
|
setLayoutRes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public SwitchPreferenceCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
setLayoutRes();
|
setLayoutRes();
|
||||||
|
|
|
@ -463,7 +463,7 @@ public class CameraView extends ViewGroup {
|
||||||
}
|
}
|
||||||
final float newWidth = visibleRect.width() * scale;
|
final float newWidth = visibleRect.width() * scale;
|
||||||
final float newHeight = visibleRect.height() * scale;
|
final float newHeight = visibleRect.height() * scale;
|
||||||
final float centerX = (VERSION.SDK_INT < 14 || isTroublemaker()) ? previewWidth - newWidth / 2 : previewWidth / 2;
|
final float centerX = isTroublemaker() ? previewWidth - newWidth / 2 : previewWidth / 2;
|
||||||
final float centerY = previewHeight / 2;
|
final float centerY = previewHeight / 2;
|
||||||
|
|
||||||
visibleRect.set((int) (centerX - newWidth / 2),
|
visibleRect.set((int) (centerX - newWidth / 2),
|
||||||
|
|
|
@ -260,9 +260,7 @@ public class DcHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static File getImexDir() {
|
public static File getImexDir() {
|
||||||
// DIRECTORY_DOCUMENTS is only available since KitKat;
|
// DIRECTORY_DOCUMENTS could be used but DIRECTORY_DOWNLOADS seems to be easier accessible by the user,
|
||||||
// as we also support Ice Cream Sandwich and Jellybean (2017: 11% in total), this is no option.
|
|
||||||
// Moreover, DIRECTORY_DOWNLOADS seems to be easier accessible by the user,
|
|
||||||
// eg. "Download Managers" are nearly always installed.
|
// eg. "Download Managers" are nearly always installed.
|
||||||
// CAVE: do not use DownloadManager to add the file as it is deleted on uninstall then ...
|
// CAVE: do not use DownloadManager to add the file as it is deleted on uninstall then ...
|
||||||
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||||
|
|
|
@ -1,345 +0,0 @@
|
||||||
/*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will Google be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
*
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, as long as the origin is not misrepresented.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.thoughtcrime.securesms.crypto;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.Provider;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.security.SecureRandomSpi;
|
|
||||||
import java.security.Security;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class is taken directly from the Android blog post announcing this bug:
|
|
||||||
* http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html
|
|
||||||
*
|
|
||||||
* Since I still don't know exactly what the source of this bug was, I'm using
|
|
||||||
* this class verbatim under the assumption that the Android team knows what
|
|
||||||
* they're doing. Although, at this point, that is perhaps a foolish assumption.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fixes for the output of the default PRNG having low entropy.
|
|
||||||
*
|
|
||||||
* The fixes need to be applied via {@link #apply()} before any use of Java
|
|
||||||
* Cryptography Architecture primitives. A good place to invoke them is in the
|
|
||||||
* application's {@code onCreate}.
|
|
||||||
*/
|
|
||||||
public final class PRNGFixes {
|
|
||||||
|
|
||||||
private static final int VERSION_CODE_JELLY_BEAN = 16;
|
|
||||||
private static final int VERSION_CODE_JELLY_BEAN_MR2 = 18;
|
|
||||||
private static final byte[] BUILD_FINGERPRINT_AND_DEVICE_SERIAL =
|
|
||||||
getBuildFingerprintAndDeviceSerial();
|
|
||||||
|
|
||||||
/** Hidden constructor to prevent instantiation. */
|
|
||||||
private PRNGFixes() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies all fixes.
|
|
||||||
*
|
|
||||||
* @throws SecurityException if a fix is needed but could not be applied.
|
|
||||||
*/
|
|
||||||
public static void apply() {
|
|
||||||
applyOpenSSLFix();
|
|
||||||
installLinuxPRNGSecureRandom();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies the fix for OpenSSL PRNG having low entropy. Does nothing if the
|
|
||||||
* fix is not needed.
|
|
||||||
*
|
|
||||||
* @throws SecurityException if the fix is needed but could not be applied.
|
|
||||||
*/
|
|
||||||
private static void applyOpenSSLFix() throws SecurityException {
|
|
||||||
if ((Build.VERSION.SDK_INT < VERSION_CODE_JELLY_BEAN)
|
|
||||||
|| (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2)) {
|
|
||||||
// No need to apply the fix
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Mix in the device- and invocation-specific seed.
|
|
||||||
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
|
||||||
.getMethod("RAND_seed", byte[].class)
|
|
||||||
.invoke(null, generateSeed());
|
|
||||||
|
|
||||||
// Mix output of Linux PRNG into OpenSSL's PRNG
|
|
||||||
int bytesRead = (Integer) Class.forName(
|
|
||||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
|
||||||
.getMethod("RAND_load_file", String.class, long.class)
|
|
||||||
.invoke(null, "/dev/urandom", 1024);
|
|
||||||
if (bytesRead != 1024) {
|
|
||||||
throw new IOException(
|
|
||||||
"Unexpected number of bytes read from Linux PRNG: "
|
|
||||||
+ bytesRead);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new SecurityException("Failed to seed OpenSSL PRNG", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Installs a Linux PRNG-backed {@code SecureRandom} implementation as the
|
|
||||||
* default. Does nothing if the implementation is already the default or if
|
|
||||||
* there is not need to install the implementation.
|
|
||||||
*
|
|
||||||
* @throws SecurityException if the fix is needed but could not be applied.
|
|
||||||
*/
|
|
||||||
private static void installLinuxPRNGSecureRandom()
|
|
||||||
throws SecurityException {
|
|
||||||
if (Build.VERSION.SDK_INT > VERSION_CODE_JELLY_BEAN_MR2) {
|
|
||||||
// No need to apply the fix
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install a Linux PRNG-based SecureRandom implementation as the
|
|
||||||
// default, if not yet installed.
|
|
||||||
Provider[] secureRandomProviders =
|
|
||||||
Security.getProviders("SecureRandom.SHA1PRNG");
|
|
||||||
if ((secureRandomProviders == null)
|
|
||||||
|| (secureRandomProviders.length < 1)
|
|
||||||
|| (!LinuxPRNGSecureRandomProvider.class.equals(
|
|
||||||
secureRandomProviders[0].getClass()))) {
|
|
||||||
Security.insertProviderAt(new LinuxPRNGSecureRandomProvider(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assert that new SecureRandom() and
|
|
||||||
// SecureRandom.getInstance("SHA1PRNG") return a SecureRandom backed
|
|
||||||
// by the Linux PRNG-based SecureRandom implementation.
|
|
||||||
SecureRandom rng1 = new SecureRandom();
|
|
||||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
|
||||||
rng1.getProvider().getClass())) {
|
|
||||||
throw new SecurityException(
|
|
||||||
"new SecureRandom() backed by wrong Provider: "
|
|
||||||
+ rng1.getProvider().getClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
SecureRandom rng2;
|
|
||||||
try {
|
|
||||||
rng2 = SecureRandom.getInstance("SHA1PRNG");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new SecurityException("SHA1PRNG not available", e);
|
|
||||||
}
|
|
||||||
if (!LinuxPRNGSecureRandomProvider.class.equals(
|
|
||||||
rng2.getProvider().getClass())) {
|
|
||||||
throw new SecurityException(
|
|
||||||
"SecureRandom.getInstance(\"SHA1PRNG\") backed by wrong"
|
|
||||||
+ " Provider: " + rng2.getProvider().getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code Provider} of {@code SecureRandom} engines which pass through
|
|
||||||
* all requests to the Linux PRNG.
|
|
||||||
*/
|
|
||||||
private static class LinuxPRNGSecureRandomProvider extends Provider {
|
|
||||||
|
|
||||||
public LinuxPRNGSecureRandomProvider() {
|
|
||||||
super("LinuxPRNG",
|
|
||||||
1.0,
|
|
||||||
"A Linux-specific random number provider that uses"
|
|
||||||
+ " /dev/urandom");
|
|
||||||
// Although /dev/urandom is not a SHA-1 PRNG, some apps
|
|
||||||
// explicitly request a SHA1PRNG SecureRandom and we thus need to
|
|
||||||
// prevent them from getting the default implementation whose output
|
|
||||||
// may have low entropy.
|
|
||||||
put("SecureRandom.SHA1PRNG", LinuxPRNGSecureRandom.class.getName());
|
|
||||||
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG
|
|
||||||
* ({@code /dev/urandom}).
|
|
||||||
*/
|
|
||||||
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IMPLEMENTATION NOTE: Requests to generate bytes and to mix in a seed
|
|
||||||
* are passed through to the Linux PRNG (/dev/urandom). Instances of
|
|
||||||
* this class seed themselves by mixing in the current time, PID, UID,
|
|
||||||
* build fingerprint, and hardware serial number (where available) into
|
|
||||||
* Linux PRNG.
|
|
||||||
*
|
|
||||||
* Concurrency: Read requests to the underlying Linux PRNG are
|
|
||||||
* serialized (on sLock) to ensure that multiple threads do not get
|
|
||||||
* duplicated PRNG output.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private static final File URANDOM_FILE = new File("/dev/urandom");
|
|
||||||
|
|
||||||
private static final Object sLock = new Object();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Input stream for reading from Linux PRNG or {@code null} if not yet
|
|
||||||
* opened.
|
|
||||||
*
|
|
||||||
* @GuardedBy("sLock")
|
|
||||||
*/
|
|
||||||
private static DataInputStream sUrandomIn;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output stream for writing to Linux PRNG or {@code null} if not yet
|
|
||||||
* opened.
|
|
||||||
*
|
|
||||||
* @GuardedBy("sLock")
|
|
||||||
*/
|
|
||||||
private static OutputStream sUrandomOut;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this engine instance has been seeded. This is needed because
|
|
||||||
* each instance needs to seed itself if the client does not explicitly
|
|
||||||
* seed it.
|
|
||||||
*/
|
|
||||||
private boolean mSeeded;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineSetSeed(byte[] bytes) {
|
|
||||||
try {
|
|
||||||
OutputStream out;
|
|
||||||
synchronized (sLock) {
|
|
||||||
out = getUrandomOutputStream();
|
|
||||||
}
|
|
||||||
out.write(bytes);
|
|
||||||
out.flush();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// On a small fraction of devices /dev/urandom is not writable.
|
|
||||||
// Log and ignore.
|
|
||||||
Log.w(PRNGFixes.class.getSimpleName(),
|
|
||||||
"Failed to mix seed into " + URANDOM_FILE);
|
|
||||||
} finally {
|
|
||||||
mSeeded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void engineNextBytes(byte[] bytes) {
|
|
||||||
if (!mSeeded) {
|
|
||||||
// Mix in the device- and invocation-specific seed.
|
|
||||||
engineSetSeed(generateSeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
DataInputStream in;
|
|
||||||
synchronized (sLock) {
|
|
||||||
in = getUrandomInputStream();
|
|
||||||
}
|
|
||||||
synchronized (in) {
|
|
||||||
in.readFully(bytes);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SecurityException(
|
|
||||||
"Failed to read from " + URANDOM_FILE, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected byte[] engineGenerateSeed(int size) {
|
|
||||||
byte[] seed = new byte[size];
|
|
||||||
engineNextBytes(seed);
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private DataInputStream getUrandomInputStream() {
|
|
||||||
synchronized (sLock) {
|
|
||||||
if (sUrandomIn == null) {
|
|
||||||
// NOTE: Consider inserting a BufferedInputStream between
|
|
||||||
// DataInputStream and FileInputStream if you need higher
|
|
||||||
// PRNG output performance and can live with future PRNG
|
|
||||||
// output being pulled into this process prematurely.
|
|
||||||
try {
|
|
||||||
sUrandomIn = new DataInputStream(
|
|
||||||
new FileInputStream(URANDOM_FILE));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SecurityException("Failed to open "
|
|
||||||
+ URANDOM_FILE + " for reading", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sUrandomIn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private OutputStream getUrandomOutputStream() throws IOException {
|
|
||||||
synchronized (sLock) {
|
|
||||||
if (sUrandomOut == null) {
|
|
||||||
sUrandomOut = new FileOutputStream(URANDOM_FILE);
|
|
||||||
}
|
|
||||||
return sUrandomOut;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a device- and invocation-specific seed to be mixed into the
|
|
||||||
* Linux PRNG.
|
|
||||||
*/
|
|
||||||
private static byte[] generateSeed() {
|
|
||||||
try {
|
|
||||||
ByteArrayOutputStream seedBuffer = new ByteArrayOutputStream();
|
|
||||||
DataOutputStream seedBufferOut =
|
|
||||||
new DataOutputStream(seedBuffer);
|
|
||||||
seedBufferOut.writeLong(System.currentTimeMillis());
|
|
||||||
seedBufferOut.writeLong(System.nanoTime());
|
|
||||||
seedBufferOut.writeInt(Process.myPid());
|
|
||||||
seedBufferOut.writeInt(Process.myUid());
|
|
||||||
seedBufferOut.write(BUILD_FINGERPRINT_AND_DEVICE_SERIAL);
|
|
||||||
seedBufferOut.close();
|
|
||||||
return seedBuffer.toByteArray();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SecurityException("Failed to generate seed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the hardware serial number of this device.
|
|
||||||
*
|
|
||||||
* @return serial number or {@code null} if not available.
|
|
||||||
*/
|
|
||||||
private static String getDeviceSerialNumber() {
|
|
||||||
// We're using the Reflection API because Build.SERIAL is only available
|
|
||||||
// since API Level 9 (Gingerbread, Android 2.3).
|
|
||||||
try {
|
|
||||||
return (String) Build.class.getField("SERIAL").get(null);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] getBuildFingerprintAndDeviceSerial() {
|
|
||||||
StringBuilder result = new StringBuilder();
|
|
||||||
String fingerprint = Build.FINGERPRINT;
|
|
||||||
if (fingerprint != null) {
|
|
||||||
result.append(fingerprint);
|
|
||||||
}
|
|
||||||
String serial = getDeviceSerialNumber();
|
|
||||||
if (serial != null) {
|
|
||||||
result.append(serial);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return result.toString().getBytes("UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new RuntimeException("UTF-8 encoding not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -585,7 +585,7 @@ public class AttachmentManager {
|
||||||
final Intent intent = new Intent();
|
final Intent intent = new Intent();
|
||||||
intent.setType(type);
|
intent.setType(type);
|
||||||
|
|
||||||
if (extraMimeType != null && Build.VERSION.SDK_INT >= 19) {
|
if (extraMimeType != null) {
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeType);
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, extraMimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -597,14 +597,12 @@ public class AttachmentManager {
|
||||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
|
||||||
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
|
try {
|
||||||
try {
|
activity.startActivityForResult(intent, requestCode);
|
||||||
activity.startActivityForResult(intent, requestCode);
|
return;
|
||||||
return;
|
} catch (ActivityNotFoundException anfe) {
|
||||||
} catch (ActivityNotFoundException anfe) {
|
Log.w(TAG, "couldn't complete ACTION_OPEN_DOCUMENT, no activity found. falling back.");
|
||||||
Log.w(TAG, "couldn't complete ACTION_OPEN_DOCUMENT, no activity found. falling back.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||||
|
|
|
@ -187,9 +187,7 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
|
||||||
|
|
||||||
Preference developerModeEnabled = this.findPreference("pref_developer_mode_enabled");
|
Preference developerModeEnabled = this.findPreference("pref_developer_mode_enabled");
|
||||||
developerModeEnabled.setOnPreferenceChangeListener((preference, newValue) -> {
|
developerModeEnabled.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
WebView.setWebContentsDebuggingEnabled((Boolean) newValue);
|
||||||
WebView.setWebContentsDebuggingEnabled((Boolean) newValue);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ public class ProfilePreference extends Preference {
|
||||||
private TextView profileNameView;
|
private TextView profileNameView;
|
||||||
private TextView profileAddressView;
|
private TextView profileAddressView;
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public ProfilePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public ProfilePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
initialize();
|
initialize();
|
||||||
|
|
|
@ -63,15 +63,10 @@ public class AvatarHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cropAvatar(Activity context, Uri imageUri) {
|
public static void cropAvatar(Activity context, Uri imageUri) {
|
||||||
if (Build.VERSION.SDK_INT >= 19) { // Image editor requires Android 4.4 KitKat or newer.
|
Intent intent = new Intent(context, ScribbleActivity.class);
|
||||||
Intent intent = new Intent(context, ScribbleActivity.class);
|
intent.setData(imageUri);
|
||||||
intent.setData(imageUri);
|
intent.putExtra(ScribbleActivity.CROP_AVATAR, true);
|
||||||
intent.putExtra(ScribbleActivity.CROP_AVATAR, true);
|
context.startActivityForResult(intent, ScribbleActivity.SCRIBBLE_REQUEST_CODE);
|
||||||
context.startActivityForResult(intent, ScribbleActivity.SCRIBBLE_REQUEST_CODE);
|
|
||||||
} else {
|
|
||||||
Uri outputFile = Uri.fromFile(new File(context.getCacheDir(), "cropped"));
|
|
||||||
Crop.of(imageUri, outputFile).asSquare().start(context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,9 +59,6 @@ public class AddReactionView extends LinearLayout {
|
||||||
}
|
}
|
||||||
anyReactionView = findViewById(R.id.reaction_any);
|
anyReactionView = findViewById(R.id.reaction_any);
|
||||||
anyReactionView.setOnClickListener(v -> anyReactionClicked());
|
anyReactionView.setOnClickListener(v -> anyReactionClicked());
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
anyReactionView.setVisibility(View.GONE); // EmojiPickerView requires SDK 21 or newer
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,11 +96,7 @@ public final class ImageEditorHud extends LinearLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeVisibilityMap() {
|
private void initializeVisibilityMap() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, blurButton, textButton, stickerButton, cropButton, undoButton, saveButton);
|
||||||
setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, blurButton, textButton, stickerButton, cropButton, undoButton, saveButton);
|
|
||||||
} else {
|
|
||||||
setVisibleViewsWhenInMode(Mode.NONE, drawButton, highlightButton, textButton, stickerButton, cropButton, undoButton, saveButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
setVisibleViewsWhenInMode(Mode.DRAW, confirmButton, undoButton, colorPicker, colorPalette);
|
setVisibleViewsWhenInMode(Mode.DRAW, confirmButton, undoButton, colorPicker, colorPalette);
|
||||||
|
|
||||||
|
|
|
@ -117,16 +117,13 @@ final class UriGlideRenderer implements Renderer {
|
||||||
|
|
||||||
rendererContext.restore();
|
rendererContext.restore();
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
renderBlurOverlay(rendererContext);
|
||||||
renderBlurOverlay(rendererContext);
|
|
||||||
}
|
|
||||||
} else if (rendererContext.isBlockingLoad()) {
|
} else if (rendererContext.isBlockingLoad()) {
|
||||||
// If failed to load, we draw a black out, in case image was sticker positioned to cover private info.
|
// If failed to load, we draw a black out, in case image was sticker positioned to cover private info.
|
||||||
rendererContext.canvas.drawRect(Bounds.FULL_BOUNDS, paint);
|
rendererContext.canvas.drawRect(Bounds.FULL_BOUNDS, paint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(17)
|
|
||||||
private void renderBlurOverlay(RendererContext rendererContext) {
|
private void renderBlurOverlay(RendererContext rendererContext) {
|
||||||
boolean renderMask = false;
|
boolean renderMask = false;
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,6 @@ public class VerticalSlideColorPicker extends View {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
public VerticalSlideColorPicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
init();
|
init();
|
||||||
|
|
|
@ -15,10 +15,6 @@ public final class AccessibilityUtil {
|
||||||
Log.e("AccessibilityUtil", "animationsDisabled: context was null");
|
Log.e("AccessibilityUtil", "animationsDisabled: context was null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
return Settings.Global.getFloat(context.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1) == 0f;
|
||||||
return Settings.Global.getFloat(context.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, 1) == 0f;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,11 +110,7 @@ public class DateUtils extends android.text.format.DateUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getLocalizedPattern(String template) {
|
private static String getLocalizedPattern(String template) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
return DateFormat.getBestDateTimePattern(Util.getLocale(), template);
|
||||||
return DateFormat.getBestDateTimePattern(Util.getLocale(), template);
|
|
||||||
} else {
|
|
||||||
return new SimpleDateFormat(template, Util.getLocale()).toLocalizedPattern();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getFormatedDuration(long millis) {
|
public static String getFormatedDuration(long millis) {
|
||||||
|
|
|
@ -16,8 +16,7 @@ public class FileProviderUtil {
|
||||||
private static final String AUTHORITY = BuildConfig.APPLICATION_ID+".fileprovider";
|
private static final String AUTHORITY = BuildConfig.APPLICATION_ID+".fileprovider";
|
||||||
|
|
||||||
public static Uri getUriFor(@NonNull Context context, @NonNull File file) throws IllegalStateException, NullPointerException {
|
public static Uri getUriFor(@NonNull Context context, @NonNull File file) throws IllegalStateException, NullPointerException {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) return FileProvider.getUriForFile(context, AUTHORITY, file);
|
return FileProvider.getUriForFile(context, AUTHORITY, file);
|
||||||
else return Uri.fromFile(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ public class ScreenLockUtil {
|
||||||
public static boolean applyScreenLock(Activity activity, String title, String descr, int requestCode) {
|
public static boolean applyScreenLock(Activity activity, String title, String descr, int requestCode) {
|
||||||
KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
|
KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
|
||||||
Intent intent;
|
Intent intent;
|
||||||
if (keyguardManager != null && isScreenLockAvailable()) {
|
if (keyguardManager != null) {
|
||||||
intent = keyguardManager.createConfirmDeviceCredentialIntent(title, descr);
|
intent = keyguardManager.createConfirmDeviceCredentialIntent(title, descr);
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
activity.startActivityForResult(intent, requestCode);
|
activity.startActivityForResult(intent, requestCode);
|
||||||
|
@ -22,7 +22,4 @@ public class ScreenLockUtil {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isScreenLockAvailable() {
|
|
||||||
return android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,28 +20,16 @@ public class StorageUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Uri getVideoUri() {
|
public static @NonNull Uri getVideoUri() {
|
||||||
if (Build.VERSION.SDK_INT < 21) {
|
return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
return getLegacyUri(Environment.DIRECTORY_MOVIES);
|
|
||||||
} else {
|
|
||||||
return MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull
|
public static @NonNull
|
||||||
Uri getAudioUri() {
|
Uri getAudioUri() {
|
||||||
if (Build.VERSION.SDK_INT < 21) {
|
return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
return getLegacyUri(Environment.DIRECTORY_MUSIC);
|
|
||||||
} else {
|
|
||||||
return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Uri getImageUri() {
|
public static @NonNull Uri getImageUri() {
|
||||||
if (Build.VERSION.SDK_INT < 21) {
|
return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
return getLegacyUri(Environment.DIRECTORY_PICTURES);
|
|
||||||
} else {
|
|
||||||
return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull Uri getDownloadUri() {
|
public static @NonNull Uri getDownloadUri() {
|
||||||
|
|
|
@ -257,12 +257,10 @@ public class Util {
|
||||||
return Arrays.hashCode(objects);
|
return Arrays.hashCode(objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.KITKAT)
|
|
||||||
public static boolean isLowMemory(Context context) {
|
public static boolean isLowMemory(Context context) {
|
||||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
|
|
||||||
return (VERSION.SDK_INT >= VERSION_CODES.KITKAT && activityManager.isLowRamDevice()) ||
|
return activityManager.isLowRamDevice() || activityManager.getLargeMemoryClass() <= 64;
|
||||||
activityManager.getLargeMemoryClass() <= 64;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int clamp(int value, int min, int max) {
|
public static int clamp(int value, int min, int max) {
|
||||||
|
@ -386,10 +384,7 @@ public class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getLayoutDirection(Context context) {
|
public static int getLayoutDirection(Context context) {
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
Configuration configuration = context.getResources().getConfiguration();
|
||||||
Configuration configuration = context.getResources().getConfiguration();
|
return configuration.getLayoutDirection();
|
||||||
return configuration.getLayoutDirection();
|
|
||||||
}
|
|
||||||
return ViewCompat.LAYOUT_DIRECTION_LTR;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,18 +168,15 @@ public class ViewUtil {
|
||||||
|
|
||||||
@SuppressLint("RtlHardcoded")
|
@SuppressLint("RtlHardcoded")
|
||||||
public static void setTextViewGravityStart(final @NonNull TextView textView, @NonNull Context context) {
|
public static void setTextViewGravityStart(final @NonNull TextView textView, @NonNull Context context) {
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
||||||
if (Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
textView.setGravity(Gravity.RIGHT);
|
||||||
textView.setGravity(Gravity.RIGHT);
|
} else {
|
||||||
} else {
|
textView.setGravity(Gravity.LEFT);
|
||||||
textView.setGravity(Gravity.LEFT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void mirrorIfRtl(View view, Context context) {
|
public static void mirrorIfRtl(View view, Context context) {
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1 &&
|
if (Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
||||||
Util.getLayoutDirection(context) == View.LAYOUT_DIRECTION_RTL) {
|
|
||||||
view.setScaleX(-1.0f);
|
view.setScaleX(-1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
package org.thoughtcrime.securesms.video.recode;
|
package org.thoughtcrime.securesms.video.recode;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.media.MediaCodecList;
|
|
||||||
import android.media.MediaExtractor;
|
import android.media.MediaExtractor;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Build;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
@ -36,14 +32,7 @@ public class VideoRecoder {
|
||||||
|
|
||||||
private static final String TAG = VideoRecoder.class.getSimpleName();
|
private static final String TAG = VideoRecoder.class.getSimpleName();
|
||||||
|
|
||||||
private boolean videoConvertFirstWrite = true;
|
|
||||||
private final static String MIME_TYPE = "video/avc";
|
private final static String MIME_TYPE = "video/avc";
|
||||||
private final static int PROCESSOR_TYPE_OTHER = 0;
|
|
||||||
private final static int PROCESSOR_TYPE_QCOM = 1;
|
|
||||||
private final static int PROCESSOR_TYPE_INTEL = 2;
|
|
||||||
private final static int PROCESSOR_TYPE_MTK = 3;
|
|
||||||
private final static int PROCESSOR_TYPE_SEC = 4;
|
|
||||||
private final static int PROCESSOR_TYPE_TI = 5;
|
|
||||||
private final boolean cancelCurrentVideoConversion = false;
|
private final boolean cancelCurrentVideoConversion = false;
|
||||||
private final Object videoConvertSync = new Object();
|
private final Object videoConvertSync = new Object();
|
||||||
|
|
||||||
|
@ -57,7 +46,6 @@ public class VideoRecoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(16)
|
|
||||||
private int selectTrack(MediaExtractor extractor, boolean audio) {
|
private int selectTrack(MediaExtractor extractor, boolean audio) {
|
||||||
int numTracks = extractor.getTrackCount();
|
int numTracks = extractor.getTrackCount();
|
||||||
for (int i = 0; i < numTracks; i++) {
|
for (int i = 0; i < numTracks; i++) {
|
||||||
|
@ -76,60 +64,6 @@ public class VideoRecoder {
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private static MediaCodecInfo selectCodec(String mimeType) {
|
|
||||||
int numCodecs = MediaCodecList.getCodecCount();
|
|
||||||
MediaCodecInfo lastCodecInfo = null;
|
|
||||||
for (int i = 0; i < numCodecs; i++) {
|
|
||||||
MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
|
|
||||||
if (!codecInfo.isEncoder()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String[] types = codecInfo.getSupportedTypes();
|
|
||||||
for (String type : types) {
|
|
||||||
if (type.equalsIgnoreCase(mimeType)) {
|
|
||||||
lastCodecInfo = codecInfo;
|
|
||||||
if (!lastCodecInfo.getName().equals("OMX.SEC.avc.enc")) {
|
|
||||||
return lastCodecInfo;
|
|
||||||
} else if (lastCodecInfo.getName().equals("OMX.SEC.AVC.Encoder")) {
|
|
||||||
return lastCodecInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lastCodecInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isRecognizedFormat(int colorFormat) {
|
|
||||||
switch (colorFormat) {
|
|
||||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
|
|
||||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
|
|
||||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
|
|
||||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
|
|
||||||
case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private static int selectColorFormat(MediaCodecInfo codecInfo, String mimeType) {
|
|
||||||
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
|
|
||||||
int lastColorFormat = 0;
|
|
||||||
for (int i = 0; i < capabilities.colorFormats.length; i++) {
|
|
||||||
int colorFormat = capabilities.colorFormats[i];
|
|
||||||
if (isRecognizedFormat(colorFormat)) {
|
|
||||||
lastColorFormat = colorFormat;
|
|
||||||
if (!(codecInfo.getName().equals("OMX.SEC.AVC.Encoder") && colorFormat == 19)) {
|
|
||||||
return colorFormat;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lastColorFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(16)
|
|
||||||
private long readAndWriteTrack(MediaExtractor extractor, MP4Builder mediaMuxer, MediaCodec.BufferInfo info, long start, long end, File file, boolean isAudio) throws Exception {
|
private long readAndWriteTrack(MediaExtractor extractor, MP4Builder mediaMuxer, MediaCodec.BufferInfo info, long start, long end, File file, boolean isAudio) throws Exception {
|
||||||
int trackIndex = selectTrack(extractor, isAudio);
|
int trackIndex = selectTrack(extractor, isAudio);
|
||||||
if (trackIndex >= 0) {
|
if (trackIndex >= 0) {
|
||||||
|
@ -199,7 +133,6 @@ public class VideoRecoder {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(16)
|
|
||||||
private boolean convertVideo(final VideoEditedInfo videoEditedInfo, String destPath) {
|
private boolean convertVideo(final VideoEditedInfo videoEditedInfo, String destPath) {
|
||||||
|
|
||||||
long startTime = videoEditedInfo.startTime;
|
long startTime = videoEditedInfo.startTime;
|
||||||
|
@ -214,29 +147,21 @@ public class VideoRecoder {
|
||||||
int rotateRender = 0;
|
int rotateRender = 0;
|
||||||
File cacheFile = new File(destPath);
|
File cacheFile = new File(destPath);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < 18 && resultHeight > resultWidth && resultWidth != originalWidth && resultHeight != originalHeight) {
|
if (rotationValue == 90) {
|
||||||
int temp = resultHeight;
|
int temp = resultHeight;
|
||||||
resultHeight = resultWidth;
|
resultHeight = resultWidth;
|
||||||
resultWidth = temp;
|
resultWidth = temp;
|
||||||
rotationValue = 90;
|
rotationValue = 0;
|
||||||
rotateRender = 270;
|
rotateRender = 270;
|
||||||
} else if (Build.VERSION.SDK_INT > 20) {
|
} else if (rotationValue == 180) {
|
||||||
if (rotationValue == 90) {
|
rotateRender = 180;
|
||||||
int temp = resultHeight;
|
rotationValue = 0;
|
||||||
resultHeight = resultWidth;
|
} else if (rotationValue == 270) {
|
||||||
resultWidth = temp;
|
int temp = resultHeight;
|
||||||
rotationValue = 0;
|
resultHeight = resultWidth;
|
||||||
rotateRender = 270;
|
resultWidth = temp;
|
||||||
} else if (rotationValue == 180) {
|
rotationValue = 0;
|
||||||
rotateRender = 180;
|
rotateRender = 90;
|
||||||
rotationValue = 0;
|
|
||||||
} else if (rotationValue == 270) {
|
|
||||||
int temp = resultHeight;
|
|
||||||
resultHeight = resultWidth;
|
|
||||||
resultWidth = temp;
|
|
||||||
rotationValue = 0;
|
|
||||||
rotateRender = 90;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File inputFile = new File(videoEditedInfo.originalPath);
|
File inputFile = new File(videoEditedInfo.originalPath);
|
||||||
|
@ -246,7 +171,6 @@ public class VideoRecoder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
videoConvertFirstWrite = true;
|
|
||||||
boolean error = false;
|
boolean error = false;
|
||||||
long videoStartTime = startTime;
|
long videoStartTime = startTime;
|
||||||
|
|
||||||
|
@ -282,71 +206,12 @@ public class VideoRecoder {
|
||||||
boolean outputDone = false;
|
boolean outputDone = false;
|
||||||
boolean inputDone = false;
|
boolean inputDone = false;
|
||||||
boolean decoderDone = false;
|
boolean decoderDone = false;
|
||||||
int swapUV = 0;
|
|
||||||
int videoTrackIndex = -5;
|
int videoTrackIndex = -5;
|
||||||
|
|
||||||
int colorFormat;
|
int colorFormat;
|
||||||
int processorType = PROCESSOR_TYPE_OTHER;
|
colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
|
||||||
String manufacturer = Build.MANUFACTURER.toLowerCase();
|
|
||||||
if (Build.VERSION.SDK_INT < 18) {
|
|
||||||
MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
|
|
||||||
colorFormat = selectColorFormat(codecInfo, MIME_TYPE);
|
|
||||||
if (colorFormat == 0) {
|
|
||||||
throw new RuntimeException("no supported color format");
|
|
||||||
}
|
|
||||||
String codecName = codecInfo.getName();
|
|
||||||
if (codecName.contains("OMX.qcom.")) {
|
|
||||||
processorType = PROCESSOR_TYPE_QCOM;
|
|
||||||
if (Build.VERSION.SDK_INT == 16) {
|
|
||||||
if (manufacturer.equals("lge") || manufacturer.equals("nokia")) {
|
|
||||||
swapUV = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (codecName.contains("OMX.Intel.")) {
|
|
||||||
processorType = PROCESSOR_TYPE_INTEL;
|
|
||||||
} else if (codecName.equals("OMX.MTK.VIDEO.ENCODER.AVC")) {
|
|
||||||
processorType = PROCESSOR_TYPE_MTK;
|
|
||||||
} else if (codecName.equals("OMX.SEC.AVC.Encoder")) {
|
|
||||||
processorType = PROCESSOR_TYPE_SEC;
|
|
||||||
swapUV = 1;
|
|
||||||
} else if (codecName.equals("OMX.TI.DUCATI1.VIDEO.H264E")) {
|
|
||||||
processorType = PROCESSOR_TYPE_TI;
|
|
||||||
}
|
|
||||||
//Log.i("DeltaChat", "codec = " + codecInfo.getName() + " manufacturer = " + manufacturer + "device = " + Build.MODEL);
|
|
||||||
} else {
|
|
||||||
colorFormat = MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
|
|
||||||
}
|
|
||||||
//Log.i("DeltaChat", "colorFormat = " + colorFormat);
|
//Log.i("DeltaChat", "colorFormat = " + colorFormat);
|
||||||
|
|
||||||
int resultHeightAligned = resultHeight;
|
|
||||||
int padding = 0;
|
|
||||||
int bufferSize = resultWidth * resultHeight * 3 / 2;
|
|
||||||
if (processorType == PROCESSOR_TYPE_OTHER) {
|
|
||||||
if (resultHeight % 16 != 0) {
|
|
||||||
resultHeightAligned += (16 - (resultHeight % 16));
|
|
||||||
padding = resultWidth * (resultHeightAligned - resultHeight);
|
|
||||||
bufferSize += padding * 5 / 4;
|
|
||||||
}
|
|
||||||
} else if (processorType == PROCESSOR_TYPE_QCOM) {
|
|
||||||
if (!manufacturer.toLowerCase().equals("lge")) {
|
|
||||||
int uvoffset = (resultWidth * resultHeight + 2047) & ~2047;
|
|
||||||
padding = uvoffset - (resultWidth * resultHeight);
|
|
||||||
bufferSize += padding;
|
|
||||||
}
|
|
||||||
} else if (processorType == PROCESSOR_TYPE_TI) {
|
|
||||||
//resultHeightAligned = 368;
|
|
||||||
//bufferSize = resultWidth * resultHeightAligned * 3 / 2;
|
|
||||||
//resultHeightAligned += (16 - (resultHeight % 16));
|
|
||||||
//padding = resultWidth * (resultHeightAligned - resultHeight);
|
|
||||||
//bufferSize += padding * 5 / 4;
|
|
||||||
} else if (processorType == PROCESSOR_TYPE_MTK) {
|
|
||||||
if (manufacturer.equals("baidu")) {
|
|
||||||
resultHeightAligned += (16 - (resultHeight % 16));
|
|
||||||
padding = resultWidth * (resultHeightAligned - resultHeight);
|
|
||||||
bufferSize += padding * 5 / 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extractor.selectTrack(videoIndex);
|
extractor.selectTrack(videoIndex);
|
||||||
if (startTime > 0) {
|
if (startTime > 0) {
|
||||||
extractor.seekTo(startTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
|
extractor.seekTo(startTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
|
||||||
|
@ -360,39 +225,21 @@ public class VideoRecoder {
|
||||||
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, resultVideoBitrate != 0 ? resultVideoBitrate : 921600);
|
outputFormat.setInteger(MediaFormat.KEY_BIT_RATE, resultVideoBitrate != 0 ? resultVideoBitrate : 921600);
|
||||||
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
|
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
|
||||||
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
|
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
|
||||||
if (Build.VERSION.SDK_INT < 18) {
|
|
||||||
outputFormat.setInteger("stride", resultWidth + 32);
|
|
||||||
outputFormat.setInteger("slice-height", resultHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
|
encoder = MediaCodec.createEncoderByType(MIME_TYPE);
|
||||||
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
encoder.configure(outputFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
||||||
if (Build.VERSION.SDK_INT >= 18) {
|
inputSurface = new InputSurface(encoder.createInputSurface());
|
||||||
inputSurface = new InputSurface(encoder.createInputSurface());
|
inputSurface.makeCurrent();
|
||||||
inputSurface.makeCurrent();
|
|
||||||
}
|
|
||||||
encoder.start();
|
encoder.start();
|
||||||
|
|
||||||
decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME));
|
decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME));
|
||||||
if (Build.VERSION.SDK_INT >= 18) {
|
outputSurface = new OutputSurface();
|
||||||
outputSurface = new OutputSurface();
|
|
||||||
} else {
|
|
||||||
outputSurface = new OutputSurface(resultWidth, resultHeight, rotateRender);
|
|
||||||
}
|
|
||||||
decoder.configure(inputFormat, outputSurface.getSurface(), null, 0);
|
decoder.configure(inputFormat, outputSurface.getSurface(), null, 0);
|
||||||
decoder.start();
|
decoder.start();
|
||||||
|
|
||||||
final int TIMEOUT_USEC = 2500;
|
final int TIMEOUT_USEC = 2500;
|
||||||
ByteBuffer[] decoderInputBuffers = null;
|
ByteBuffer[] decoderInputBuffers = null;
|
||||||
ByteBuffer[] encoderOutputBuffers = null;
|
ByteBuffer[] encoderOutputBuffers = null;
|
||||||
ByteBuffer[] encoderInputBuffers = null;
|
|
||||||
if (Build.VERSION.SDK_INT < 21) {
|
|
||||||
decoderInputBuffers = decoder.getInputBuffers();
|
|
||||||
encoderOutputBuffers = encoder.getOutputBuffers();
|
|
||||||
if (Build.VERSION.SDK_INT < 18) {
|
|
||||||
encoderInputBuffers = encoder.getInputBuffers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
checkConversionCanceled();
|
checkConversionCanceled();
|
||||||
|
|
||||||
|
@ -405,11 +252,7 @@ public class VideoRecoder {
|
||||||
int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
|
int inputBufIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
|
||||||
if (inputBufIndex >= 0) {
|
if (inputBufIndex >= 0) {
|
||||||
ByteBuffer inputBuf;
|
ByteBuffer inputBuf;
|
||||||
if (Build.VERSION.SDK_INT < 21) {
|
inputBuf = decoder.getInputBuffer(inputBufIndex);
|
||||||
inputBuf = decoderInputBuffers[inputBufIndex];
|
|
||||||
} else {
|
|
||||||
inputBuf = decoder.getInputBuffer(inputBufIndex);
|
|
||||||
}
|
|
||||||
int chunkSize = extractor.readSampleData(inputBuf, 0);
|
int chunkSize = extractor.readSampleData(inputBuf, 0);
|
||||||
if (chunkSize < 0) {
|
if (chunkSize < 0) {
|
||||||
decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
decoder.queueInputBuffer(inputBufIndex, 0, 0, 0L, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||||
|
@ -439,9 +282,6 @@ public class VideoRecoder {
|
||||||
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
|
||||||
encoderOutputAvailable = false;
|
encoderOutputAvailable = false;
|
||||||
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||||
if (Build.VERSION.SDK_INT < 21) {
|
|
||||||
encoderOutputBuffers = encoder.getOutputBuffers();
|
|
||||||
}
|
|
||||||
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
|
||||||
MediaFormat newFormat = encoder.getOutputFormat();
|
MediaFormat newFormat = encoder.getOutputFormat();
|
||||||
if (videoTrackIndex == -5) {
|
if (videoTrackIndex == -5) {
|
||||||
|
@ -451,11 +291,7 @@ public class VideoRecoder {
|
||||||
throw new RuntimeException("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
|
throw new RuntimeException("unexpected result from encoder.dequeueOutputBuffer: " + encoderStatus);
|
||||||
} else {
|
} else {
|
||||||
ByteBuffer encodedData;
|
ByteBuffer encodedData;
|
||||||
if (Build.VERSION.SDK_INT < 21) {
|
encodedData = encoder.getOutputBuffer(encoderStatus);
|
||||||
encodedData = encoderOutputBuffers[encoderStatus];
|
|
||||||
} else {
|
|
||||||
encodedData = encoder.getOutputBuffer(encoderStatus);
|
|
||||||
}
|
|
||||||
if (encodedData == null) {
|
if (encodedData == null) {
|
||||||
throw new RuntimeException("encoderOutputBuffer " + encoderStatus + " was null");
|
throw new RuntimeException("encoderOutputBuffer " + encoderStatus + " was null");
|
||||||
}
|
}
|
||||||
|
@ -513,11 +349,7 @@ public class VideoRecoder {
|
||||||
throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus);
|
throw new RuntimeException("unexpected result from decoder.dequeueOutputBuffer: " + decoderStatus);
|
||||||
} else {
|
} else {
|
||||||
boolean doRender;
|
boolean doRender;
|
||||||
if (Build.VERSION.SDK_INT >= 18) {
|
doRender = info.size != 0;
|
||||||
doRender = info.size != 0;
|
|
||||||
} else {
|
|
||||||
doRender = info.size != 0 || info.presentationTimeUs != 0;
|
|
||||||
}
|
|
||||||
if (endTime > 0 && info.presentationTimeUs >= endTime) {
|
if (endTime > 0 && info.presentationTimeUs >= endTime) {
|
||||||
inputDone = true;
|
inputDone = true;
|
||||||
decoderDone = true;
|
decoderDone = true;
|
||||||
|
@ -542,40 +374,15 @@ public class VideoRecoder {
|
||||||
Log.w(TAG, "error while waiting for recording output surface", e);
|
Log.w(TAG, "error while waiting for recording output surface", e);
|
||||||
}
|
}
|
||||||
if (!errorWait) {
|
if (!errorWait) {
|
||||||
if (Build.VERSION.SDK_INT >= 18) {
|
outputSurface.drawImage(false);
|
||||||
outputSurface.drawImage(false);
|
inputSurface.setPresentationTime(info.presentationTimeUs * 1000);
|
||||||
inputSurface.setPresentationTime(info.presentationTimeUs * 1000);
|
inputSurface.swapBuffers();
|
||||||
inputSurface.swapBuffers();
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Cannot proceed with the current SDK version");
|
|
||||||
return false; // TODO: this should be caught much earlier
|
|
||||||
/*
|
|
||||||
int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
|
|
||||||
if (inputBufIndex >= 0) {
|
|
||||||
outputSurface.drawImage(true);
|
|
||||||
ByteBuffer rgbBuf = outputSurface.getFrame();
|
|
||||||
ByteBuffer yuvBuf = encoderInputBuffers[inputBufIndex];
|
|
||||||
yuvBuf.clear();
|
|
||||||
Utilities.convertVideoFrame(rgbBuf, yuvBuf, colorFormat, resultWidth, resultHeight, padding, swapUV);
|
|
||||||
encoder.queueInputBuffer(inputBufIndex, 0, bufferSize, info.presentationTimeUs, 0);
|
|
||||||
} else {
|
|
||||||
//Log.i("DeltaChat", "input buffer not available");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
|
||||||
decoderOutputAvailable = false;
|
decoderOutputAvailable = false;
|
||||||
//Log.i("DeltaChat", "decoder stream end");
|
//Log.i("DeltaChat", "decoder stream end");
|
||||||
if (Build.VERSION.SDK_INT >= 18) {
|
encoder.signalEndOfInputStream();
|
||||||
encoder.signalEndOfInputStream();
|
|
||||||
} else {
|
|
||||||
int inputBufIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
|
|
||||||
if (inputBufIndex >= 0) {
|
|
||||||
encoder.queueInputBuffer(inputBufIndex, 0, 1, info.presentationTimeUs, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,45 +469,6 @@ public class VideoRecoder {
|
||||||
int estimatedBytes;
|
int estimatedBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean canRecode()
|
|
||||||
{
|
|
||||||
boolean canRecode = true;
|
|
||||||
if (Build.VERSION.SDK_INT < 16 /*= Jelly Bean 4.1 (before that codecInfo.getName() was not there) */) {
|
|
||||||
Log.w(TAG, "Cannot recode: API < 16");
|
|
||||||
canRecode = false;
|
|
||||||
}
|
|
||||||
else if (Build.VERSION.SDK_INT < 18 /*= Jelly Bean 4.3*/) {
|
|
||||||
try {
|
|
||||||
MediaCodecInfo codecInfo = VideoRecoder.selectCodec(VideoRecoder.MIME_TYPE);
|
|
||||||
if (codecInfo == null) {
|
|
||||||
Log.w(TAG, "Cannot recode: cannot select codec");
|
|
||||||
canRecode = false;
|
|
||||||
} else {
|
|
||||||
String name = codecInfo.getName();
|
|
||||||
if (name.equals("OMX.google.h264.encoder") ||
|
|
||||||
name.equals("OMX.ST.VFM.H264Enc") ||
|
|
||||||
name.equals("OMX.Exynos.avc.enc") ||
|
|
||||||
name.equals("OMX.MARVELL.VIDEO.HW.CODA7542ENCODER") ||
|
|
||||||
name.equals("OMX.MARVELL.VIDEO.H264ENCODER") ||
|
|
||||||
name.equals("OMX.k3.video.encoder.avc") ||
|
|
||||||
name.equals("OMX.TI.DUCATI1.VIDEO.H264E")) {
|
|
||||||
Log.w(TAG, "Cannot recode: no supported codec found");
|
|
||||||
canRecode = false;
|
|
||||||
} else {
|
|
||||||
if (VideoRecoder.selectColorFormat(codecInfo, VideoRecoder.MIME_TYPE) == 0) {
|
|
||||||
Log.w(TAG, "Cannot recode: cannot select color format");
|
|
||||||
canRecode = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, "Cannot recode: Determinating recoding capabilities failed unexpectedly", e);
|
|
||||||
canRecode = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return canRecode;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static VideoEditedInfo getVideoEditInfoFromFile(String videoPath) {
|
private static VideoEditedInfo getVideoEditInfoFromFile(String videoPath) {
|
||||||
// load information for the given video
|
// load information for the given video
|
||||||
VideoEditedInfo vei = new VideoEditedInfo();
|
VideoEditedInfo vei = new VideoEditedInfo();
|
||||||
|
@ -811,11 +579,6 @@ public class VideoRecoder {
|
||||||
}
|
}
|
||||||
msg.setDuration((int)vei.originalDurationMs);
|
msg.setDuration((int)vei.originalDurationMs);
|
||||||
|
|
||||||
if (!canRecode()) {
|
|
||||||
alert(context, String.format("Recoding failed for %s: this system cannot recode videos", inPath));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if video bitrate is already reasonable
|
// check if video bitrate is already reasonable
|
||||||
final int MAX_KBPS = 1500000;
|
final int MAX_KBPS = 1500000;
|
||||||
final long MAX_BYTES = DcHelper.getInt(context, "sys.msgsize_max_recommended");
|
final long MAX_BYTES = DcHelper.getInt(context, "sys.msgsize_max_recommended");
|
||||||
|
|
Before Width: | Height: | Size: 1,002 B After Width: | Height: | Size: 1,002 B |
Before Width: | Height: | Size: 750 B |
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 532 B |
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/touch_highlight">
|
|
||||||
<item android:id="@android:id/mask">
|
|
||||||
<shape android:shape="oval">
|
|
||||||
<solid android:color="@color/black" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/delta_primary">
|
|
||||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
|
||||||
<item>
|
|
||||||
<selector>
|
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
|
||||||
</selector>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/delta_primary">
|
|
||||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
|
||||||
<item>
|
|
||||||
<selector>
|
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
|
||||||
</selector>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/delta_primary">
|
|
||||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
|
||||||
<item>
|
|
||||||
<selector>
|
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
|
||||||
<item android:drawable="@color/pinned_bg" />
|
|
||||||
</selector>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/delta_primary">
|
|
||||||
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
|
||||||
<item>
|
|
||||||
<selector>
|
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
|
||||||
<item android:drawable="@color/pinned_bg_dark" />
|
|
||||||
</selector>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/touch_highlight">
|
|
||||||
<item
|
|
||||||
android:id="@android:id/mask"
|
|
||||||
android:drawable="@android:color/white" />
|
|
||||||
</ripple>
|
|
|
@ -1,12 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Like touch_highlight_background, but creates a stronger (i.e. better visible)
|
|
||||||
visual feedback when the button is pressed. -->
|
|
||||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:color="@color/delta_primary">
|
|
||||||
<item android:id="@android:id/mask" android:drawable="@android:color/white" />
|
|
||||||
<item>
|
|
||||||
<selector>
|
|
||||||
<item android:drawable="@color/touch_highlight_strong" android:state_pressed="true" />
|
|
||||||
</selector>
|
|
||||||
</item>
|
|
||||||
</ripple>
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
@ -1,8 +1,9 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<item android:state_pressed="true">
|
android:color="@color/touch_highlight">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
<shape android:shape="oval">
|
<shape android:shape="oval">
|
||||||
<solid android:color="@color/touch_highlight" />
|
<solid android:color="@color/black" />
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</selector>
|
</ripple>
|
|
@ -1,6 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
android:color="@color/delta_primary">
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_pressed="true" />
|
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
<item>
|
||||||
</selector>
|
<selector>
|
||||||
|
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
android:color="@color/delta_primary">
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_pressed="true" />
|
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
<item>
|
||||||
</selector>
|
<selector>
|
||||||
|
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:drawable="@drawable/pause_icon"
|
android:drawable="@drawable/pause_icon"
|
||||||
tools:targetApi="lollipop">
|
>
|
||||||
|
|
||||||
<target
|
<target
|
||||||
android:name="@string/play_icon_group_parts"
|
android:name="@string/play_icon_group_parts"
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
android:color="@color/delta_primary">
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_pressed="true" />
|
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
<item>
|
||||||
<item android:drawable="@color/pinned_bg" />
|
<selector>
|
||||||
</selector>
|
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
||||||
|
<item android:drawable="@color/pinned_bg" />
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
android:color="@color/delta_primary">
|
||||||
<item android:drawable="@color/primary_alpha33" android:state_pressed="true" />
|
<item android:id="@android:id/mask" android:drawable="@android:color/black" />
|
||||||
<item android:drawable="@color/primary_alpha_focus" android:state_focused="true" />
|
<item>
|
||||||
<item android:drawable="@color/pinned_bg_dark" />
|
<selector>
|
||||||
</selector>
|
<item android:drawable="@color/primary_alpha33" android:state_selected="true" />
|
||||||
|
<item android:drawable="@color/pinned_bg_dark" />
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:drawable="@drawable/play_icon"
|
android:drawable="@drawable/play_icon"
|
||||||
tools:targetApi="lollipop">
|
>
|
||||||
|
|
||||||
<target
|
<target
|
||||||
android:name="@string/play_icon_group_parts"
|
android:name="@string/play_icon_group_parts"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<item android:state_pressed="true" android:drawable="@color/touch_highlight" />
|
android:color="@color/touch_highlight">
|
||||||
<item android:state_focused="true" android:drawable="@color/delta_primary" />
|
<item
|
||||||
<item android:drawable="@android:color/transparent" />
|
android:id="@android:id/mask"
|
||||||
</selector>
|
android:drawable="@android:color/white" />
|
||||||
|
</ripple>
|
|
@ -1,6 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<!-- Like touch_highlight_background, but creates a stronger (i.e. better visible)
|
||||||
<item android:state_pressed="true" android:drawable="@color/touch_highlight_strong" />
|
visual feedback when the button is pressed. -->
|
||||||
<item android:state_focused="true" android:drawable="@color/delta_primary" />
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<item android:drawable="@android:color/transparent" />
|
android:color="@color/delta_primary">
|
||||||
</selector>
|
<item android:id="@android:id/mask" android:drawable="@android:color/white" />
|
||||||
|
<item>
|
||||||
|
<selector>
|
||||||
|
<item android:drawable="@color/touch_highlight_strong" android:state_pressed="true" />
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<color name="touch_highlight">#22000000</color>
|
|
||||||
</resources>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<style name="TextSecure.ScribbleTheme" parent="TextSecure.DarkNoActionBar">
|
|
||||||
<item name="android:windowNoTitle">true</item>
|
|
||||||
<item name="android:windowFullscreen">true</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
|
@ -36,7 +36,7 @@
|
||||||
<color name="conversation_compose_divider">#32000000</color>
|
<color name="conversation_compose_divider">#32000000</color>
|
||||||
|
|
||||||
<color name="action_mode_status_bar">@color/gray50</color>
|
<color name="action_mode_status_bar">@color/gray50</color>
|
||||||
<color name="touch_highlight">#400099cc</color>
|
<color name="touch_highlight">#22000000</color>
|
||||||
<color name="touch_highlight_strong">#5C4CB8DB</color>
|
<color name="touch_highlight_strong">#5C4CB8DB</color>
|
||||||
|
|
||||||
<color name="sticker_selected_color">#99ffffff</color>
|
<color name="sticker_selected_color">#99ffffff</color>
|
||||||
|
|
|
@ -256,6 +256,8 @@
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="TextSecure.ScribbleTheme" parent="TextSecure.DarkNoActionBar">
|
<style name="TextSecure.ScribbleTheme" parent="TextSecure.DarkNoActionBar">
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:windowFullscreen">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|