Commit b6da758b authored by jiangjiantao's avatar jiangjiantao

新版banner

parent 8552baff
......@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.android.tools.build:gradle:3.5.1'
// NOTE: Do not place your application dependencies here; they belong
......
apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 22
minSdkVersion 18
targetSdkVersion 22
versionCode 1
versionName "1.0"
......@@ -21,17 +21,15 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'com.github.bumptech.glide:glide:3.7.0'
//当需要FFmpegMediaMetadataRetriever时必选
implementation 'com.github.wseemann:FFmpegMediaMetadataRetriever-armeabi-v7a:1.0.14'
implementation 'com.wang.avi:library:2.1.3'
// implementation 'com.google.android.exoplayer:exoplayer:2.13.3'
//视频播放组件
api 'com.google.android.exoplayer:exoplayer-core:2.7.2'
api 'com.google.android.exoplayer:exoplayer-dash:2.7.2'
api 'com.google.android.exoplayer:exoplayer-ui:2.7.2'
api 'jp.wasabeef:glide-transformations:4.0.0'
implementation ('com.shuyu:GSYVideoPlayer:6.0.1') {
exclude module: 'support-v4'
exclude group: 'com.android.support'
}
// api 'jp.wasabeef:glide-transformations:4.0.0'
api 'com.alibaba:fastjson:1.2.8'
api 'com.blankj:utilcode:1.30.0'
}
......@@ -8,7 +8,6 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".BaseApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
......
......@@ -12,7 +12,6 @@ import java.util.List;
public class DetailActivity extends AppCompatActivity {
private List<MediaBean> list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
......
......@@ -29,31 +29,25 @@ public class MainActivity extends AppCompatActivity {
MediaBean mediaBean = new MediaBean();
mediaBean.setUrl("https://hh-oss-html.miyapay.com/hhops/picture/16045733014903c31992a674f.png");
mediaBean.setResType(TYPE_IMAGE);
mediaBean.setType(TYPE_IMAGE);
list.add(mediaBean);
MediaBean mediaBean2 = new MediaBean();
mediaBean2.setUrl("https://hh-oss-html.miyapay.com/hhops/picture/1611112341654cffdd7b004bb.png");
mediaBean2.setResType(TYPE_IMAGE);
mediaBean2.setType(TYPE_IMAGE);
list.add(mediaBean2);
// String basePath = "http://miya-hz.oss-cn-shanghai.aliyuncs.com/huihua-test/face-pay/archive/";
String basePath = Environment.getExternalStorageDirectory().getPath()+"/";
String basePath = Environment.getExternalStorageDirectory().getPath()+"/";
MediaBean mediaBean3 = new MediaBean();
mediaBean3.setUrl(basePath+"shu.mp4");
mediaBean3.setCover(basePath+"shu.jpeg");
mediaBean3.setWidth(1080);
mediaBean3.setHeight(1440);
mediaBean3.setResType(TYPE_VIDEO);
mediaBean3.setType(TYPE_VIDEO);
list.add(mediaBean3);
MediaBean mediaBean4 = new MediaBean();
mediaBean4.setUrl(basePath+"heng.mp4");
mediaBean4.setCover(basePath+"heng.jpeg");
mediaBean4.setWidth(1440);
mediaBean4.setHeight(1080);
mediaBean4.setResType(TYPE_VIDEO);
mediaBean4.setType(TYPE_VIDEO);
list.add(mediaBean4);
banner.bindData(list);
......
package com.widget.imagevideobanner.bean;
import android.support.annotation.Nullable;
import android.content.Context;
import android.text.TextUtils;
import com.widget.imagevideobanner.R;
import com.widget.imagevideobanner.utils.ResourceUtils;
import java.io.Serializable;
/**
......@@ -15,12 +18,24 @@ public class MediaBean implements Serializable {
private String url;
//资源类型
private int resType;
private int type;
//视频的封面
private String cover;
private int width;
private int height;
private boolean looping;
public MediaBean() { }
public MediaBean(String url, int type, String cover, int width, int height) {
this.url = url;
this.type = type;
this.cover = cover;
this.width = width;
this.height = height;
}
public String getUrl() {
return url;
......@@ -30,12 +45,12 @@ public class MediaBean implements Serializable {
this.url = url;
}
public int getResType() {
return resType;
public int getType() {
return type;
}
public void setResType(int resType) {
this.resType = resType;
public void setType(int type) {
this.type = type;
}
public String getCover() {
......@@ -61,27 +76,40 @@ public class MediaBean implements Serializable {
public void setHeight(int height) {
this.height = height;
}
public boolean isLooping() {
return looping;
}
public void setLooping(boolean looping) {
this.looping = looping;
}
/**
* 默认资源
* @param context
* @return
*/
public static MediaBean getDefaultMedia(Context context){
MediaBean mediaBean = new MediaBean();
mediaBean.setType(TYPE_IMAGE);
mediaBean.setUrl(ResourceUtils.imageTranslateUri(context, R.drawable.guide_defaut));
mediaBean.setWidth(1080);
mediaBean.setHeight(1186);
return mediaBean;
}
@Override
public boolean equals(@Nullable Object obj) {
public boolean equals( Object obj) {
if (!(obj instanceof MediaBean))
return false;
MediaBean mediaBean = (MediaBean) obj;
return resType == mediaBean.resType
return type == mediaBean.type
&& width == mediaBean.width
&& height == mediaBean.height
&& TextUtils.equals(url, mediaBean.url)
&& TextUtils.equals(cover, mediaBean.cover);
}
public boolean isNull() {
if (resType == TYPE_IMAGE) {
return TextUtils.isEmpty(url) ;
}
return (width == 0) || (height == 0)
|| TextUtils.isEmpty(cover)
|| TextUtils.isEmpty(url) ;
}
}
......@@ -11,7 +11,7 @@ import android.util.LruCache;
import com.widget.imagevideobanner.BuildConfig;
import com.widget.imagevideobanner.cache.disk.DiskLruCache;
import com.widget.imagevideobanner.log.BannerLogFileUtils;
import com.widget.imagevideobanner.log.BLogUtil;
import com.widget.imagevideobanner.utils.BitmapUtils;
import com.widget.imagevideobanner.utils.MD5Util;
import com.widget.imagevideobanner.utils.ThrowableUtils;
......@@ -80,12 +80,12 @@ public class BitmapCache {
size = bitmap.getAllocationByteCount() / 1024;
}
Log.e(TAG,"已经加入内存缓存 size =" +size);
BannerLogFileUtils.writeLog("已经加入内存缓存 size =" +size);
BLogUtil.log("已经加入内存缓存 size =" +size);
return size;
}
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
BannerLogFileUtils.writeLog("内存缓存移除 key =" +key);
BLogUtil.log("内存缓存移除 key =" +key);
Log.e(TAG,"内存缓存移除 key =" +key);
//todo bitmap复用
oldValue.recycle();
......@@ -173,7 +173,7 @@ public class BitmapCache {
putBitmap2Disk(blurkey, blurBitmap);
}catch (Exception e){
e.printStackTrace();
BannerLogFileUtils.writeLog("截取视频首帧出错 url ="+url + "出错原因是:"+ ThrowableUtils.getFullStackTrace(e));
BLogUtil.log("截取视频首帧出错 url ="+url + "出错原因是:"+ ThrowableUtils.getFullStackTrace(e));
}
}
});
......
package com.widget.imagevideobanner.log;
import com.blankj.utilcode.util.LogUtils;
import com.widget.imagevideobanner.utils.DateUtils;
import java.util.Date;
public class BLogUtil {
private static LogWriteListener mLogWriteListener;
public static void setBLogWriteListener(LogWriteListener bLogWriteListener) {
mLogWriteListener = bLogWriteListener;
}
public static void log(final String input) {
LogUtils.e(input);
if (null != mLogWriteListener) {
mLogWriteListener.onLogWriteListener(DateUtils.format18(new Date()) + input + "\n");
}
}
public interface LogWriteListener {
void onLogWriteListener(String msg);
}
}
package com.widget.imagevideobanner.log;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BannerLogFileUtils {
/**
* 日志打印
*/
public class FileUtils {
private static ExecutorService sExecutor;
private static SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
private static final String BANNER_LOG_DIR = Environment.getExternalStorageDirectory().getPath()
+ File.separator
+ "miyaterminal"
+ File.separator
+ "log"
+ File.separator
+ "bannererr.log";
@SuppressLint("SimpleDateFormat")
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
static {
File logFiles = new File(BANNER_LOG_DIR);
if (!logFiles.exists()) {
logFiles.mkdirs();
}
}
public static void writeLog(final String input) {
if (sExecutor == null) {
sExecutor = Executors.newSingleThreadExecutor();
}
sExecutor.execute(new Runnable() {
@Override
public void run() {
writeFileLog(input);
}
});
}
private static void writeFileLog(String input) {
public static void writeFileLog(String filePath, String input) {
if (TextUtils.isEmpty(input)) {
return;
}
......@@ -53,10 +32,10 @@ public class BannerLogFileUtils {
FileOutputStream fos = null;
OutputStreamWriter writer = null;
try {
File logFile = new File(BANNER_LOG_DIR);
File logFile = new File(filePath);
if (!logFile.exists() && !logFile.createNewFile()) return;
fos = new FileOutputStream(logFile, true);
writer = new OutputStreamWriter(fos, Charset.forName("utf-8"));
writer = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
writer.write(input);
writer.flush();
writer.close();
......@@ -80,8 +59,24 @@ public class BannerLogFileUtils {
}
}
public static File getDownloadDir(Context context) {
File downloadDir = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if( context.getExternalCacheDir() != null){
downloadDir = new File(context.getExternalCacheDir(), "VIDEO-AD");
}
} else {
if( context.getCacheDir() != null){
downloadDir = new File(context.getCacheDir(), "VIDEO-AD");
}
}
if(downloadDir == null){
downloadDir= new File( Environment.getExternalStorageDirectory().getAbsolutePath(),"VIDEO-AD");
}
if (!downloadDir.exists()) {
downloadDir.mkdirs();
}
return downloadDir;
}
}
package com.widget.imagevideobanner.player;
import android.app.Application;
import android.net.Uri;
import android.util.Log;
import android.view.LayoutInflater;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.upstream.FileDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.Cache;
import com.google.android.exoplayer2.upstream.cache.CacheDataSinkFactory;
import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
import com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory;
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.android.exoplayer2.util.Util;
import com.widget.imagevideobanner.BaseApplication;
import com.widget.imagevideobanner.R;
import java.io.File;
import java.util.HashMap;
public class PlayerManager {
private ExtractorMediaSource.Factory networkMediaSourceFactory;
private ExtractorMediaSource.Factory localMediaSourceFactory;
public SimpleExoPlayer exoPlayer;
public PlayerView playerView;
public String playUrl;
private static PlayerManager mInstance;
private HashMap<String,MediaSource> mediaSourceHashMap = new HashMap<>();
public PlayerManager() {
Application application = BaseApplication.getApplication();
buildLocalMediaSourceFactory(application);
buildNetworkMediaSourceFactory(application);
buildPlayerAndView(application);
}
private void buildPlayerAndView(Application application){
//创建exoplayer播放器实例
exoPlayer = ExoPlayerFactory.newSimpleInstance(application,
//视频的音视频轨道如何加载,使用默认的轨道选择器
new DefaultTrackSelector(),
//视频缓存控制逻辑,使用默认的即可
new DefaultLoadControl());
//加载咱们布局层级优化之后的能够展示视频画面的View
playerView = (PlayerView) LayoutInflater.from(application).inflate(R.layout.layout_exo_player_view, null, false);
//别忘记 把播放器实例 和 playerView,controlView相关联
//如此视频画面才能正常显示,播放进度条才能自动更新
playerView.setPlayer(exoPlayer);
}
private void buildNetworkMediaSourceFactory(Application application){
DefaultHttpDataSourceFactory dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(application, application.getPackageName())); //创建缓存,指定缓存位置,和缓存策略,为最近最少使用原则,最大为200m
Cache cache = new SimpleCache(application.getCacheDir(), new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 200));
//把缓存对象cache和负责缓存数据读取、写入的工厂类CacheDataSinkFactory 相关联
CacheDataSinkFactory cacheDataSinkFactory = new CacheDataSinkFactory(cache, Long.MAX_VALUE);
CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(cache,
dataSourceFactory,
new FileDataSourceFactory(),
cacheDataSinkFactory,
CacheDataSource.FLAG_BLOCK_ON_CACHE,
null);
networkMediaSourceFactory = new ExtractorMediaSource.Factory(cacheDataSourceFactory);
}
private void buildLocalMediaSourceFactory(Application application){
FileDataSourceFactory fileDataSourceFactory = new FileDataSourceFactory();
//创建缓存,指定缓存位置,和缓存策略,为最近最少使用原则,最大为200m
Cache cache = new SimpleCache(application.getCacheDir(), new LeastRecentlyUsedCacheEvictor(1024 * 1024 * 200));
//把缓存对象cache和负责缓存数据读取、写入的工厂类CacheDataSinkFactory 相关联
CacheDataSinkFactory cacheDataSinkFactory = new CacheDataSinkFactory(cache, Long.MAX_VALUE);
CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(cache,
fileDataSourceFactory,
new FileDataSourceFactory(),
cacheDataSinkFactory,
CacheDataSource.FLAG_BLOCK_ON_CACHE,
new CacheDataSource.EventListener() {
@Override
public void onCachedBytesRead(long cacheSizeBytes, long cachedBytesRead) {
Log.e("###视频缓存 ","cachedBytesRead ="+cachedBytesRead + "cacheSizeBytes = "+cacheSizeBytes);
}
});
localMediaSourceFactory = new ExtractorMediaSource.Factory(cacheDataSourceFactory);
}
public static PlayerManager getInstance(){
if (mInstance == null) {
synchronized (PlayerManager.class) {
if (mInstance == null) {
mInstance = new PlayerManager();
}
}
}
return mInstance;
}
/**
* 切换与播放器exoplayer 绑定的exoplayerView。用于页面切换视频无缝续播的场景
* @param newPlayerView
* @param attach
*/
public void switchPlayerView(PlayerView newPlayerView, boolean attach) {
playerView.setPlayer(attach ? null : exoPlayer);
newPlayerView.setPlayer(attach ? exoPlayer : null);
}
public MediaSource createMediaSource(String url) {
if(mediaSourceHashMap.containsKey(url)){
return mediaSourceHashMap.get(url);
}
MediaSource mediaSource = null;
if(url.contains("http")){
return networkMediaSourceFactory.createMediaSource(Uri.parse(url));
}else{
mediaSource = localMediaSourceFactory.createMediaSource(Uri.fromFile(new File(url)));
}
mediaSourceHashMap.put(url,mediaSource);
return mediaSource;
}
}
\ No newline at end of file
package com.widget.imagevideobanner.utils;
import com.blankj.utilcode.util.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 日期相关工具类
*/
public class DateUtils {
public static final String DF_YYYYMMDD = "yyyyMMdd";
public static final String DF_YYMMDD = "yyMMdd";
public static final String DF_YYYY_MM_DD = "yyyy-MM-dd";
public static final String DF_YYYY_MM_DD2 = "yyyy/MM/dd";
public static final String DF_YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
public static final String DF_YYYY_MM_DDHHMMSS = "yyyy-MM-dd HH:mm:ss";
public static final String DF_HHMMSS = "HHmmss";
public static final String DF_HHMMSS2 = "HH:mm:ss";
public static final String DF_CN_YYYY_MM_DD = "yyyy年MM月dd日";
public static final String DF_HHMM = "HH:mm";
/**
* 带ms的时间戳,与poshub对接时添加
*/
public static final String DF_YYYY_MM_DD_HHMMSSSS = "yyyy-MM-dd HH:mm:ss.SSS";
private static final String TAG = "DateUtils";
/**
* 将日期转换成yyyy-MM-dd HH:mm:ss.SSS字符串
*
* @return yyyy-MM-dd HH:mm:ss.SSS
*/
public static String format17(Date date) {
return format(date, DF_YYYY_MM_DD_HHMMSSSS);
}
/**
* 将日期转换成字符串
*
* @param date
* @param format
* @return
*/
public static String format(Date date, String format) {
if (date == null) {
throw new IllegalArgumentException("Param date is null!");
}
if (StringUtils.isEmpty(format)) {
throw new IllegalArgumentException("Param format is blank!");
}
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
}
/**
* 将日期转换成yyyyMMddHHmmss字符串
*
* @param date
* @return
*/
public static String format14(Date date) {
return format(date, DF_YYYYMMDDHHMMSS);
}
/**
* 将日期转换成yyyy-MM-dd- HH:mm:ss字符串
*
* @param date
* @return
*/
public static String format18(Date date) {
return format(date, DF_YYYY_MM_DDHHMMSS);
}
public static String format8(Date date) {
return format(date, DF_YYYYMMDD);
}
public static String format6(Date date) {
return format(date, DF_YYMMDD);
}
public static String format10(Date date) {
return format(date, DF_YYYY_MM_DD);
}
public static String formatCN(Date date) {
return format(date, DF_CN_YYYY_MM_DD);
}
public static String formatCustom(Date date, String style) {
return format(date, style);
}
public static Date parse8(String dateStr) throws ParseException {
return parse(dateStr, DF_YYYYMMDD);
}
public static Date parse10(String dateStr) throws ParseException {
return parse(dateStr, DF_YYYY_MM_DD);
}
/**
* 返回当前日期 yyyyMMdd格式 字符串
*
* @return
*/
public static String getNow8() {
return format8(new Date());
}
/**
* 返回当前日期 yyyyMMdd格式 字符串
*
* @return
*/
public static String getNow6() {
return format6(new Date());
}
/**
* 返回当前日期 yyyyMMdd格式 字符串
*
* @return
*/
public static String getNow10() {
return format10(new Date());
}
/**
* 获取当天0点 date对象
*
* @return
*/
public static Date getToday() {
Date today = null;
try {
today = parse(getNow8(), DF_YYYYMMDD);
} catch (ParseException e) {
today = new Date();
}
return today;
}
/**
* 根据时间的字符串转成Date对象
* yyyy-MM-dd HH:mm:ss
*
* @return
*/
public static Date getDateByDateString(String dateStr) {
Date today = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(DF_YYYY_MM_DDHHMMSS);
return sdf.parse(dateStr);
} catch (ParseException e) {
//unreachable
today = new Date();
}
return today;
}
/**
* 将不同格式的 String 转换成 时间戳
*
* @param str
* @param formatStyle
* @return
*/
public static long stringToLong(String str, String formatStyle) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat(formatStyle);
return sdf.parse(str).getTime();//毫秒
}
/**
* 根据时间的字符串转成Date对象
* yyyy-MM-dd HH:mm:ss
*
* @return
*/
public static Date getDateByDateString2(String dateStr) {
Date today = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(DF_YYYYMMDDHHMMSS);
return sdf.parse(dateStr);
} catch (ParseException e) {
today = new Date();
}
return today;
}
public static String getDateStringByTimeStamp(long timestamp) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(timestamp);
SimpleDateFormat sdf = new SimpleDateFormat(DF_YYYY_MM_DDHHMMSS);
return sdf.format(c.getTime());
}
/**
* 获取当年1月1日0点 date对象
*
* @return
*/
public static Date getNowYear() {
Date year = null;
try {
String format = "yyyy";
year = parse(format(new Date(), format), format);
} catch (ParseException e) {
//unreachable
year = new Date();
}
return year;
}
/**
* 获取当月1号0点 date对象
*
* @return
*/
public static Date getNowMonth() {
Date month = null;
try {
String format = "yyyy-MM";
month = parse(format(new Date(), format), format);
} catch (ParseException e) {
//unreachable
month = new Date();
}
return month;
}
public static int getMonthSpace(Date date1, Date date2) {
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
c1.setTime(date1);
c2.setTime(date2);
int result = c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR);
return result == 0 ? 1 : Math.abs(result);
}
public static int getYearSpace4Now(String date) throws ParseException {
Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
c1.setTime(new Date());
c2.setTime(sdf.parse(date));
int result = c2.get(Calendar.YEAR) - c1.get(Calendar.YEAR);
return result == 0 ? 1 : Math.abs(result);
}
public static Date parse(String dateStr, String format) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.parse(dateStr);
}
/**
* @return 获取当前月第一天:
*/
public static Date getFirstDateOfCurrentMonth() {
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, 0);
c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
try {
return parse(format8(c.getTime()), DF_YYYYMMDD);
} catch (ParseException e) {
return null;
}
}
/**
* @return 获取下月第一天:
*/
public static Date getFirstDateOfNextMonth() {
Calendar c = Calendar.getInstance();
c.add(Calendar.MONTH, 1);
c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
try {
return parse(format8(c.getTime()), DF_YYYYMMDD);
} catch (ParseException e) {
return null;
}
}
/**
* @return 获取当前月最后一天
*/
public static Date getListDateOfCurrentMonth() {
Calendar c = Calendar.getInstance();
c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
try {
return parse(format8(c.getTime()), DF_YYYYMMDD);
} catch (ParseException e) {
return null;
}
}
/**
* @return 当月的总天数
*/
public static int getCurrentMonthDay() {
Calendar a = Calendar.getInstance();
a.set(Calendar.DATE, 1);
a.roll(Calendar.DATE, -1);
int maxDate = a.get(Calendar.DATE);
return maxDate;
}
/**
* 计算时间间隔
*
* @param fDate 上次时间
* @param oDate 本次时间
* @return 精确到天
*/
public static int getIntervalDays(Date fDate, Date oDate) {
if (fDate == null || oDate == null) {
return -1;
}
try {
fDate = parse(format8(fDate), DF_YYYYMMDD);
oDate = parse(format8(oDate), DF_YYYYMMDD);
} catch (ParseException e) {
}
long nd = 1000 * 24 * 60 * 60;//一天的毫秒数
//获得两个时间的毫秒时间差异
long diff = oDate.getTime() - fDate.getTime();
Long day = diff / nd;//计算差多少天
return day.intValue();
}
/**
* @param date
* @return
* @throws ParseException
*/
public static Date getDate8(Date date) {
try {
return parse8(format8(date));
} catch (ParseException e) {
//unreachable
}
return null;
}
}
package com.widget.imagevideobanner.utils;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.widget.imagevideobanner.utils.zxing.BarcodeFormat;
import com.widget.imagevideobanner.utils.zxing.BitMatrix;
import com.widget.imagevideobanner.utils.zxing.EncodeHintType;
import com.widget.imagevideobanner.utils.zxing.QRCodeWriter;
import com.widget.imagevideobanner.utils.zxing.WriterException;
import java.util.Hashtable;
/**
* @ClassName: QRCodeUtil
* @Description: 二维码工具类
*/
public class QRCodeUtil {
/**
* 创建二维码位图
*
* @param content 字符串内容
* @param size 位图宽&高(单位:px)
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(@Nullable String content, int size){
return createQRCodeBitmap(content, size, "UTF-8", "H", "4", Color.BLACK, Color.WHITE, null, null, 0F);
}
/**
* 创建二维码位图 (自定义黑、白色块颜色)
*
* @param content 字符串内容
* @param size 位图宽&高(单位:px)
* @param color_black 黑色色块的自定义颜色值
* @param color_white 白色色块的自定义颜色值
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(@Nullable String content, int size, @ColorInt int color_black, @ColorInt int color_white){
return createQRCodeBitmap(content, size, "UTF-8", "H", "4", color_black, color_white, null, null, 0F);
}
/**
* 创建二维码位图 (带Logo小图片)
*
* @param content 字符串内容
* @param size 位图宽&高(单位:px)
* @param logoBitmap logo图片
* @param logoPercent logo小图片在二维码图片中的占比大小,范围[0F,1F]。超出范围->默认使用0.2F
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(String content, int size, @Nullable Bitmap logoBitmap, float logoPercent){
return createQRCodeBitmap(content, size, "UTF-8", "H", "4", Color.BLACK, Color.WHITE, null, logoBitmap, logoPercent);
}
/**
* 创建二维码位图 (Bitmap颜色代替黑色) 注意!!!注意!!!注意!!! 选用的Bitmap图片一定不能有白色色块,否则会识别不出来!!!
*
* @param content 字符串内容
* @param size 位图宽&高(单位:px)
* @param targetBitmap 目标图片 (如果targetBitmap != null, 黑色色块将会被该图片像素色值替代)
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(String content, int size, Bitmap targetBitmap){
return createQRCodeBitmap(content, size, "UTF-8", "H", "4", Color.BLACK, Color.WHITE, targetBitmap, null, 0F);
}
/**
* 创建二维码位图 (支持自定义配置和自定义样式)
*
* @param content 字符串内容
* @param size 位图宽&高(单位:px)
* @param margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。
* @param color_black 黑色色块的自定义颜色值
* @param color_white 白色色块的自定义颜色值
* @param targetBitmap 目标图片 (如果targetBitmap != null, 黑色色块将会被该图片像素色值替代)
* @param logoBitmap logo小图片
* @param logoPercent logo小图片在二维码图片中的占比大小,范围[0F,1F],超出范围->默认使用0.2F。
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(@Nullable String content, int size,
@Nullable String character_set, @Nullable String error_correction, @Nullable String margin,
@ColorInt int color_black, @ColorInt int color_white, @Nullable Bitmap targetBitmap,
@Nullable Bitmap logoBitmap, float logoPercent){
/** 1.参数合法性判断 */
if(TextUtils.isEmpty(content)){ // 字符串内容判空
return null;
}
if(size <= 0){ // 宽&高都需要>0
return null;
}
try {
/** 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */
Hashtable<EncodeHintType, String> hints = new Hashtable<>();
if(!TextUtils.isEmpty(character_set)) {
hints.put(EncodeHintType.CHARACTER_SET, character_set); // 字符转码格式设置
}
if(!TextUtils.isEmpty(error_correction)){
hints.put(EncodeHintType.ERROR_CORRECTION, error_correction); // 容错级别设置
}
if(!TextUtils.isEmpty(margin)){
hints.put(EncodeHintType.MARGIN, margin); // 空白边距设置
}
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, size, size, hints);
/** 3.根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */
if(targetBitmap != null){
targetBitmap = Bitmap.createScaledBitmap(targetBitmap, size, size, false);
}
int[] pixels = new int[size * size];
for(int y = 0; y < size; y++){
for(int x = 0; x < size; x++){
if(bitMatrix.get(x, y)){ // 黑色色块像素设置
if(targetBitmap != null) {
pixels[y * size + x] = targetBitmap.getPixel(x, y);
} else {
pixels[y * size + x] = color_black;
}
} else { // 白色色块像素设置
pixels[y * size + x] = color_white;
}
}
}
/** 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, size, 0, 0, size, size);
/** 5.为二维码添加logo小图标 */
if(logoBitmap != null){
return addLogo(bitmap, logoBitmap, logoPercent);
}
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
/**
* 向一张图片中间添加logo小图片(图片合成)
*
* @param srcBitmap 原图片
* @param logoBitmap logo图片
* @param logoPercent 百分比 (用于调整logo图片在原图片中的显示大小, 取值范围[0,1], 传值不合法时使用0.2F)
* 原图片是二维码时,建议使用0.2F,百分比过大可能导致二维码扫描失败。
* @return
*/
@Nullable
private static Bitmap addLogo(@Nullable Bitmap srcBitmap, @Nullable Bitmap logoBitmap, float logoPercent){
/** 1. 参数合法性判断 */
if(srcBitmap == null){
return null;
}
if(logoBitmap == null){
return srcBitmap;
}
if(logoPercent < 0F || logoPercent > 1F){
logoPercent = 0.2F;
}
/** 2. 获取原图片和Logo图片各自的宽、高值 */
int srcWidth = srcBitmap.getWidth();
int srcHeight = srcBitmap.getHeight();
int logoWidth = logoBitmap.getWidth();
int logoHeight = logoBitmap.getHeight();
/** 3. 计算画布缩放的宽高比 */
float scaleWidth = srcWidth * logoPercent / logoWidth;
float scaleHeight = srcHeight * logoPercent / logoHeight;
/** 4. 使用Canvas绘制,合成图片 */
Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(srcBitmap, 0, 0, null);
canvas.scale(scaleWidth, scaleHeight, srcWidth/2, srcHeight/2);
canvas.drawBitmap(logoBitmap, srcWidth/2 - logoWidth/2, srcHeight/2 - logoHeight/2, null);
return bitmap;
}
/**
* 创建二维码位图
*
* @param content 字符串内容
* @param size 位图宽&高(单位:px)
* @param margin 空白边距 (可修改,要求>=0)
* @return
*/
@Nullable
public static Bitmap createQRCodeBitmap(@Nullable String content, int size, String margin) {
return createQRCodeBitmap(content, size, "UTF-8", "H", margin, Color.BLACK, Color.WHITE, null, null, 0F);
}
}
\ No newline at end of file
package com.widget.imagevideobanner.utils;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.text.TextUtils;
import com.widget.imagevideobanner.bean.MediaBean;
import com.widget.imagevideobanner.log.BLogUtil;
import java.io.File;
import java.io.FileOutputStream;
import wseemann.media.FFmpegMediaMetadataRetriever;
public class ResourceUtils {
/**
* drawable 转成 uri
*
* @param context
* @param resId
* @return
*/
public static String imageTranslateUri(Context context, int resId) {
Resources r = context.getResources();
Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
+ r.getResourcePackageName(resId) + "/"
+ r.getResourceTypeName(resId) + "/"
+ r.getResourceEntryName(resId));
return uri.toString();
}
/**
* 解析宽高
*/
public static void resolveVideo(MediaBean mediaBean) {
BLogUtil.log("截取视频第一帧开始");
if (TextUtils.isEmpty(mediaBean.getUrl())) {
return;
}
File tempFile = new File(mediaBean.getUrl());
if (!tempFile.exists()) {
return;
}
//信息是全的
if (!TextUtils.isEmpty(mediaBean.getCover())
&& mediaBean.getWidth() != 0
&& mediaBean.getHeight() != 0) {
return;
}
//配置了缩略图 但是没配置宽高
String cover = !TextUtils.isEmpty(mediaBean.getCover())?mediaBean.getCover() :
mediaBean.getUrl().substring(0, mediaBean.getUrl().lastIndexOf(".")) + ".png";
File file = new File(cover);
if (file.exists()) {
mediaBean.setCover(cover);
if (mediaBean.getWidth() == 0 || mediaBean.getHeight() == 0) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(cover, options);
int outWidth = options.outWidth;
int outHeight = options.outHeight;
mediaBean.setWidth(outWidth);
mediaBean.setHeight(outHeight);
}
return;
}
FFmpegMediaMetadataRetriever mmr = null;
Bitmap bmp = null;
try {
mmr = new FFmpegMediaMetadataRetriever();
mmr.setDataSource(mediaBean.getUrl());
mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_ALBUM);
mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_ARTIST);
bmp = mmr.getFrameAtTime();
FileOutputStream out = new FileOutputStream(cover);
bmp.compress(Bitmap.CompressFormat.PNG, 100, out);
mediaBean.setCover(cover);
mediaBean.setWidth(bmp.getWidth());
mediaBean.setHeight(bmp.getHeight());
BLogUtil.log("截取视频第一帧结束");
} catch (Exception e) {
e.printStackTrace();
BLogUtil.log("截取视频第一帧异常"+ThrowableUtils.getFullStackTrace(e));
} finally {
if (null != bmp) bmp.recycle();
if (null != mmr) mmr.release();
}
}
}
package com.widget.imagevideobanner.utils;
import android.graphics.Bitmap;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.bumptech.glide.request.target.ImageViewTarget;
/**
* 设置图片等比缩放
*/
public class TransformationUtils extends ImageViewTarget<Bitmap> {
private ImageView target;
public TransformationUtils(ImageView target) {
super(target);
this.target = target;
}
@Override
protected void setResource(Bitmap resource) {
view.setImageBitmap(resource);
//获取原图的宽高
int width = resource.getWidth();
int height = resource.getHeight();
//获取imageView的宽
int imageViewWidth = target.getWidth();
//计算缩放比例
float sy = (float) (imageViewWidth * 0.1) / (float) (width * 0.1);
//计算图片等比例放大后的高
int imageViewHeight = (int) (height * sy);
ViewGroup.LayoutParams params = target.getLayoutParams();
params.height = imageViewHeight;
target.setLayoutParams(params);
}
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* Enumerates barcode formats known to this package. Please keep alphabetized.
*
* @author Sean Owen
*/
public enum BarcodeFormat {
/** Aztec 2D barcode format. */
AZTEC,
/** CODABAR 1D format. */
CODABAR,
/** Code 39 1D format. */
CODE_39,
/** Code 93 1D format. */
CODE_93,
/** Code 128 1D format. */
CODE_128,
/** Data Matrix 2D barcode format. */
DATA_MATRIX,
/** EAN-8 1D format. */
EAN_8,
/** EAN-13 1D format. */
EAN_13,
/** ITF (Interleaved Two of Five) 1D format. */
ITF,
/** MaxiCode 2D barcode format. */
MAXICODE,
/** PDF417 format. */
PDF_417,
/** QR Code 2D barcode format. */
QR_CODE,
/** RSS 14 */
RSS_14,
/** RSS EXPANDED */
RSS_EXPANDED,
/** UPC-A 1D format. */
UPC_A,
/** UPC-E 1D format. */
UPC_E,
/** UPC/EAN extension format. Not a stand-alone format. */
UPC_EAN_EXTENSION
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
import java.util.Arrays;
/**
* <p>A simple, fast array of bits, represented compactly by an array of ints internally.</p>
*
* @author Sean Owen
*/
public final class BitArray implements Cloneable {
private int[] bits;
private int size;
public BitArray() {
this.size = 0;
this.bits = new int[1];
}
public BitArray(int size) {
this.size = size;
this.bits = makeArray(size);
}
// For testing only
BitArray(int[] bits, int size) {
this.bits = bits;
this.size = size;
}
public int getSize() {
return size;
}
public int getSizeInBytes() {
return (size + 7) / 8;
}
private void ensureCapacity(int size) {
if (size > bits.length * 32) {
int[] newBits = makeArray(size);
System.arraycopy(bits, 0, newBits, 0, bits.length);
this.bits = newBits;
}
}
/**
* @param i bit to get
* @return true iff bit i is set
*/
public boolean get(int i) {
return (bits[i / 32] & (1 << (i & 0x1F))) != 0;
}
/**
* Sets bit i.
*
* @param i bit to set
*/
public void set(int i) {
bits[i / 32] |= 1 << (i & 0x1F);
}
/**
* Flips bit i.
*
* @param i bit to set
*/
public void flip(int i) {
bits[i / 32] ^= 1 << (i & 0x1F);
}
/**
* @param from first bit to check
* @return index of first bit that is set, starting from the given index, or size if none are set
* at or beyond this given index
* @see #getNextUnset(int)
*/
public int getNextSet(int from) {
if (from >= size) {
return size;
}
int bitsOffset = from / 32;
int currentBits = bits[bitsOffset];
// mask off lesser bits first
currentBits &= ~((1 << (from & 0x1F)) - 1);
while (currentBits == 0) {
if (++bitsOffset == bits.length) {
return size;
}
currentBits = bits[bitsOffset];
}
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
return result > size ? size : result;
}
/**
* @param from index to start looking for unset bit
* @return index of next unset bit, or {@code size} if none are unset until the end
* @see #getNextSet(int)
*/
public int getNextUnset(int from) {
if (from >= size) {
return size;
}
int bitsOffset = from / 32;
int currentBits = ~bits[bitsOffset];
// mask off lesser bits first
currentBits &= ~((1 << (from & 0x1F)) - 1);
while (currentBits == 0) {
if (++bitsOffset == bits.length) {
return size;
}
currentBits = ~bits[bitsOffset];
}
int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits);
return result > size ? size : result;
}
/**
* Sets a block of 32 bits, starting at bit i.
*
* @param i first bit to set
* @param newBits the new value of the next 32 bits. Note again that the least-significant bit
* corresponds to bit i, the next-least-significant to i+1, and so on.
*/
public void setBulk(int i, int newBits) {
bits[i / 32] = newBits;
}
/**
* Clears all bits (sets to false).
*/
public void clear() {
int max = bits.length;
for (int i = 0; i < max; i++) {
bits[i] = 0;
}
}
public void appendBit(boolean bit) {
ensureCapacity(size + 1);
if (bit) {
bits[size / 32] |= 1 << (size & 0x1F);
}
size++;
}
/**
* Appends the least-significant bits, from value, in order from most-significant to
* least-significant. For example, appending 6 bits from 0x000001E will append the bits
* 0, 1, 1, 1, 1, 0 in that order.
*
* @param value {@code int} containing bits to append
* @param numBits bits from value to append
*/
public void appendBits(int value, int numBits) {
if (numBits < 0 || numBits > 32) {
throw new IllegalArgumentException("Num bits must be between 0 and 32");
}
ensureCapacity(size + numBits);
for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) {
appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1);
}
}
public void appendBitArray(BitArray other) {
int otherSize = other.size;
ensureCapacity(size + otherSize);
for (int i = 0; i < otherSize; i++) {
appendBit(other.get(i));
}
}
public void xor(BitArray other) {
if (size != other.size) {
throw new IllegalArgumentException("Sizes don't match");
}
for (int i = 0; i < bits.length; i++) {
// The last int could be incomplete (i.e. not have 32 bits in
// it) but there is no problem since 0 XOR 0 == 0.
bits[i] ^= other.bits[i];
}
}
/**
*
* @param bitOffset first bit to start writing
* @param array array to write into. Bytes are written most-significant byte first. This is the opposite
* of the internal representation, which is exposed by {@link #getBitArray()}
* @param offset position in array to start writing
* @param numBytes how many bytes to write
*/
public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) {
for (int i = 0; i < numBytes; i++) {
int theByte = 0;
for (int j = 0; j < 8; j++) {
if (get(bitOffset)) {
theByte |= 1 << (7 - j);
}
bitOffset++;
}
array[offset + i] = (byte) theByte;
}
}
/**
* @return underlying array of ints. The first element holds the first 32 bits, and the least
* significant bit is bit 0.
*/
public int[] getBitArray() {
return bits;
}
/**
* Reverses all bits in the array.
*/
public void reverse() {
int[] newBits = new int[bits.length];
// reverse all int's first
int len = (size - 1) / 32;
int oldBitsLen = len + 1;
for (int i = 0; i < oldBitsLen; i++) {
long x = bits[i];
x = ((x >> 1) & 0x55555555L) | ((x & 0x55555555L) << 1);
x = ((x >> 2) & 0x33333333L) | ((x & 0x33333333L) << 2);
x = ((x >> 4) & 0x0f0f0f0fL) | ((x & 0x0f0f0f0fL) << 4);
x = ((x >> 8) & 0x00ff00ffL) | ((x & 0x00ff00ffL) << 8);
x = ((x >> 16) & 0x0000ffffL) | ((x & 0x0000ffffL) << 16);
newBits[len - i] = (int) x;
}
// now correct the int's if the bit size isn't a multiple of 32
if (size != oldBitsLen * 32) {
int leftOffset = oldBitsLen * 32 - size;
int currentInt = newBits[0] >>> leftOffset;
for (int i = 1; i < oldBitsLen; i++) {
int nextInt = newBits[i];
currentInt |= nextInt << (32 - leftOffset);
newBits[i - 1] = currentInt;
currentInt = nextInt >>> leftOffset;
}
newBits[oldBitsLen - 1] = currentInt;
}
bits = newBits;
}
private static int[] makeArray(int size) {
return new int[(size + 31) / 32];
}
@Override
public boolean equals(Object o) {
if (!(o instanceof BitArray)) {
return false;
}
BitArray other = (BitArray) o;
return size == other.size && Arrays.equals(bits, other.bits);
}
@Override
public int hashCode() {
return 31 * size + Arrays.hashCode(bits);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(size);
for (int i = 0; i < size; i++) {
if ((i & 0x07) == 0) {
result.append(' ');
}
result.append(get(i) ? 'X' : '.');
}
return result.toString();
}
@Override
public BitArray clone() {
return new BitArray(bits.clone(), size);
}
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
import java.util.Arrays;
/**
* <p>Represents a 2D matrix of bits. In function arguments below, and throughout the common
* module, x is the column position, and y is the row position. The ordering is always x, y.
* The origin is at the top-left.</p>
*
* <p>Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins
* with a new int. This is done intentionally so that we can copy out a row into a BitArray very
* efficiently.</p>
*
* <p>The ordering of bits is row-major. Within each int, the least significant bits are used first,
* meaning they represent lower x values. This is compatible with BitArray's implementation.</p>
*
* @author Sean Owen
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class BitMatrix implements Cloneable {
private final int width;
private final int height;
private final int rowSize;
private final int[] bits;
// A helper to construct a square matrix.
public BitMatrix(int dimension) {
this(dimension, dimension);
}
public BitMatrix(int width, int height) {
if (width < 1 || height < 1) {
throw new IllegalArgumentException("Both dimensions must be greater than 0");
}
this.width = width;
this.height = height;
this.rowSize = (width + 31) / 32;
bits = new int[rowSize * height];
}
private BitMatrix(int width, int height, int rowSize, int[] bits) {
this.width = width;
this.height = height;
this.rowSize = rowSize;
this.bits = bits;
}
public static BitMatrix parse(String stringRepresentation, String setString, String unsetString) {
if (stringRepresentation == null) {
throw new IllegalArgumentException();
}
boolean[] bits = new boolean[stringRepresentation.length()];
int bitsPos = 0;
int rowStartPos = 0;
int rowLength = -1;
int nRows = 0;
int pos = 0;
while (pos < stringRepresentation.length()) {
if (stringRepresentation.charAt(pos) == '\n' ||
stringRepresentation.charAt(pos) == '\r') {
if (bitsPos > rowStartPos) {
if (rowLength == -1) {
rowLength = bitsPos - rowStartPos;
} else if (bitsPos - rowStartPos != rowLength) {
throw new IllegalArgumentException("row lengths do not match");
}
rowStartPos = bitsPos;
nRows++;
}
pos++;
} else if (stringRepresentation.substring(pos, pos + setString.length()).equals(setString)) {
pos += setString.length();
bits[bitsPos] = true;
bitsPos++;
} else if (stringRepresentation.substring(pos, pos + unsetString.length()).equals(unsetString)) {
pos += unsetString.length();
bits[bitsPos] = false;
bitsPos++;
} else {
throw new IllegalArgumentException(
"illegal character encountered: " + stringRepresentation.substring(pos));
}
}
// no EOL at end?
if (bitsPos > rowStartPos) {
if (rowLength == -1) {
rowLength = bitsPos - rowStartPos;
} else if (bitsPos - rowStartPos != rowLength) {
throw new IllegalArgumentException("row lengths do not match");
}
nRows++;
}
BitMatrix matrix = new BitMatrix(rowLength, nRows);
for (int i = 0; i < bitsPos; i++) {
if (bits[i]) {
matrix.set(i % rowLength, i / rowLength);
}
}
return matrix;
}
/**
* <p>Gets the requested bit, where true means black.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
* @return value of given bit in matrix
*/
public boolean get(int x, int y) {
int offset = y * rowSize + (x / 32);
return ((bits[offset] >>> (x & 0x1f)) & 1) != 0;
}
/**
* <p>Sets the given bit to true.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
*/
public void set(int x, int y) {
int offset = y * rowSize + (x / 32);
bits[offset] |= 1 << (x & 0x1f);
}
/**
* <p>Flips the given bit.</p>
*
* @param x The horizontal component (i.e. which column)
* @param y The vertical component (i.e. which row)
*/
public void flip(int x, int y) {
int offset = y * rowSize + (x / 32);
bits[offset] ^= 1 << (x & 0x1f);
}
/**
* Clears all bits (sets to false).
*/
public void clear() {
int max = bits.length;
for (int i = 0; i < max; i++) {
bits[i] = 0;
}
}
/**
* <p>Sets a square region of the bit matrix to true.</p>
*
* @param left The horizontal position to begin at (inclusive)
* @param top The vertical position to begin at (inclusive)
* @param width The width of the region
* @param height The height of the region
*/
public void setRegion(int left, int top, int width, int height) {
if (top < 0 || left < 0) {
throw new IllegalArgumentException("Left and top must be nonnegative");
}
if (height < 1 || width < 1) {
throw new IllegalArgumentException("Height and width must be at least 1");
}
int right = left + width;
int bottom = top + height;
if (bottom > this.height || right > this.width) {
throw new IllegalArgumentException("The region must fit inside the matrix");
}
for (int y = top; y < bottom; y++) {
int offset = y * rowSize;
for (int x = left; x < right; x++) {
bits[offset + (x / 32)] |= 1 << (x & 0x1f);
}
}
}
/**
* @return The width of the matrix
*/
public int getWidth() {
return width;
}
/**
* @return The height of the matrix
*/
public int getHeight() {
return height;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof BitMatrix)) {
return false;
}
BitMatrix other = (BitMatrix) o;
return width == other.width && height == other.height && rowSize == other.rowSize &&
Arrays.equals(bits, other.bits);
}
@Override
public int hashCode() {
int hash = width;
hash = 31 * hash + width;
hash = 31 * hash + height;
hash = 31 * hash + rowSize;
hash = 31 * hash + Arrays.hashCode(bits);
return hash;
}
/**
* @return string representation using "X" for set and " " for unset bits
*/
@Override
public String toString() {
return toString("X ", " ");
}
/**
* @param setString representation of a set bit
* @param unsetString representation of an unset bit
* @return string representation of entire matrix utilizing given strings
*/
public String toString(String setString, String unsetString) {
return buildToString(setString, unsetString, "\n");
}
/**
* @param setString representation of a set bit
* @param unsetString representation of an unset bit
* @param lineSeparator newline character in string representation
* @return string representation of entire matrix utilizing given strings and line separator
* @deprecated call {@link #toString(String, String)} only, which uses \n line separator always
*/
@Deprecated
public String toString(String setString, String unsetString, String lineSeparator) {
return buildToString(setString, unsetString, lineSeparator);
}
private String buildToString(String setString, String unsetString, String lineSeparator) {
StringBuilder result = new StringBuilder(height * (width + 1));
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
result.append(get(x, y) ? setString : unsetString);
}
result.append(lineSeparator);
}
return result.toString();
}
@Override
public BitMatrix clone() {
return new BitMatrix(width, height, rowSize, bits.clone());
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
final class BlockPair {
private final byte[] dataBytes;
private final byte[] errorCorrectionBytes;
BlockPair(byte[] data, byte[] errorCorrection) {
dataBytes = data;
errorCorrectionBytes = errorCorrection;
}
public byte[] getDataBytes() {
return dataBytes;
}
public byte[] getErrorCorrectionBytes() {
return errorCorrectionBytes;
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned
* -1, 0, and 1, I'm going to use less memory and go with bytes.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class ByteMatrix {
private final byte[][] bytes;
private final int width;
private final int height;
public ByteMatrix(int width, int height) {
bytes = new byte[height][width];
this.width = width;
this.height = height;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public byte get(int x, int y) {
return bytes[y][x];
}
/**
* @return an internal representation as bytes, in row-major order. array[y][x] represents point (x,y)
*/
public byte[][] getArray() {
return bytes;
}
public void set(int x, int y, byte value) {
bytes[y][x] = value;
}
public void set(int x, int y, int value) {
bytes[y][x] = (byte) value;
}
public void set(int x, int y, boolean value) {
bytes[y][x] = (byte) (value ? 1 : 0);
}
public void clear(byte value) {
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
bytes[y][x] = value;
}
}
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(2 * width * height + 2);
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
switch (bytes[y][x]) {
case 0:
result.append(" 0");
break;
case 1:
result.append(" 1");
break;
default:
result.append(" ");
break;
}
}
result.append('\n');
}
return result.toString();
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
import java.util.HashMap;
import java.util.Map;
/**
* Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1
* of ISO 18004.
*
* @author Sean Owen
*/
public enum CharacterSetECI {
// Enum name is a Java encoding valid for java.lang and java.io
Cp437(new int[]{0,2}),
ISO8859_1(new int[]{1,3}, "ISO-8859-1"),
ISO8859_2(4, "ISO-8859-2"),
ISO8859_3(5, "ISO-8859-3"),
ISO8859_4(6, "ISO-8859-4"),
ISO8859_5(7, "ISO-8859-5"),
ISO8859_6(8, "ISO-8859-6"),
ISO8859_7(9, "ISO-8859-7"),
ISO8859_8(10, "ISO-8859-8"),
ISO8859_9(11, "ISO-8859-9"),
ISO8859_10(12, "ISO-8859-10"),
ISO8859_11(13, "ISO-8859-11"),
ISO8859_13(15, "ISO-8859-13"),
ISO8859_14(16, "ISO-8859-14"),
ISO8859_15(17, "ISO-8859-15"),
ISO8859_16(18, "ISO-8859-16"),
SJIS(20, "Shift_JIS"),
Cp1250(21, "windows-1250"),
Cp1251(22, "windows-1251"),
Cp1252(23, "windows-1252"),
Cp1256(24, "windows-1256"),
UnicodeBigUnmarked(25, "UTF-16BE", "UnicodeBig"),
UTF8(26, "UTF-8"),
ASCII(new int[] {27, 170}, "US-ASCII"),
Big5(28),
GB18030(29, "GB2312", "EUC_CN", "GBK"),
EUC_KR(30, "EUC-KR");
private static final Map<Integer,CharacterSetECI> VALUE_TO_ECI = new HashMap<>();
private static final Map<String,CharacterSetECI> NAME_TO_ECI = new HashMap<>();
static {
for (CharacterSetECI eci : values()) {
for (int value : eci.values) {
VALUE_TO_ECI.put(value, eci);
}
NAME_TO_ECI.put(eci.name(), eci);
for (String name : eci.otherEncodingNames) {
NAME_TO_ECI.put(name, eci);
}
}
}
private final int[] values;
private final String[] otherEncodingNames;
CharacterSetECI(int value) {
this(new int[] {value});
}
CharacterSetECI(int value, String... otherEncodingNames) {
this.values = new int[] {value};
this.otherEncodingNames = otherEncodingNames;
}
CharacterSetECI(int[] values, String... otherEncodingNames) {
this.values = values;
this.otherEncodingNames = otherEncodingNames;
}
public int getValue() {
return values[0];
}
/**
* @param name character set ECI encoding name
* @return CharacterSetECI representing ECI for character encoding, or null if it is legal
* but unsupported
*/
public static CharacterSetECI getCharacterSetECIByName(String name) {
return NAME_TO_ECI.get(name);
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* These are a set of hints that you may pass to Writers to specify their behavior.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public enum EncodeHintType {
/**
* Specifies what degree of error correction to use, for example in QR Codes.
* Type depends on the encoder. For example for QR codes it's type
* {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}.
* For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words.
* For PDF417 it is of type {@link Integer}, valid values being 0 to 8.
* In all cases, it can also be a {@link String} representation of the desired value as well.
* Note: an Aztec symbol should have a minimum of 25% EC words.
*/
ERROR_CORRECTION,
/**
* Specifies what character encoding to use where applicable (type {@link String})
*/
CHARACTER_SET,
/**
* Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint})
*/
DATA_MATRIX_SHAPE,
/**
* Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*
* @deprecated use width/height params in
* {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)}
*/
@Deprecated
MIN_SIZE,
/**
* Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now.
*
* @deprecated without replacement
*/
@Deprecated
MAX_SIZE,
/**
* Specifies margin, in pixels, to use when generating the barcode. The meaning can vary
* by format; for example it controls margin before and after the barcode horizontally for
* most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value).
*/
MARGIN,
/**
* Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false"
* {@link String} value).
*/
PDF417_COMPACT,
/**
* Specifies what compaction mode to use for PDF417 (type
* {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its
* enum values).
*/
PDF417_COMPACTION,
/**
* Specifies the minimum and maximum number of rows and columns for PDF417 (type
* {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}).
*/
PDF417_DIMENSIONS,
/**
* Specifies the required number of layers for an Aztec code.
* A negative number (-1, -2, -3, -4) specifies a compact Aztec code.
* 0 indicates to use the minimum number of layers (the default).
* A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code.
* (Type {@link Integer}, or {@link String} representation of the integer value).
*/
AZTEC_LAYERS,
/**
* Specifies the exact version of QR code to be encoded.
* (Type {@link Integer}, or {@link String} representation of the integer value).
*/
QR_VERSION,
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* <p>See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
* defined by the QR code standard.</p>
*
* @author Sean Owen
*/
public enum ErrorCorrectionLevel {
/** L = ~7% correction */
L(0x01),
/** M = ~15% correction */
M(0x00),
/** Q = ~25% correction */
Q(0x03),
/** H = ~30% correction */
H(0x02);
private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q};
private final int bits;
ErrorCorrectionLevel(int bits) {
this.bits = bits;
}
public int getBits() {
return bits;
}
/**
* @param bits int containing the two bits encoding a QR Code's error correction level
* @return ErrorCorrectionLevel representing the encoded error correction level
*/
public static ErrorCorrectionLevel forBits(int bits) {
if (bits < 0 || bits >= FOR_BITS.length) {
throw new IllegalArgumentException();
}
return FOR_BITS[bits];
}
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* Thrown when a barcode was successfully detected, but some aspect of
* the content did not conform to the barcode's format rules. This could have
* been due to a mis-detection.
*
* @author Sean Owen
*/
public final class FormatException extends ReaderException {
private static final FormatException INSTANCE = new FormatException();
static {
INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless
}
private FormatException() {
}
}
\ No newline at end of file
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* <p>Encapsulates a QR Code's format information, including the data mask used and
* error correction level.</p>
*
* @author Sean Owen
* @see DataMask
* @see ErrorCorrectionLevel
*/
final class FormatInformation {
private static final int FORMAT_INFO_MASK_QR = 0x5412;
/**
* See ISO 18004:2006, Annex C, Table C.1
*/
private static final int[][] FORMAT_INFO_DECODE_LOOKUP = {
{0x5412, 0x00},
{0x5125, 0x01},
{0x5E7C, 0x02},
{0x5B4B, 0x03},
{0x45F9, 0x04},
{0x40CE, 0x05},
{0x4F97, 0x06},
{0x4AA0, 0x07},
{0x77C4, 0x08},
{0x72F3, 0x09},
{0x7DAA, 0x0A},
{0x789D, 0x0B},
{0x662F, 0x0C},
{0x6318, 0x0D},
{0x6C41, 0x0E},
{0x6976, 0x0F},
{0x1689, 0x10},
{0x13BE, 0x11},
{0x1CE7, 0x12},
{0x19D0, 0x13},
{0x0762, 0x14},
{0x0255, 0x15},
{0x0D0C, 0x16},
{0x083B, 0x17},
{0x355F, 0x18},
{0x3068, 0x19},
{0x3F31, 0x1A},
{0x3A06, 0x1B},
{0x24B4, 0x1C},
{0x2183, 0x1D},
{0x2EDA, 0x1E},
{0x2BED, 0x1F},
};
private final ErrorCorrectionLevel errorCorrectionLevel;
private final byte dataMask;
private FormatInformation(int formatInfo) {
// Bits 3,4
errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03);
// Bottom 3 bits
dataMask = (byte) (formatInfo & 0x07);
}
static int numBitsDiffering(int a, int b) {
return Integer.bitCount(a ^ b);
}
/**
* @param maskedFormatInfo1 format info indicator, with mask still applied
* @param maskedFormatInfo2 second copy of same info; both are checked at the same time
* to establish best match
* @return information about the format it specifies, or {@code null}
* if doesn't seem to match any known pattern
*/
static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2);
if (formatInfo != null) {
return formatInfo;
}
// Should return null, but, some QR codes apparently
// do not mask this info. Try again by actually masking the pattern
// first
return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR,
maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR);
}
private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) {
// Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
int bestDifference = Integer.MAX_VALUE;
int bestFormatInfo = 0;
for (int[] decodeInfo : FORMAT_INFO_DECODE_LOOKUP) {
int targetInfo = decodeInfo[0];
if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) {
// Found an exact match
return new FormatInformation(decodeInfo[1]);
}
int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo);
if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference;
}
if (maskedFormatInfo1 != maskedFormatInfo2) {
// also try the other option
bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo);
if (bitsDifference < bestDifference) {
bestFormatInfo = decodeInfo[1];
bestDifference = bitsDifference;
}
}
}
// Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits
// differing means we found a match
if (bestDifference <= 3) {
return new FormatInformation(bestFormatInfo);
}
return null;
}
ErrorCorrectionLevel getErrorCorrectionLevel() {
return errorCorrectionLevel;
}
byte getDataMask() {
return dataMask;
}
@Override
public int hashCode() {
return (errorCorrectionLevel.ordinal() << 3) | dataMask;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof FormatInformation)) {
return false;
}
FormatInformation other = (FormatInformation) o;
return this.errorCorrectionLevel == other.errorCorrectionLevel &&
this.dataMask == other.dataMask;
}
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* <p>This class contains utility methods for performing mathematical operations over
* the Galois Fields. Operations use a given primitive polynomial in calculations.</p>
*
* <p>Throughout this package, elements of the GF are represented as an {@code int}
* for convenience and speed (but at the cost of memory).
* </p>
*
* @author Sean Owen
* @author David Olivier
*/
public final class GenericGF {
public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256;
public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6;
private final int[] expTable;
private final int[] logTable;
private final GenericGFPoly zero;
private final GenericGFPoly one;
private final int size;
private final int primitive;
private final int generatorBase;
/**
* Create a representation of GF(size) using the given primitive polynomial.
*
* @param primitive irreducible polynomial whose coefficients are represented by
* the bits of an int, where the least-significant bit represents the constant
* coefficient
* @param size the size of the field
* @param b the factor b in the generator polynomial can be 0- or 1-based
* (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
* In most cases it should be 1, but for QR code it is 0.
*/
public GenericGF(int primitive, int size, int b) {
this.primitive = primitive;
this.size = size;
this.generatorBase = b;
expTable = new int[size];
logTable = new int[size];
int x = 1;
for (int i = 0; i < size; i++) {
expTable[i] = x;
x *= 2; // we're assuming the generator alpha is 2
if (x >= size) {
x ^= primitive;
x &= size - 1;
}
}
for (int i = 0; i < size - 1; i++) {
logTable[expTable[i]] = i;
}
// logTable[0] == 0 but this should never be used
zero = new GenericGFPoly(this, new int[]{0});
one = new GenericGFPoly(this, new int[]{1});
}
GenericGFPoly getZero() {
return zero;
}
GenericGFPoly getOne() {
return one;
}
/**
* @return the monomial representing coefficient * x^degree
*/
GenericGFPoly buildMonomial(int degree, int coefficient) {
if (degree < 0) {
throw new IllegalArgumentException();
}
if (coefficient == 0) {
return zero;
}
int[] coefficients = new int[degree + 1];
coefficients[0] = coefficient;
return new GenericGFPoly(this, coefficients);
}
/**
* Implements both addition and subtraction -- they are the same in GF(size).
*
* @return sum/difference of a and b
*/
static int addOrSubtract(int a, int b) {
return a ^ b;
}
/**
* @return 2 to the power of a in GF(size)
*/
int exp(int a) {
return expTable[a];
}
/**
* @return base 2 log of a in GF(size)
*/
int log(int a) {
if (a == 0) {
throw new IllegalArgumentException();
}
return logTable[a];
}
/**
* @return multiplicative inverse of a
*/
int inverse(int a) {
if (a == 0) {
throw new ArithmeticException();
}
return expTable[size - logTable[a] - 1];
}
/**
* @return product of a and b in GF(size)
*/
int multiply(int a, int b) {
if (a == 0 || b == 0) {
return 0;
}
return expTable[(logTable[a] + logTable[b]) % (size - 1)];
}
public int getSize() {
return size;
}
public int getGeneratorBase() {
return generatorBase;
}
@Override
public String toString() {
return "GF(0x" + Integer.toHexString(primitive) + ',' + size + ')';
}
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* <p>Represents a polynomial whose coefficients are elements of a GF.
* Instances of this class are immutable.</p>
*
* <p>Much credit is due to William Rucklidge since portions of this code are an indirect
* port of his C++ Reed-Solomon implementation.</p>
*
* @author Sean Owen
*/
final class GenericGFPoly {
private final GenericGF field;
private final int[] coefficients;
/**
* @param field the {@link GenericGF} instance representing the field to use
* to perform computations
* @param coefficients coefficients as ints representing elements of GF(size), arranged
* from most significant (highest-power term) coefficient to least significant
* @throws IllegalArgumentException if argument is null or empty,
* or if leading coefficient is 0 and this is not a
* constant polynomial (that is, it is not the monomial "0")
*/
GenericGFPoly(GenericGF field, int[] coefficients) {
if (coefficients.length == 0) {
throw new IllegalArgumentException();
}
this.field = field;
int coefficientsLength = coefficients.length;
if (coefficientsLength > 1 && coefficients[0] == 0) {
// Leading term must be non-zero for anything except the constant polynomial "0"
int firstNonZero = 1;
while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) {
firstNonZero++;
}
if (firstNonZero == coefficientsLength) {
this.coefficients = new int[]{0};
} else {
this.coefficients = new int[coefficientsLength - firstNonZero];
System.arraycopy(coefficients,
firstNonZero,
this.coefficients,
0,
this.coefficients.length);
}
} else {
this.coefficients = coefficients;
}
}
int[] getCoefficients() {
return coefficients;
}
/**
* @return degree of this polynomial
*/
int getDegree() {
return coefficients.length - 1;
}
/**
* @return true iff this polynomial is the monomial "0"
*/
boolean isZero() {
return coefficients[0] == 0;
}
/**
* @return coefficient of x^degree term in this polynomial
*/
int getCoefficient(int degree) {
return coefficients[coefficients.length - 1 - degree];
}
GenericGFPoly addOrSubtract(GenericGFPoly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
}
if (isZero()) {
return other;
}
if (other.isZero()) {
return this;
}
int[] smallerCoefficients = this.coefficients;
int[] largerCoefficients = other.coefficients;
if (smallerCoefficients.length > largerCoefficients.length) {
int[] temp = smallerCoefficients;
smallerCoefficients = largerCoefficients;
largerCoefficients = temp;
}
int[] sumDiff = new int[largerCoefficients.length];
int lengthDiff = largerCoefficients.length - smallerCoefficients.length;
// Copy high-order terms only found in higher-degree polynomial's coefficients
System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff);
for (int i = lengthDiff; i < largerCoefficients.length; i++) {
sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]);
}
return new GenericGFPoly(field, sumDiff);
}
GenericGFPoly multiply(GenericGFPoly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
}
if (isZero() || other.isZero()) {
return field.getZero();
}
int[] aCoefficients = this.coefficients;
int aLength = aCoefficients.length;
int[] bCoefficients = other.coefficients;
int bLength = bCoefficients.length;
int[] product = new int[aLength + bLength - 1];
for (int i = 0; i < aLength; i++) {
int aCoeff = aCoefficients[i];
for (int j = 0; j < bLength; j++) {
product[i + j] = GenericGF.addOrSubtract(product[i + j],
field.multiply(aCoeff, bCoefficients[j]));
}
}
return new GenericGFPoly(field, product);
}
GenericGFPoly multiplyByMonomial(int degree, int coefficient) {
if (degree < 0) {
throw new IllegalArgumentException();
}
if (coefficient == 0) {
return field.getZero();
}
int size = coefficients.length;
int[] product = new int[size + degree];
for (int i = 0; i < size; i++) {
product[i] = field.multiply(coefficients[i], coefficient);
}
return new GenericGFPoly(field, product);
}
GenericGFPoly[] divide(GenericGFPoly other) {
if (!field.equals(other.field)) {
throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field");
}
if (other.isZero()) {
throw new IllegalArgumentException("Divide by 0");
}
GenericGFPoly quotient = field.getZero();
GenericGFPoly remainder = this;
int denominatorLeadingTerm = other.getCoefficient(other.getDegree());
int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm);
while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) {
int degreeDifference = remainder.getDegree() - other.getDegree();
int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm);
GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale);
GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale);
quotient = quotient.addOrSubtract(iterationQuotient);
remainder = remainder.addOrSubtract(term);
}
return new GenericGFPoly[] { quotient, remainder };
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(8 * getDegree());
for (int degree = getDegree(); degree >= 0; degree--) {
int coefficient = getCoefficient(degree);
if (coefficient != 0) {
if (coefficient < 0) {
result.append(" - ");
coefficient = -coefficient;
} else {
if (result.length() > 0) {
result.append(" + ");
}
}
if (degree == 0 || coefficient != 1) {
int alphaPower = field.log(coefficient);
if (alphaPower == 0) {
result.append('1');
} else if (alphaPower == 1) {
result.append('a');
} else {
result.append("a^");
result.append(alphaPower);
}
}
if (degree != 0) {
if (degree == 1) {
result.append('x');
} else {
result.append("x^");
result.append(degree);
}
}
}
}
return result.toString();
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* @author Satoru Takabayashi
* @author Daniel Switkin
* @author Sean Owen
*/
final class MaskUtil {
// Penalty weights from section 6.8.2.1
private static final int N1 = 3;
private static final int N2 = 3;
private static final int N3 = 40;
private static final int N4 = 10;
private MaskUtil() {
// do nothing
}
/**
* Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and
* give penalty to them. Example: 00000 or 11111.
*/
static int applyMaskPenaltyRule1(ByteMatrix matrix) {
return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false);
}
/**
* Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give
* penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a
* penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block.
*/
static int applyMaskPenaltyRule2(ByteMatrix matrix) {
int penalty = 0;
byte[][] array = matrix.getArray();
int width = matrix.getWidth();
int height = matrix.getHeight();
for (int y = 0; y < height - 1; y++) {
for (int x = 0; x < width - 1; x++) {
int value = array[y][x];
if (value == array[y][x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) {
penalty++;
}
}
}
return N2 * penalty;
}
/**
* Apply mask penalty rule 3 and return the penalty. Find consecutive runs of 1:1:3:1:1:4
* starting with black, or 4:1:1:3:1:1 starting with white, and give penalty to them. If we
* find patterns like 000010111010000, we give penalty once.
*/
static int applyMaskPenaltyRule3(ByteMatrix matrix) {
int numPenalties = 0;
byte[][] array = matrix.getArray();
int width = matrix.getWidth();
int height = matrix.getHeight();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
byte[] arrayY = array[y]; // We can at least optimize this access
if (x + 6 < width &&
arrayY[x] == 1 &&
arrayY[x + 1] == 0 &&
arrayY[x + 2] == 1 &&
arrayY[x + 3] == 1 &&
arrayY[x + 4] == 1 &&
arrayY[x + 5] == 0 &&
arrayY[x + 6] == 1 &&
(isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11))) {
numPenalties++;
}
if (y + 6 < height &&
array[y][x] == 1 &&
array[y + 1][x] == 0 &&
array[y + 2][x] == 1 &&
array[y + 3][x] == 1 &&
array[y + 4][x] == 1 &&
array[y + 5][x] == 0 &&
array[y + 6][x] == 1 &&
(isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11))) {
numPenalties++;
}
}
}
return numPenalties * N3;
}
private static boolean isWhiteHorizontal(byte[] rowArray, int from, int to) {
from = Math.max(from, 0);
to = Math.min(to, rowArray.length);
for (int i = from; i < to; i++) {
if (rowArray[i] == 1) {
return false;
}
}
return true;
}
private static boolean isWhiteVertical(byte[][] array, int col, int from, int to) {
from = Math.max(from, 0);
to = Math.min(to, array.length);
for (int i = from; i < to; i++) {
if (array[i][col] == 1) {
return false;
}
}
return true;
}
/**
* Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give
* penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance.
*/
static int applyMaskPenaltyRule4(ByteMatrix matrix) {
int numDarkCells = 0;
byte[][] array = matrix.getArray();
int width = matrix.getWidth();
int height = matrix.getHeight();
for (int y = 0; y < height; y++) {
byte[] arrayY = array[y];
for (int x = 0; x < width; x++) {
if (arrayY[x] == 1) {
numDarkCells++;
}
}
}
int numTotalCells = matrix.getHeight() * matrix.getWidth();
int fivePercentVariances = Math.abs(numDarkCells * 2 - numTotalCells) * 10 / numTotalCells;
return fivePercentVariances * N4;
}
/**
* Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask
* pattern conditions.
*/
static boolean getDataMaskBit(int maskPattern, int x, int y) {
int intermediate;
int temp;
switch (maskPattern) {
case 0:
intermediate = (y + x) & 0x1;
break;
case 1:
intermediate = y & 0x1;
break;
case 2:
intermediate = x % 3;
break;
case 3:
intermediate = (y + x) % 3;
break;
case 4:
intermediate = ((y / 2) + (x / 3)) & 0x1;
break;
case 5:
temp = y * x;
intermediate = (temp & 0x1) + (temp % 3);
break;
case 6:
temp = y * x;
intermediate = ((temp & 0x1) + (temp % 3)) & 0x1;
break;
case 7:
temp = y * x;
intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1;
break;
default:
throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern);
}
return intermediate == 0;
}
/**
* Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both
* vertical and horizontal orders respectively.
*/
private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) {
int penalty = 0;
int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth();
int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight();
byte[][] array = matrix.getArray();
for (int i = 0; i < iLimit; i++) {
int numSameBitCells = 0;
int prevBit = -1;
for (int j = 0; j < jLimit; j++) {
int bit = isHorizontal ? array[i][j] : array[j][i];
if (bit == prevBit) {
numSameBitCells++;
} else {
if (numSameBitCells >= 5) {
penalty += N1 + (numSameBitCells - 5);
}
numSameBitCells = 1; // Include the cell itself.
prevBit = bit;
}
}
if (numSameBitCells >= 5) {
penalty += N1 + (numSameBitCells - 5);
}
}
return penalty;
}
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* <p>See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
* data can be encoded to bits in the QR code standard.</p>
*
* @author Sean Owen
*/
public enum Mode {
TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode...
NUMERIC(new int[]{10, 12, 14}, 0x01),
ALPHANUMERIC(new int[]{9, 11, 13}, 0x02),
STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported
BYTE(new int[]{8, 16, 16}, 0x04),
ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply
KANJI(new int[]{8, 10, 12}, 0x08),
FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05),
FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09),
/** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
HANZI(new int[]{8, 10, 12}, 0x0D);
private final int[] characterCountBitsForVersions;
private final int bits;
Mode(int[] characterCountBitsForVersions, int bits) {
this.characterCountBitsForVersions = characterCountBitsForVersions;
this.bits = bits;
}
/**
* @param bits four bits encoding a QR Code data mode
* @return Mode encoded by these bits
* @throws IllegalArgumentException if bits do not correspond to a known mode
*/
public static Mode forBits(int bits) {
switch (bits) {
case 0x0:
return TERMINATOR;
case 0x1:
return NUMERIC;
case 0x2:
return ALPHANUMERIC;
case 0x3:
return STRUCTURED_APPEND;
case 0x4:
return BYTE;
case 0x5:
return FNC1_FIRST_POSITION;
case 0x7:
return ECI;
case 0x8:
return KANJI;
case 0x9:
return FNC1_SECOND_POSITION;
case 0xD:
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
return HANZI;
default:
throw new IllegalArgumentException();
}
}
/**
* @param version version in question
* @return number of bits used, in this QR Code symbol {@link Version}, to encode the
* count of characters that will follow encoded in this Mode
*/
public int getCharacterCountBits(Version version) {
int number = version.getVersionNumber();
int offset;
if (number <= 9) {
offset = 0;
} else if (number <= 26) {
offset = 1;
} else {
offset = 2;
}
return characterCountBitsForVersions[offset];
}
public int getBits() {
return bits;
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* @author satorux@google.com (Satoru Takabayashi) - creator
* @author dswitkin@google.com (Daniel Switkin) - ported from C++
*/
public final class QRCode {
public static final int NUM_MASK_PATTERNS = 8;
private Mode mode;
private ErrorCorrectionLevel ecLevel;
private Version version;
private int maskPattern;
private ByteMatrix matrix;
public QRCode() {
maskPattern = -1;
}
public Mode getMode() {
return mode;
}
public Version getVersion() {
return version;
}
public ByteMatrix getMatrix() {
return matrix;
}
@Override
public String toString() {
StringBuilder result = new StringBuilder(200);
result.append("<<\n");
result.append(" mode: ");
result.append(mode);
result.append("\n ecLevel: ");
result.append(ecLevel);
result.append("\n version: ");
result.append(version);
result.append("\n maskPattern: ");
result.append(maskPattern);
if (matrix == null) {
result.append("\n matrix: null\n");
} else {
result.append("\n matrix:\n");
result.append(matrix);
}
result.append(">>\n");
return result.toString();
}
public void setMode(Mode value) {
mode = value;
}
public void setECLevel(ErrorCorrectionLevel value) {
ecLevel = value;
}
public void setVersion(Version version) {
this.version = version;
}
public void setMaskPattern(int value) {
maskPattern = value;
}
public void setMatrix(ByteMatrix value) {
matrix = value;
}
// Check if "mask_pattern" is valid.
public static boolean isValidMaskPattern(int maskPattern) {
return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS;
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
import java.util.Map;
/**
* This object renders a QR Code as a BitMatrix 2D array of greyscale values.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class QRCodeWriter implements Writer {
private static final int QUIET_ZONE_SIZE = 4;
@Override
public BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
throws WriterException {
new String(new byte[]{12,12});
return encode(contents, format, width, height, null);
}
@Override
public BitMatrix encode(String contents,
BarcodeFormat format,
int width,
int height,
Map<EncodeHintType,?> hints) throws WriterException {
if (contents.isEmpty()) {
throw new IllegalArgumentException("Found empty contents");
}
if (format != BarcodeFormat.QR_CODE) {
throw new IllegalArgumentException("Can only encode QR_CODE, but got " + format);
}
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' +
height);
}
ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
int quietZone = QUIET_ZONE_SIZE;
if (hints != null) {
if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) {
errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString());
}
if (hints.containsKey(EncodeHintType.MARGIN)) {
quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString());
}
}
QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints);
return renderResult(code, width, height, quietZone);
}
// Note that the input matrix uses 0 == white, 1 == black, while the output matrix uses
// 0 == black, 255 == white (i.e. an 8 bit greyscale bitmap).
private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
int qrWidth = inputWidth + (quietZone * 2);
int qrHeight = inputHeight + (quietZone * 2);
int outputWidth = Math.max(width, qrWidth);
int outputHeight = Math.max(height, qrHeight);
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
// Padding includes both the quiet zone and the extra white pixels to accommodate the requested
// dimensions. For example, if input is 25x25 the QR will be 33x33 including the quiet zone.
// If the requested size is 200x160, the multiple will be 4, for a QR of 132x132. These will
// handle all the padding from 100x100 (the actual QR) up to 200x160.
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
// Write the contents of this row of the barcode
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
}
}
return output;
}
}
/*
* Copyright 2007 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* The general exception class throw when something goes wrong during decoding of a barcode.
* This includes, but is not limited to, failing checksums / error correction algorithms, being
* unable to locate finder timing patterns, and so on.
*
* @author Sean Owen
*/
public abstract class ReaderException extends Exception {
// disable stack traces when not running inside test units
protected static final boolean isStackTrace =
System.getProperty("surefire.test.class.path") != null;
protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0];
ReaderException() {
// do nothing
}
// Prevent stack traces from being taken
@Override
public final synchronized Throwable fillInStackTrace() {
return null;
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
import java.util.ArrayList;
import java.util.List;
/**
* <p>Implements Reed-Solomon enbcoding, as the name implies.</p>
*
* @author Sean Owen
* @author William Rucklidge
*/
public final class ReedSolomonEncoder {
private final GenericGF field;
private final List<GenericGFPoly> cachedGenerators;
public ReedSolomonEncoder(GenericGF field) {
this.field = field;
this.cachedGenerators = new ArrayList<>();
cachedGenerators.add(new GenericGFPoly(field, new int[]{1}));
}
private GenericGFPoly buildGenerator(int degree) {
if (degree >= cachedGenerators.size()) {
GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1);
for (int d = cachedGenerators.size(); d <= degree; d++) {
GenericGFPoly nextGenerator = lastGenerator.multiply(
new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) }));
cachedGenerators.add(nextGenerator);
lastGenerator = nextGenerator;
}
}
return cachedGenerators.get(degree);
}
public void encode(int[] toEncode, int ecBytes) {
if (ecBytes == 0) {
throw new IllegalArgumentException("No error correction bytes");
}
int dataBytes = toEncode.length - ecBytes;
if (dataBytes <= 0) {
throw new IllegalArgumentException("No data bytes provided");
}
GenericGFPoly generator = buildGenerator(ecBytes);
int[] infoCoefficients = new int[dataBytes];
System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes);
GenericGFPoly info = new GenericGFPoly(field, infoCoefficients);
info = info.multiplyByMonomial(ecBytes, 1);
GenericGFPoly remainder = info.divide(generator)[1];
int[] coefficients = remainder.getCoefficients();
int numZeroCoefficients = ecBytes - coefficients.length;
for (int i = 0; i < numZeroCoefficients; i++) {
toEncode[dataBytes + i] = 0;
}
System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length);
}
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
import java.util.Map;
/**
* The base class for all objects which encode/generate a barcode image.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public interface Writer {
/**
* Encode a barcode using the default settings.
*
* @param contents The contents to encode in the barcode
* @param format The barcode format to generate
* @param width The preferred width in pixels
* @param height The preferred height in pixels
* @return {@link BitMatrix} representing encoded barcode image
* @throws WriterException if contents cannot be encoded legally in a format
*/
BitMatrix encode(String contents, BarcodeFormat format, int width, int height)
throws WriterException;
/**
* @param contents The contents to encode in the barcode
* @param format The barcode format to generate
* @param width The preferred width in pixels
* @param height The preferred height in pixels
* @param hints Additional parameters to supply to the encoder
* @return {@link BitMatrix} representing encoded barcode image
* @throws WriterException if contents cannot be encoded legally in a format
*/
BitMatrix encode(String contents,
BarcodeFormat format,
int width,
int height,
Map<EncodeHintType, ?> hints)
throws WriterException;
}
/*
* Copyright 2008 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.widget.imagevideobanner.utils.zxing;
/**
* A base class which covers the range of exceptions which may occur when encoding a barcode using
* the Writer framework.
*
* @author dswitkin@google.com (Daniel Switkin)
*/
public final class WriterException extends Exception {
public WriterException() {
}
public WriterException(String message) {
super(message);
}
public WriterException(Throwable cause) {
super(cause);
}
}
package com.widget.imagevideobanner.view;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.os.Looper;
import android.support.v7.widget.AppCompatImageView;
import android.util.Log;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.Target;
import com.widget.imagevideobanner.R;
import com.widget.imagevideobanner.bean.MediaBean;
import com.widget.imagevideobanner.log.BLogUtil;
import com.widget.imagevideobanner.player.IPlayTarget;
import com.widget.imagevideobanner.utils.QRCodeUtil;
import com.widget.imagevideobanner.utils.ThrowableUtils;
/**
* 5秒自动切换图片
*/
public class VpImageView extends AppCompatImageView implements IPlayTarget {
public class ImageSubView extends AppCompatImageView implements IPlayTarget, RequestListener<String, GlideDrawable>,Runnable {
private static final String TAG = "VpImageView";
private static final int DEFAULT_SWITCH_PAGE_TIME = 5000;
private OnActionFinishListener onPageChangedListener;
private Handler handler = new Handler();
private Runnable pageChangeRunable = new Runnable() {
@Override
public void run() {
inActive();
if (null != onPageChangedListener){
onPageChangedListener.next();
}
}
};
private RequestListener requestListener = new RequestListener() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
Log.e(TAG,ThrowableUtils.getFullStackTrace(e));
return false;
}
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public boolean onResourceReady(Object resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
return false;
}
};
public VpImageView(Context context) {
public ImageSubView(Context context) {
super(context);
}
public VpImageView(Context context, final MediaBean mediaBean, final OnActionFinishListener onPageChangedListener) {
public ImageSubView(Context context, final MediaBean mediaBean, final OnActionFinishListener onPageChangedListener) {
super(context);
this.onPageChangedListener = onPageChangedListener;
setImageUrl(mediaBean.getUrl());
}
private void setImageUrl(String url) {
Log.e(TAG,url);
BLogUtil.log("设置图片链接:"+url);
Drawable err = new BitmapDrawable(getResources(), QRCodeUtil.createQRCodeBitmap(url,100));
Glide.with(getContext()).load(url)
.error(R.drawable.load_fail)
.placeholder(R.drawable.load_fail)
.listener(requestListener).into(this);
.centerCrop()
.error(err)
.listener(this)
.into(this);
}
@Override
public void onActive() {
handler.removeCallbacks(pageChangeRunable);
handler.postDelayed(pageChangeRunable, DEFAULT_SWITCH_PAGE_TIME);
BLogUtil.log("图片开始展示");
handler.removeCallbacks(this);
handler.postDelayed(this,DEFAULT_SWITCH_PAGE_TIME);
}
@Override
public void inActive() {
handler.removeCallbacks(pageChangeRunable);
BLogUtil.log("图片结束展示");
handler.removeCallbacks(this);
}
@Override
public void run() {
inActive();
if (null != onPageChangedListener){
onPageChangedListener.next();
}
}
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
BLogUtil.log("图片加载异常"+ThrowableUtils.getFullStackTrace(e));
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
BLogUtil.log("图片加载完成");
return false;
}
}
package com.widget.imagevideobanner.view;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack;
import com.shuyu.gsyvideoplayer.video.GSYSampleADVideoPlayer;
import com.widget.imagevideobanner.R;
import com.widget.imagevideobanner.bean.MediaBean;
import com.widget.imagevideobanner.log.BLogUtil;
import com.widget.imagevideobanner.player.IPlayTarget;
public class VideoSubView extends FrameLayout implements IPlayTarget {
private OnActionFinishListener mOnPageChangedListener;
private SampleCoverVideo player;
private final GSYSampleCallBack sampleCallBack = new GSYSampleCallBack(){
@Override
public void onAutoComplete(String url, Object... objects) {
super.onAutoComplete(url, objects);
BLogUtil.log("当前视频播放完毕,切换下一帧");
if(null != mOnPageChangedListener){
mOnPageChangedListener.next();
}
}
};
public VideoSubView(@NonNull Context context) {
super(context);
}
public VideoSubView(@NonNull Context context, MediaBean mediaBean, final OnActionFinishListener onPageChangedListener) {
super(context);
mOnPageChangedListener = onPageChangedListener;
View parent = LayoutInflater.from(context).inflate(R.layout.layout_player_view, this, true);
player = parent.findViewById(R.id.player);
player.getBackButton().setVisibility(View.GONE);
player.setAutoFullWithSize(true);
player.setVideoAllCallBack(sampleCallBack);
player.loadCoverImage(mediaBean.getCover(),0);
player.setUp(mediaBean.getUrl(), true, null);
player.setLooping(mediaBean.isLooping());
}
@Override
public void onActive() {
BLogUtil.log("视频开始播放");
player.startPlayLogic();
}
@Override
public void inActive() {
BLogUtil.log("视频暂停播放");
try {
player.onVideoReset();
}catch (Throwable e){
BLogUtil.log("停止视频播放出错 "+e.getLocalizedMessage());
}
}
}
......@@ -88,7 +88,7 @@ public class ViewPager extends ViewGroup {
private static final boolean USE_CACHE = false;
private static final int DEFAULT_OFFSCREEN_PAGES = 5;//默认是1,这里直接改为0
private static final int DEFAULT_OFFSCREEN_PAGES = 0;//默认是1,这里直接改为0
private static final int MAX_SETTLE_DURATION = 600; // ms
private static final int MIN_DISTANCE_FOR_FLING = 25; // dips
......
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.exoplayer2.ui.PlayerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
app:use_controller="false"
app:show_timeout="1000"
app:surface_type="texture_view"
app:resize_mode="zoom"
app:player_layout_id="@layout/layout_simple_exo_player_view"
android:orientation="vertical">
</com.google.android.exoplayer2.ui.PlayerView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<ImageView
android:id="@+id/blur_background"
<com.widget.imagevideobanner.view.SampleCoverVideo
android:id="@+id/player"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionName="blur_image"
android:scaleType="centerCrop"
tools:background="@drawable/guide_defaut" />
android:layout_height="match_parent" />
<!-- 真正能够播放展示视频画面的view 会动态的添加到这里-->
<ImageView
android:id="@+id/cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerInside"
android:transitionName="cover" />
<ProgressBar
android:id="@+id/buffer_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:indeterminateTint="#CAE7DF"
android:transitionName="buffer_view"
android:visibility="visible"/>
</merge>
\ No newline at end of file
</FrameLayout>
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment