/*
* Artcodes recognises a different marker scheme that allows the
* creation of aesthetically pleasing, even beautiful, codes.
* Copyright (C) 2013-2016 The University of Nottingham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package uk.ac.horizon.artcodes.scanner;
import android.Manifest;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import java.util.List;
import uk.ac.horizon.artcodes.animator.TextAnimator;
import uk.ac.horizon.artcodes.animator.VisibilityAnimator;
import uk.ac.horizon.artcodes.camera.CameraView;
import uk.ac.horizon.artcodes.detect.ArtcodeDetector;
import uk.ac.horizon.artcodes.detect.DetectorCallback;
import uk.ac.horizon.artcodes.detect.DetectorSetting;
import uk.ac.horizon.artcodes.detect.handler.CodeDetectionHandler;
import uk.ac.horizon.artcodes.detect.handler.MarkerCodeDetectionHandler;
import uk.ac.horizon.artcodes.model.Experience;
public class ScannerActivity extends AppCompatActivity
{
private static final int CAMERA_PERMISSION_REQUEST = 47;
private LinearLayout settingIcons;
protected ProgressBar progressBar;
private ArtcodeDetector detector;
private Experience experience;
private VisibilityAnimator menuAnimator;
private TextAnimator textAnimator;
private CameraView cameraView;
@SuppressWarnings("UnusedParameters")
public void hideMenu(View view)
{
menuAnimator.hideView();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void hideSystemUI()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
hideSystemUI();
setContentView(R.layout.scanner);
settingIcons = (LinearLayout) findViewById(R.id.settingsSwitches);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
cameraView = (CameraView) findViewById(R.id.cameraView);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null)
{
getSupportActionBar().setDisplayShowTitleEnabled(false);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST);
}
menuAnimator = new VisibilityAnimator(findViewById(R.id.settingsMenu), findViewById(R.id.settingsMenuButton));
TextView settingsFeedback = (TextView) findViewById(R.id.settingsFeedback);
textAnimator = new TextAnimator(settingsFeedback);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
{
switch (requestCode)
{
case CAMERA_PERMISSION_REQUEST:
{
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
startScanning();
}
else
{
Log.i("a", "Permission not granted");
// TODO
}
}
}
}
public void loaded(Experience experience)
{
this.experience = experience;
startScanning();
}
@SuppressWarnings("UnusedParameters")
public void showMenu(View view)
{
createSettingsUI(detector.getSettings());
menuAnimator.showView();
}
protected Experience getExperience()
{
return experience;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
if (item.getItemId() == android.R.id.home)
{
if (getCallingActivity() != null)
{
setResult(RESULT_CANCELED);
finish();
return true;
}
}
return super.onOptionsItemSelected(item);
}
private void startScanning()
{
if (experience != null)
{
Log.i("a", "Start Scanning");
detector = this.getNewDetector(experience);
detector.setCallback(new DetectorCallback()
{
@Override
public void detectionStart(final int margin)
{
runOnUiThread(new Runnable()
{
@Override
public void run()
{
View errorView = findViewById(R.id.cameraError);
if (errorView != null)
{
errorView.setVisibility(View.GONE);
}
progressBar.setVisibility(View.VISIBLE);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
View topView = findViewById(R.id.topView);
if (topView != null)
{
topView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, margin));
}
View bottomView = findViewById(R.id.bottomView);
if (bottomView != null)
{
bottomView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, margin));
}
}
else
{
View topView = findViewById(R.id.topView);
if (topView != null)
{
topView.setLayoutParams(new LinearLayout.LayoutParams(margin, ViewGroup.LayoutParams.MATCH_PARENT));
}
View bottomView = findViewById(R.id.bottomView);
if (bottomView != null)
{
bottomView.setLayoutParams(new LinearLayout.LayoutParams(margin, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
}
});
}
});
detector.setOverlay((ImageView) findViewById(R.id.overlay));
if (getSupportActionBar() != null)
{
getSupportActionBar().setDisplayShowTitleEnabled(true);
getSupportActionBar().setTitle(experience.getName());
}
cameraView.setDetector(detector, experience);
if (cameraView.deviceNeedsTapToFocus())
{
// tap to focus
View v = findViewById(R.id.thumbnailImageLayout);
if (v != null)
{
v.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View view)
{
setFocusTextVisible(false);
cameraView.focus(new Runnable()
{
@Override
public void run()
{
setFocusTextVisible(true);
}
});
}
});
}
setFocusTextVisible(true);
}
}
else
{
cameraView.setDetector(null, null);
}
}
private boolean focusTextAlreadyShown = false;
private void setFocusTextVisible(final boolean visible)
{
final TextView focusTextView = (TextView) findViewById(R.id.focusText);
runOnUiThread(new Runnable()
{
@Override
public void run()
{
if (focusTextView != null)
{
if (focusTextAlreadyShown)
{
focusTextView.setText(R.string.tap_to_refocus);
focusTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
focusTextView.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
}
if (visible)
{
focusTextView.setVisibility(View.VISIBLE);
}
else
{
focusTextView.setVisibility(View.INVISIBLE);
focusTextAlreadyShown = true;
}
}
}
});
}
private void onCodeDetected(String markerCode)
{
Log.i("Marker", "MarkerDisplay Detected: " + markerCode);
if (markerCode != null)
{
if (experience.getCallback() != null)
{
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(experience.getCallback().replace("{code}", markerCode))));
}
else
{
Intent intent = getIntent();
intent.putExtra("marker", markerCode);
setResult(RESULT_OK, intent);
finish();
}
}
}
protected void loadExperience(Bundle savedInstanceState)
{
if (savedInstanceState != null && savedInstanceState.containsKey("experience"))
{
loaded(new Gson().fromJson(savedInstanceState.getString("experience"), Experience.class));
}
else
{
Intent intent = getIntent();
if (intent.hasExtra("experience"))
{
loaded(new Gson().fromJson(intent.getStringExtra("experience"), Experience.class));
}
}
}
@Override
protected void onPostCreate(Bundle savedInstanceState)
{
super.onPostCreate(savedInstanceState);
loadExperience(savedInstanceState);
}
private Drawable getTintedDrawable(@DrawableRes int drawable, @ColorInt int color)
{
final Drawable original = ContextCompat.getDrawable(this, drawable);
if (original != null)
{
final Drawable wrapDrawable = DrawableCompat.wrap(original);
DrawableCompat.setTint(wrapDrawable, color);
return wrapDrawable;
}
return null;
}
private void createSettingsUI(List<DetectorSetting> settings)
{
settingIcons.removeAllViews();
if (settings != null && !settings.isEmpty())
{
final int padding = getResources().getDimensionPixelSize(R.dimen.setting_padding);
List<DetectorSetting> settingList = settings;
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{
settingList = Lists.reverse(settings);
}
for (final DetectorSetting setting : settingList)
{
final ImageView button = new ImageView(this);
button.setContentDescription(getString(setting.getText()));
final int[] attrs = new int[]{android.R.attr.selectableItemBackground};
final TypedArray ta = obtainStyledAttributes(attrs);
final Drawable drawableFromTheme = ta.getDrawable(0);
ta.recycle();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
button.setImageResource(setting.getIcon());
button.setImageTintList(ColorStateList.valueOf(Color.WHITE));
button.setBackground(drawableFromTheme);
}
else
{
button.setImageDrawable(getTintedDrawable(setting.getIcon(), Color.WHITE));
//noinspection deprecation
button.setBackgroundDrawable(drawableFromTheme);
}
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
setting.nextValue();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
button.setImageResource(setting.getIcon());
}
else
{
button.setImageDrawable(getTintedDrawable(setting.getIcon(), Color.WHITE));
}
button.setContentDescription(getString(setting.getText()));
textAnimator.setText(setting.getText());
}
});
final LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, 1);
params.weight = 1;
button.setLayoutParams(params);
settingIcons.addView(button);
button.setPadding(padding, padding, padding, padding);
}
}
menuAnimator.setViewVisible(settingIcons.getChildCount() > 0);
}
protected ArtcodeDetector getNewDetector(Experience experience)
{
return new ArtcodeDetector(this, experience, new MarkerCodeDetectionHandler(this.experience, new CodeDetectionHandler()
{
@Override
public void onMarkerCodeDetected(String code)
{
onCodeDetected(code);
}
}));
}
}