mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-14 19:59:05 +01:00
Port Signal's emoji implementation to Briar
Add functionality to save and restore recently used Emojis Update emoji and add new categories based on AOSP's XML file
This commit is contained in:
@@ -15,7 +15,6 @@ import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.format.DateUtils;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.URLSpan;
|
||||
@@ -23,6 +22,7 @@ import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.view.ArticleMovementMethod;
|
||||
import org.briarproject.android.widget.LinkDialogFragment;
|
||||
import org.briarproject.util.IoUtils;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.text.Layout;
|
||||
import android.text.Spannable;
|
||||
import android.text.method.ArrowKeyMovementMethod;
|
||||
import android.text.method.MovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class ArticleMovementMethod extends ArrowKeyMovementMethod {
|
||||
|
||||
private static ArticleMovementMethod sInstance;
|
||||
|
||||
public static MovementMethod getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ArticleMovementMethod();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(TextView widget, Spannable buffer,
|
||||
MotionEvent event) {
|
||||
int action = event.getAction();
|
||||
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
int x = (int) event.getX();
|
||||
int y = (int) event.getY();
|
||||
|
||||
x -= widget.getTotalPaddingLeft();
|
||||
y -= widget.getTotalPaddingTop();
|
||||
|
||||
x += widget.getScrollX();
|
||||
y += widget.getScrollY();
|
||||
|
||||
Layout layout = widget.getLayout();
|
||||
int line = layout.getLineForVertical(y);
|
||||
int off = layout.getOffsetForHorizontal(line, x);
|
||||
|
||||
ClickableSpan[] link =
|
||||
buffer.getSpans(off, off, ClickableSpan.class);
|
||||
|
||||
if (link.length != 0) {
|
||||
link[0].onClick(widget);
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(widget, buffer, event);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Typeface;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.blogs.BlogActivity;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.Author.Status;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
import im.delight.android.identicons.IdenticonDrawable;
|
||||
|
||||
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.graphics.Typeface.BOLD;
|
||||
import static android.graphics.Typeface.NORMAL;
|
||||
import static android.support.v4.app.ActivityOptionsCompat.makeCustomAnimation;
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_PX;
|
||||
import static org.briarproject.android.BriarActivity.GROUP_ID;
|
||||
import static org.briarproject.api.identity.Author.Status.OURSELVES;
|
||||
|
||||
public class AuthorView extends RelativeLayout {
|
||||
|
||||
private final CircleImageView avatar;
|
||||
private final ImageView avatarIcon;
|
||||
private final TextView authorName;
|
||||
private final Typeface authorNameTypeface;
|
||||
private final TextView date;
|
||||
private final TrustIndicatorView trustIndicator;
|
||||
|
||||
public AuthorView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.author_view, this, true);
|
||||
|
||||
avatar = (CircleImageView) findViewById(R.id.avatar);
|
||||
avatarIcon = (ImageView) findViewById(R.id.avatarIcon);
|
||||
authorName = (TextView) findViewById(R.id.authorName);
|
||||
authorNameTypeface = authorName.getTypeface();
|
||||
date = (TextView) findViewById(R.id.dateView);
|
||||
trustIndicator = (TrustIndicatorView) findViewById(R.id.trustIndicator);
|
||||
|
||||
TypedArray attributes =
|
||||
context.obtainStyledAttributes(attrs, R.styleable.AuthorView);
|
||||
int persona = attributes.getInteger(R.styleable.AuthorView_persona, 0);
|
||||
setPersona(persona);
|
||||
attributes.recycle();
|
||||
}
|
||||
|
||||
public AuthorView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public void setAuthor(Author author) {
|
||||
authorName.setText(author.getName());
|
||||
IdenticonDrawable d = new IdenticonDrawable(author.getId().getBytes());
|
||||
avatar.setImageDrawable(d);
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setAuthorStatus(Status status) {
|
||||
trustIndicator.setTrustLevel(status);
|
||||
if (status == OURSELVES) {
|
||||
authorName.setTypeface(authorNameTypeface, BOLD);
|
||||
} else {
|
||||
authorName.setTypeface(authorNameTypeface, NORMAL);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setDate(long date) {
|
||||
this.date.setText(AndroidUtils.formatDate(getContext(), date));
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setBlogLink(final GroupId groupId) {
|
||||
setClickable(true);
|
||||
TypedValue outValue = new TypedValue();
|
||||
getContext().getTheme().resolveAttribute(
|
||||
android.R.attr.selectableItemBackground, outValue, true);
|
||||
setBackgroundResource(outValue.resourceId);
|
||||
setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent i = new Intent(getContext(), BlogActivity.class);
|
||||
i.putExtra(GROUP_ID, groupId.getBytes());
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
ActivityOptionsCompat options =
|
||||
makeCustomAnimation(getContext(),
|
||||
android.R.anim.slide_in_left,
|
||||
android.R.anim.slide_out_right);
|
||||
Intent[] intents = {i};
|
||||
ContextCompat.startActivities(getContext(), intents,
|
||||
options.toBundle());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void unsetBlogLink() {
|
||||
setClickable(false);
|
||||
setBackgroundResource(android.R.color.transparent);
|
||||
setOnClickListener(null);
|
||||
}
|
||||
|
||||
private void setPersona(int persona) {
|
||||
switch (persona) {
|
||||
// reblogger
|
||||
case 1:
|
||||
avatarIcon.setVisibility(VISIBLE);
|
||||
break;
|
||||
// commenter
|
||||
case 2:
|
||||
ViewGroup.LayoutParams params = avatar.getLayoutParams();
|
||||
int size = getResources().getDimensionPixelSize(
|
||||
R.dimen.blogs_avatar_comment_size);
|
||||
params.height = size;
|
||||
params.width = size;
|
||||
avatar.setLayoutParams(params);
|
||||
float textSize = getResources()
|
||||
.getDimensionPixelSize(R.dimen.text_size_tiny);
|
||||
authorName.setTextSize(COMPLEX_UNIT_PX, textSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.RecyclerView.Adapter;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.briarproject.android.util.AndroidUtils.MIN_RESOLUTION;
|
||||
|
||||
public class BriarRecyclerView extends FrameLayout {
|
||||
|
||||
private static final long DEFAULT_REFRESH_INTERVAL = MIN_RESOLUTION;
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BriarRecyclerView.class.getName());
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private TextView emptyView;
|
||||
private ProgressBar progressBar;
|
||||
private RecyclerView.AdapterDataObserver emptyObserver;
|
||||
private Runnable refresher = null;
|
||||
private boolean isScrollingToEnd = false;
|
||||
|
||||
public BriarRecyclerView(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public BriarRecyclerView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BriarRecyclerView(Context context, AttributeSet attrs,
|
||||
int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray attributes = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.BriarRecyclerView);
|
||||
isScrollingToEnd = attributes
|
||||
.getBoolean(R.styleable.BriarRecyclerView_scrollToEnd, true);
|
||||
String emtpyText =
|
||||
attributes.getString(R.styleable.BriarRecyclerView_emptyText);
|
||||
if (emtpyText != null) setEmptyText(emtpyText);
|
||||
attributes.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
stopPeriodicUpdate();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
View v = LayoutInflater.from(getContext()).inflate(
|
||||
R.layout.briar_recycler_view, this, true);
|
||||
|
||||
recyclerView = (RecyclerView) v.findViewById(R.id.recyclerView);
|
||||
emptyView = (TextView) v.findViewById(R.id.emptyView);
|
||||
progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
|
||||
|
||||
showProgressBar();
|
||||
|
||||
// scroll down when opening keyboard
|
||||
if (isScrollingToEnd) {
|
||||
addLayoutChangeListener();
|
||||
}
|
||||
|
||||
emptyObserver = new RecyclerView.AdapterDataObserver() {
|
||||
@Override
|
||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||
super.onItemRangeInserted(positionStart, itemCount);
|
||||
if (itemCount > 0) showData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount);
|
||||
if (itemCount > 0) showData();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void addLayoutChangeListener() {
|
||||
recyclerView.addOnLayoutChangeListener(
|
||||
new OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View v, int left, int top,
|
||||
int right, int bottom, int oldLeft, int oldTop,
|
||||
int oldRight, int oldBottom) {
|
||||
if (bottom < oldBottom) {
|
||||
recyclerView.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
scrollToPosition(
|
||||
recyclerView.getAdapter()
|
||||
.getItemCount() - 1);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setLayoutManager(RecyclerView.LayoutManager layout) {
|
||||
if (recyclerView == null) initViews();
|
||||
recyclerView.setLayoutManager(layout);
|
||||
}
|
||||
|
||||
public void setAdapter(Adapter adapter) {
|
||||
if (recyclerView == null) initViews();
|
||||
|
||||
Adapter oldAdapter = recyclerView.getAdapter();
|
||||
if (oldAdapter != null) {
|
||||
oldAdapter.unregisterAdapterDataObserver(emptyObserver);
|
||||
}
|
||||
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
if (adapter != null) {
|
||||
adapter.registerAdapterDataObserver(emptyObserver);
|
||||
|
||||
if (adapter.getItemCount() > 0) {
|
||||
// only show data if adapter has data already
|
||||
// otherwise progress bar is shown
|
||||
emptyObserver.onChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setEmptyText(String text) {
|
||||
if (recyclerView == null) initViews();
|
||||
emptyView.setText(text);
|
||||
}
|
||||
|
||||
public void setEmptyText(int res) {
|
||||
if (recyclerView == null) initViews();
|
||||
emptyView.setText(res);
|
||||
}
|
||||
|
||||
public void showProgressBar() {
|
||||
if (recyclerView == null) initViews();
|
||||
recyclerView.setVisibility(INVISIBLE);
|
||||
emptyView.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
public void showData() {
|
||||
if (recyclerView == null) initViews();
|
||||
Adapter adapter = recyclerView.getAdapter();
|
||||
if (adapter != null) {
|
||||
if (adapter.getItemCount() == 0) {
|
||||
emptyView.setVisibility(VISIBLE);
|
||||
recyclerView.setVisibility(INVISIBLE);
|
||||
} else {
|
||||
// use GONE here so empty view doesn't use space on small lists
|
||||
emptyView.setVisibility(GONE);
|
||||
recyclerView.setVisibility(VISIBLE);
|
||||
}
|
||||
progressBar.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void scrollToPosition(int position) {
|
||||
if (recyclerView == null) initViews();
|
||||
recyclerView.scrollToPosition(position);
|
||||
}
|
||||
|
||||
public void smoothScrollToPosition(int position) {
|
||||
if (recyclerView == null) initViews();
|
||||
recyclerView.smoothScrollToPosition(position);
|
||||
}
|
||||
|
||||
public RecyclerView getRecyclerView() {
|
||||
return this.recyclerView;
|
||||
}
|
||||
|
||||
public void startPeriodicUpdate() {
|
||||
if (recyclerView == null || recyclerView.getAdapter() == null) {
|
||||
throw new IllegalStateException("Need to call setAdapter() first!");
|
||||
}
|
||||
refresher = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
LOG.info("Updating Content...");
|
||||
recyclerView.getAdapter().notifyDataSetChanged();
|
||||
postDelayed(refresher, DEFAULT_REFRESH_INTERVAL);
|
||||
}
|
||||
};
|
||||
LOG.info("Adding Handler Callback");
|
||||
postDelayed(refresher, DEFAULT_REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
public void stopPeriodicUpdate() {
|
||||
if (refresher != null) {
|
||||
LOG.info("Removing Handler Callback");
|
||||
removeCallbacks(refresher);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.design.widget.CoordinatorLayout;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
public class BriarRecyclerViewBehavior
|
||||
extends CoordinatorLayout.Behavior<BriarRecyclerView> {
|
||||
|
||||
public BriarRecyclerViewBehavior(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDependentViewChanged(CoordinatorLayout parent,
|
||||
BriarRecyclerView child, View dependency) {
|
||||
|
||||
// FIXME the below code works, but does not reset margin when snackbar is dismissed
|
||||
/*
|
||||
int margin = 0;
|
||||
if (dependency.isShown()) margin = dependency.getHeight();
|
||||
|
||||
// set snackbar height as bottom margin if it is shown
|
||||
CoordinatorLayout.LayoutParams params =
|
||||
(CoordinatorLayout.LayoutParams) child.getLayoutParams();
|
||||
params.setMargins(0, 0, 0, margin);
|
||||
child.setLayoutParams(params);
|
||||
|
||||
child.scrollToPosition(0);
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean layoutDependsOn(CoordinatorLayout parent,
|
||||
BriarRecyclerView child, View dependency) {
|
||||
// we only want to trigger the change
|
||||
// only when the changes is from a snackbar
|
||||
return dependency instanceof Snackbar.SnackbarLayout;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.Camera.AutoFocusCallback;
|
||||
import android.hardware.Camera.CameraInfo;
|
||||
import android.hardware.Camera.Parameters;
|
||||
import android.hardware.Camera.Size;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT;
|
||||
import static android.hardware.Camera.Parameters.FOCUS_MODE_AUTO;
|
||||
import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
|
||||
import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
|
||||
import static android.hardware.Camera.Parameters.FOCUS_MODE_EDOF;
|
||||
import static android.hardware.Camera.Parameters.FOCUS_MODE_FIXED;
|
||||
import static android.hardware.Camera.Parameters.FOCUS_MODE_MACRO;
|
||||
import static android.hardware.Camera.Parameters.SCENE_MODE_BARCODE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
||||
AutoFocusCallback {
|
||||
|
||||
private static final int AUTO_FOCUS_RETRY_DELAY = 5000; // Milliseconds
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(CameraView.class.getName());
|
||||
|
||||
private Camera camera = null;
|
||||
private PreviewConsumer previewConsumer = null;
|
||||
private int displayOrientation = 0, surfaceWidth = 0, surfaceHeight = 0;
|
||||
private boolean autoFocus = false, surfaceExists = false;
|
||||
|
||||
public CameraView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public CameraView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
setKeepScreenOn(true);
|
||||
SurfaceHolder holder = getHolder();
|
||||
holder.addCallback(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
setKeepScreenOn(false);
|
||||
getHolder().removeCallback(this);
|
||||
}
|
||||
|
||||
public void start(Camera camera, PreviewConsumer previewConsumer,
|
||||
int rotationDegrees) {
|
||||
this.camera = camera;
|
||||
this.previewConsumer = previewConsumer;
|
||||
setDisplayOrientation(rotationDegrees);
|
||||
Parameters params = camera.getParameters();
|
||||
setFocusMode(params);
|
||||
setPreviewSize(params);
|
||||
applyParameters(params);
|
||||
if (surfaceExists) startPreview(getHolder());
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopPreview();
|
||||
try {
|
||||
if (camera != null)
|
||||
camera.release();
|
||||
} catch (RuntimeException e) {
|
||||
LOG.log(WARNING, "Error releasing camera", e);
|
||||
}
|
||||
camera = null;
|
||||
}
|
||||
|
||||
private void startPreview(SurfaceHolder holder) {
|
||||
try {
|
||||
camera.setPreviewDisplay(holder);
|
||||
camera.startPreview();
|
||||
startConsumer();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
LOG.log(WARNING, "Error starting camera preview", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopPreview() {
|
||||
try {
|
||||
stopConsumer();
|
||||
if (camera != null)
|
||||
camera.stopPreview();
|
||||
} catch (RuntimeException e) {
|
||||
LOG.log(WARNING, "Error stopping camera preview", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void startConsumer() {
|
||||
if (autoFocus) camera.autoFocus(this);
|
||||
previewConsumer.start(camera);
|
||||
}
|
||||
|
||||
public void stopConsumer() {
|
||||
if (previewConsumer != null) {
|
||||
previewConsumer.stop();
|
||||
}
|
||||
if (autoFocus) camera.cancelAutoFocus();
|
||||
}
|
||||
|
||||
private void setDisplayOrientation(int rotationDegrees) {
|
||||
int orientation;
|
||||
CameraInfo info = new CameraInfo();
|
||||
Camera.getCameraInfo(0, info);
|
||||
if (info.facing == CAMERA_FACING_FRONT) {
|
||||
orientation = (info.orientation + rotationDegrees) % 360;
|
||||
orientation = (360 - orientation) % 360;
|
||||
} else {
|
||||
orientation = (info.orientation - rotationDegrees + 360) % 360;
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Display orientation " + orientation + " degrees");
|
||||
try {
|
||||
camera.setDisplayOrientation(orientation);
|
||||
} catch (RuntimeException e) {
|
||||
LOG.log(WARNING, "Error setting display orientation", e);
|
||||
}
|
||||
displayOrientation = orientation;
|
||||
}
|
||||
|
||||
private void setFocusMode(Parameters params) {
|
||||
if (Build.VERSION.SDK_INT >= 15 &&
|
||||
params.isVideoStabilizationSupported()) {
|
||||
LOG.info("Enabling video stabilisation");
|
||||
params.setVideoStabilization(true);
|
||||
}
|
||||
// This returns null on the HTC Wildfire S
|
||||
List<String> sceneModes = params.getSupportedSceneModes();
|
||||
if (sceneModes == null) sceneModes = Collections.emptyList();
|
||||
List<String> focusModes = params.getSupportedFocusModes();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Scene modes: " + sceneModes);
|
||||
LOG.info("Focus modes: " + focusModes);
|
||||
}
|
||||
if (sceneModes.contains(SCENE_MODE_BARCODE)) {
|
||||
LOG.info("Setting scene mode to barcode");
|
||||
params.setSceneMode(SCENE_MODE_BARCODE);
|
||||
}
|
||||
if (focusModes.contains(FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
||||
LOG.info("Setting focus mode to continuous picture");
|
||||
params.setFocusMode(FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||
} else if (focusModes.contains(FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
||||
LOG.info("Setting focus mode to continuous video");
|
||||
params.setFocusMode(FOCUS_MODE_CONTINUOUS_VIDEO);
|
||||
} else if (focusModes.contains(FOCUS_MODE_EDOF)) {
|
||||
LOG.info("Setting focus mode to EDOF");
|
||||
params.setFocusMode(FOCUS_MODE_EDOF);
|
||||
} else if (focusModes.contains(FOCUS_MODE_MACRO)) {
|
||||
LOG.info("Setting focus mode to macro");
|
||||
params.setFocusMode(FOCUS_MODE_MACRO);
|
||||
autoFocus = true;
|
||||
} else if (focusModes.contains(FOCUS_MODE_AUTO)) {
|
||||
LOG.info("Setting focus mode to auto");
|
||||
params.setFocusMode(FOCUS_MODE_AUTO);
|
||||
autoFocus = true;
|
||||
} else if (focusModes.contains(FOCUS_MODE_FIXED)) {
|
||||
LOG.info("Setting focus mode to fixed");
|
||||
params.setFocusMode(FOCUS_MODE_FIXED);
|
||||
} else {
|
||||
LOG.info("No suitable focus mode");
|
||||
}
|
||||
params.setZoom(0);
|
||||
}
|
||||
|
||||
private void setPreviewSize(Parameters params) {
|
||||
if (surfaceWidth == 0 || surfaceHeight == 0) return;
|
||||
float idealRatio = (float) surfaceWidth / surfaceHeight;
|
||||
DisplayMetrics screen = getContext().getResources().getDisplayMetrics();
|
||||
int screenMax = Math.max(screen.widthPixels, screen.heightPixels);
|
||||
boolean rotatePreview = displayOrientation % 180 == 90;
|
||||
List<Size> sizes = params.getSupportedPreviewSizes();
|
||||
Size bestSize = null;
|
||||
float bestScore = 0;
|
||||
for (Size size : sizes) {
|
||||
int width = rotatePreview ? size.height : size.width;
|
||||
int height = rotatePreview ? size.width : size.height;
|
||||
float ratio = (float) width / height;
|
||||
float stretch = Math.max(ratio / idealRatio, idealRatio / ratio);
|
||||
int pixels = width * height;
|
||||
float score = width * height / stretch;
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Size " + size.width + "x" + size.height
|
||||
+ ", stretch " + stretch + ", pixels " + pixels
|
||||
+ ", score " + score);
|
||||
}
|
||||
if (bestSize == null || score > bestScore) {
|
||||
bestSize = size;
|
||||
bestScore = score;
|
||||
}
|
||||
}
|
||||
if (bestSize != null) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Best size " + bestSize.width + "x" + bestSize.height);
|
||||
params.setPreviewSize(bestSize.width, bestSize.height);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyParameters(Parameters params) {
|
||||
try {
|
||||
camera.setParameters(params);
|
||||
} catch (RuntimeException e) {
|
||||
LOG.log(WARNING, "Error setting camera parameters", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
LOG.info("Surface created");
|
||||
surfaceExists = true;
|
||||
if (camera != null) startPreview(holder);
|
||||
}
|
||||
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
|
||||
surfaceWidth = w;
|
||||
surfaceHeight = h;
|
||||
if (camera == null) return; // We are stopped
|
||||
stopPreview();
|
||||
try {
|
||||
Parameters params = camera.getParameters();
|
||||
setPreviewSize(params);
|
||||
applyParameters(params);
|
||||
} catch (RuntimeException e) {
|
||||
LOG.log(WARNING, "Error getting camera parameters", e);
|
||||
}
|
||||
startPreview(holder);
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
LOG.info("Surface destroyed");
|
||||
surfaceExists = false;
|
||||
}
|
||||
|
||||
public void onAutoFocus(boolean success, final Camera camera) {
|
||||
LOG.info("Auto focus succeeded: " + success);
|
||||
postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
retryAutoFocus();
|
||||
}
|
||||
}, AUTO_FOCUS_RETRY_DELAY);
|
||||
}
|
||||
|
||||
private void retryAutoFocus() {
|
||||
try {
|
||||
if (camera != null) camera.autoFocus(this);
|
||||
} catch (RuntimeException e) {
|
||||
LOG.log(WARNING, "Error retrying auto focus", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.identity.Author;
|
||||
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
import im.delight.android.identicons.IdenticonDrawable;
|
||||
|
||||
public class TextAvatarView extends FrameLayout {
|
||||
|
||||
final private AppCompatTextView character;
|
||||
final private CircleImageView background;
|
||||
final private TextView badge;
|
||||
private int unreadCount;
|
||||
|
||||
public TextAvatarView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater
|
||||
.inflate(R.layout.text_avatar_view, this, true);
|
||||
character = (AppCompatTextView) findViewById(R.id.textAvatarView);
|
||||
background = (CircleImageView) findViewById(R.id.avatarBackground);
|
||||
badge = (TextView) findViewById(R.id.unreadCountView);
|
||||
badge.setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
public TextAvatarView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
character.setText(text.toUpperCase());
|
||||
}
|
||||
|
||||
public void setUnreadCount(int count) {
|
||||
if (count > 0) {
|
||||
this.unreadCount = count;
|
||||
badge.setBackgroundResource(R.drawable.bubble);
|
||||
badge.setText(String.valueOf(count));
|
||||
badge.setTextColor(ContextCompat.getColor(getContext(), R.color.briar_text_primary_inverse));
|
||||
badge.setVisibility(VISIBLE);
|
||||
} else {
|
||||
badge.setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setProblem(boolean problem) {
|
||||
if (problem) {
|
||||
badge.setBackgroundResource(R.drawable.bubble_problem);
|
||||
badge.setText("!");
|
||||
badge.setTextColor(ContextCompat.getColor(getContext(), R.color.briar_primary));
|
||||
badge.setVisibility(VISIBLE);
|
||||
} else if (unreadCount > 0) {
|
||||
setUnreadCount(unreadCount);
|
||||
} else {
|
||||
badge.setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBackgroundBytes(byte[] bytes) {
|
||||
int r = getByte(bytes, 0) * 3 / 4 + 96;
|
||||
int g = getByte(bytes, 1) * 3 / 4 + 96;
|
||||
int b = getByte(bytes, 2) * 3 / 4 + 96;
|
||||
int color = Color.rgb(r, g, b);
|
||||
|
||||
background.setFillColor(color);
|
||||
}
|
||||
|
||||
private byte getByte(byte[] bytes, int index) {
|
||||
if (bytes == null) {
|
||||
return -128;
|
||||
} else {
|
||||
return bytes[index % bytes.length];
|
||||
}
|
||||
}
|
||||
|
||||
public void setAuthorAvatar(Author author) {
|
||||
Drawable drawable = new IdenticonDrawable(author.getId().getBytes());
|
||||
background.setImageDrawable(drawable);
|
||||
character.setVisibility(GONE);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package org.briarproject.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.identity.Author.Status;
|
||||
|
||||
import static org.briarproject.api.identity.Author.Status.OURSELVES;
|
||||
|
||||
public class TrustIndicatorView extends ImageView {
|
||||
|
||||
public TrustIndicatorView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public TrustIndicatorView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public TrustIndicatorView(Context context, AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setTrustLevel(Status status) {
|
||||
int res;
|
||||
switch (status) {
|
||||
case ANONYMOUS:
|
||||
res = R.drawable.trust_indicator_anonymous;
|
||||
break;
|
||||
case UNVERIFIED:
|
||||
res = R.drawable.trust_indicator_unverified;
|
||||
break;
|
||||
case VERIFIED:
|
||||
res = R.drawable.trust_indicator_verified;
|
||||
break;
|
||||
case OURSELVES:
|
||||
res = R.drawable.ic_our_identity_black;
|
||||
break;
|
||||
default:
|
||||
res = R.drawable.trust_indicator_unknown;
|
||||
}
|
||||
setImageDrawable(ContextCompat.getDrawable(getContext(), res));
|
||||
setVisibility(VISIBLE);
|
||||
|
||||
invalidate();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user