Here are the examples of the java api android.util.SparseArray taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.
2069 Examples
19
Source : StateListImageButton.java
with MIT License
from zzhoujay
with MIT License
from zzhoujay
/**
* Created by zhou on 2016/11/29.
* 可以添加状态的ImageButton
*/
public clreplaced StateListImageButton extends AppCompatImageButton {
private SparseArray<Drawable> drawableSparseArray;
private int state;
private StateChangeCallback stateChangeCallback;
public StateListImageButton(Context context) {
super(context);
init(context);
}
public StateListImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public StateListImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
drawableSparseArray = new SparseArray<>();
}
private void setBackground() {
Drawable drawable = drawableSparseArray.get(state);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
setBackground(drawable);
} else {
setBackgroundDrawable(drawable);
}
}
public void addState(int state, Drawable drawable) {
drawableSparseArray.put(state, drawable);
setBackground();
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
setBackground();
if (stateChangeCallback != null) {
stateChangeCallback.stateChange(state);
}
}
public StateChangeCallback getStateChangeCallback() {
return stateChangeCallback;
}
public void setStateChangeCallback(StateChangeCallback stateChangeCallback) {
this.stateChangeCallback = stateChangeCallback;
}
}
19
Source : Style.java
with MIT License
from zzhoujay
with MIT License
from zzhoujay
/**
* Created by zhou on 2016/11/26.
* Style
*/
public abstract clreplaced Style<T extends Styleable> {
private static final Style<BoldSpan> BOLD = new Style<BoldSpan>() {
@Override
public Clreplaced<BoldSpan> getSpanClreplaced() {
return BoldSpan.clreplaced;
}
@Override
public BoldSpan createSpan() {
return new BoldSpan();
}
@Override
public int getStyleType() {
return StyleType.BOLD;
}
};
private static final Style<ItalicSpan> ITALIC = new Style<ItalicSpan>() {
@Override
public Clreplaced<ItalicSpan> getSpanClreplaced() {
return ItalicSpan.clreplaced;
}
@Override
public ItalicSpan createSpan() {
return new ItalicSpan();
}
@Override
public int getStyleType() {
return StyleType.ITALIC;
}
};
private static final Style<DeleteSpan> DELETE = new Style<DeleteSpan>() {
@Override
public Clreplaced<DeleteSpan> getSpanClreplaced() {
return DeleteSpan.clreplaced;
}
@Override
public DeleteSpan createSpan() {
return new DeleteSpan();
}
@Override
public int getStyleType() {
return StyleType.DELETE;
}
};
private static final Style<UnderLineSpan> UNDER_LINE = new Style<UnderLineSpan>() {
@Override
public Clreplaced<UnderLineSpan> getSpanClreplaced() {
return UnderLineSpan.clreplaced;
}
@Override
public UnderLineSpan createSpan() {
return new UnderLineSpan();
}
@Override
public int getStyleType() {
return StyleType.UNDER_LINE;
}
};
private static final Style<QuoteSpan> QUOTE = new Style<QuoteSpan>() {
@Override
public Clreplaced<QuoteSpan> getSpanClreplaced() {
return QuoteSpan.clreplaced;
}
@Override
public QuoteSpan createSpan() {
return new QuoteSpan();
}
@Override
public int getStyleType() {
return StyleType.QUOTE;
}
@Override
public boolean paragraph() {
return true;
}
};
private static final Style<LinkSpan> LINK = new Style<LinkSpan>() {
@Override
public Clreplaced<LinkSpan> getSpanClreplaced() {
return LinkSpan.clreplaced;
}
@Override
public LinkSpan createSpan() {
return null;
}
@Override
public int getStyleType() {
return StyleType.LINK;
}
@Override
public LinkSpan createSpan(Object... args) {
return new LinkSpan(args[0].toString());
}
@Override
public boolean needArgument() {
return true;
}
};
private static final Style<ImageSpan> IMAGE = new Style<ImageSpan>() {
@Override
public Clreplaced<ImageSpan> getSpanClreplaced() {
return ImageSpan.clreplaced;
}
@Override
public ImageSpan createSpan() {
return null;
}
@Override
public int getStyleType() {
return StyleType.IMAGE;
}
@Override
public ImageSpan createSpan(Object... args) {
return new ImageSpan(((Drawable) args[1]), args[0].toString());
}
@Override
public boolean needArgument() {
return true;
}
};
private static final Style<CodeSpan> CODE = new Style<CodeSpan>() {
@Override
public Clreplaced<CodeSpan> getSpanClreplaced() {
return CodeSpan.clreplaced;
}
@Override
public CodeSpan createSpan() {
return new CodeSpan();
}
@Override
public int getStyleType() {
return StyleType.CODE;
}
};
private static final Style<HeadSpan> HEAD = new StyleExt<HeadSpan>() {
@Override
public Clreplaced<HeadSpan> getSpanClreplaced() {
return HeadSpan.clreplaced;
}
@Override
public HeadSpan createSpan() {
return null;
}
@Override
public int getStyleType() {
return StyleType.HEAD;
}
@Override
public HeadSpan createSpan(Object... args) {
return new HeadSpan(((int) args[0]));
}
@Override
public boolean needArgument() {
return true;
}
@Override
public void beforeStyle(SpannableStringBuilder ssb, int start, int end) {
HeadSpan[] headSpans = ssb.getSpans(start, end, HeadSpan.clreplaced);
for (HeadSpan headSpan : headSpans) {
int s = ssb.getSpanStart(headSpan);
int e = ssb.getSpanEnd(headSpan);
ssb.removeSpan(headSpan);
ssb.setSpan(headSpan, s, e, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
@Override
public int getStyleTypeState(int flag, HeadSpan headSpan) {
int state;
if (flag == Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) {
state = StyleTypeStateSpec.StyleTypeState.STATE_EXIST;
} else {
state = StyleTypeStateSpec.StyleTypeState.STATE_ACTIVE;
}
byte arg1 = (byte) headSpan.getHeadSize();
return StyleTypeStateSpec.makeStyleTypeStateSpec(headSpan.getStyleType(), state, arg1);
}
};
private static final Style<ColorSpan> COLOR = new Style<ColorSpan>() {
@Override
public Clreplaced<ColorSpan> getSpanClreplaced() {
return ColorSpan.clreplaced;
}
@Override
public ColorSpan createSpan() {
return null;
}
@Override
public int getStyleType() {
return StyleType.COLOR;
}
@Override
public boolean needArgument() {
return true;
}
@Override
public ColorSpan createSpan(Object... args) {
return new ColorSpan((Integer) args[0]);
}
};
public abstract Clreplaced<T> getSpanClreplaced();
public abstract T createSpan();
@StyleType
public abstract int getStyleType();
public T createSpan(Object... args) {
return createSpan();
}
public boolean needArgument() {
return false;
}
public boolean paragraph() {
return false;
}
private static final SparseArray<Style> styles;
private static void register(Style style) {
styles.append(style.getStyleType(), style);
}
static {
styles = new SparseArray<>();
register(BOLD);
register(ITALIC);
register(DELETE);
register(UNDER_LINE);
register(QUOTE);
register(LINK);
register(IMAGE);
register(CODE);
register(HEAD);
register(COLOR);
}
public static Style get(Styleable styleable) {
return styles.get(styleable.getStyleType());
}
public static Style get(@StyleType int styleType) {
return styles.get(styleType);
}
}
19
Source : DayWheelView.java
with Apache License 2.0
from zyyoona7
with Apache License 2.0
from zyyoona7
/**
* 日 WheelView
*
* @author zyyoona7
* @version v1.0.0
* @since 2018/8/20.
*/
public clreplaced DayWheelView extends WheelView<Integer> {
private static final SparseArray<List<Integer>> DAYS = new SparseArray<>(1);
private int mYear;
private int mMonth;
private int mMaxYear = -1;
private int mMinYear = -1;
private int mMaxMonth = -1;
private int mMinMonth = -1;
private int mMaxDay = -1;
private int mMinDay = -1;
private Calendar mCalendar;
public DayWheelView(Context context) {
this(context, null);
}
public DayWheelView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DayWheelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mCalendar = Calendar.getInstance();
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DayWheelView);
mYear = typedArray.getInt(R.styleable.DayWheelView_wv_year, mCalendar.get(Calendar.YEAR));
mMonth = typedArray.getInt(R.styleable.DayWheelView_wv_month, mCalendar.get(Calendar.MONTH) + 1);
int selectedDay = typedArray.getInt(R.styleable.DayWheelView_wv_selectedDay, mCalendar.get(Calendar.DATE));
typedArray.recycle();
updateDay();
setSelectedDay(selectedDay);
}
/**
* 同时设置年份和月份
*
* @param year 年份
* @param month 月份
*/
public void setYearAndMonth(int year, int month) {
mYear = year;
mMonth = month;
updateDay();
}
/**
* 设置年份
*
* @param year 年份
*/
public void setYear(int year) {
mYear = year;
updateDay();
}
/**
* 获取年份
*
* @return 年份
*/
public int getYear() {
return mYear;
}
/**
* 设置月份
*
* @param month 月份
*/
public void setMonth(int month) {
mMonth = month;
updateDay();
}
/**
* 获取月份
*
* @return 月份
*/
public int getMonth() {
return mMonth;
}
public void setMaxYearMonthAndDay(@IntRange(from = 0) int maxYear, @IntRange(from = 1, to = 12) int maxMonth, @IntRange(from = 1, to = 31) int maxDay) {
mMaxYear = maxYear;
mMaxMonth = maxMonth;
mMaxDay = maxDay;
checkCurrentSelected(getSelectedItemData());
}
public void setMinYearMonthAndDay(@IntRange(from = 0) int minYear, @IntRange(from = 1, to = 12) int minMonth, @IntRange(from = 1, to = 31) int minDay) {
mMinYear = minYear;
mMinMonth = minMonth;
mMinDay = minDay;
checkCurrentSelected(getSelectedItemData());
}
/**
* 更新数据
*/
private void updateDay() {
mCalendar.set(Calendar.YEAR, mYear);
mCalendar.set(Calendar.MONTH, mMonth - 1);
mCalendar.set(Calendar.DATE, 1);
mCalendar.roll(Calendar.DATE, -1);
int days = mCalendar.get(Calendar.DATE);
List<Integer> data = DAYS.get(days);
if (data == null) {
data = new ArrayList<>(1);
for (int i = 1; i <= days; i++) {
data.add(i);
}
DAYS.put(days, data);
}
super.setData(data);
checkCurrentSelected(getSelectedItemData());
}
/**
* 获取选中的日
*
* @return 选中的日
*/
public int getSelectedDay() {
return getSelectedItemData();
}
/**
* 设置选中的日
*
* @param selectedDay 选中的日
*/
public void setSelectedDay(int selectedDay) {
setSelectedDay(selectedDay, false);
}
/**
* 设置选中的日
*
* @param selectedDay 选中的日
* @param isSmoothScroll 是否平滑滚动
*/
public void setSelectedDay(int selectedDay, boolean isSmoothScroll) {
setSelectedDay(selectedDay, isSmoothScroll, 0);
}
/**
* 设置选中的日
*
* @param selectedDay 选中的日
* @param isSmoothScroll 是否平滑滚动
* @param smoothDuration 平滑滚动持续时间
*/
public void setSelectedDay(int selectedDay, boolean isSmoothScroll, int smoothDuration) {
int days = mCalendar.get(Calendar.DATE);
if (selectedDay >= 1 && selectedDay <= days) {
int shouldSelected = selectedDay;
if (isMoreThanMaxDay(selectedDay)) {
shouldSelected = mMaxDay;
} else if (isLessThanMinDay(selectedDay)) {
shouldSelected = mMinDay;
}
updateSelectedDay(shouldSelected, isSmoothScroll, smoothDuration);
}
}
/**
* 更新选中的日
*
* @param selectedDay 选中的日
* @param isSmoothScroll 是否平滑滚动
* @param smoothDuration 平滑滚动持续时间
*/
private void updateSelectedDay(int selectedDay, boolean isSmoothScroll, int smoothDuration) {
setSelectedItemPosition(selectedDay - 1, isSmoothScroll, smoothDuration);
}
@Override
public void setData(List<Integer> dataList) {
throw new UnsupportedOperationException("You can not invoke setData method in " + DayWheelView.clreplaced.getSimpleName() + ".");
}
@Override
protected void onItemSelected(Integer data, int position) {
checkCurrentSelected(data);
}
private void checkCurrentSelected(int data) {
if (isMoreThanMaxDay(data)) {
setSelectedDay(mMaxDay);
} else if (isLessThanMinDay(data)) {
setSelectedDay(mMinDay);
}
}
private boolean isMoreThanMaxDay(int data) {
return isCurrentMaxYear() && isCurrentMaxMonth() && data > mMaxDay && mMaxDay > 0;
}
private boolean isLessThanMinDay(int data) {
return isCurrentMinYear() && isCurrentMinMonth() && data < mMinDay && mMinDay > 0;
}
private boolean isCurrentMaxYear() {
return (mMaxYear > 0 && mYear == mMaxYear) || (mYear < 0 && mMaxYear < 0 && mMinYear < 0);
}
private boolean isCurrentMinYear() {
return (mYear == mMinYear && mMinYear > 0) || (mYear < 0 && mMaxYear < 0 && mMinYear < 0);
}
private boolean isCurrentMaxMonth() {
return (mMaxMonth > 0 && mMonth == mMaxMonth) || (mMonth < 0 && mMaxMonth < 0 && mMinMonth < 0);
}
private boolean isCurrentMinMonth() {
return (mMonth == mMinMonth && mMinMonth > 0) || (mMonth < 0 && mMaxMonth < 0 && mMinMonth < 0);
}
}
19
Source : HeartView.java
with Apache License 2.0
from zyyoona7
with Apache License 2.0
from zyyoona7
/**
* Created by zyyoona7 on 2017/7/3.
* 直播平台进入直播间点亮功能的心形view
*/
public clreplaced HeartView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private static final String TAG = "HeartView";
private boolean isRunning = false;
private Paint mPaint;
private ConcurrentLinkedQueue<Heart> mHearts = null;
private float mWidth;
private float mHeight;
private Matrix mMatrix;
private SparseArray<Bitmap> mBitmapSparseArray;
public HeartView(Context context) {
this(context, null);
}
public HeartView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
getHolder().addCallback(this);
// 设置背景透明
setZOrderOnTop(true);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
// ----
setFocusable(true);
setKeepScreenOn(true);
setFocusableInTouchMode(true);
mHearts = new ConcurrentLinkedQueue<>();
mMatrix = new Matrix();
mBitmapSparseArray = new SparseArray<>();
initBitmap(context);
}
/**
* 初始化bitmap
*
* @param context
*/
private void initBitmap(Context context) {
Bitmap bitmap1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart_default);
Bitmap bitmap2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart1);
Bitmap bitmap3 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart2);
Bitmap bitmap4 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart3);
Bitmap bitmap5 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart4);
Bitmap bitmap6 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart5);
mBitmapSparseArray.put(Heart.DEFAULT, bitmap1);
mBitmapSparseArray.put(Heart.PINK, bitmap2);
mBitmapSparseArray.put(Heart.CYAN, bitmap3);
mBitmapSparseArray.put(Heart.GREEN, bitmap4);
mBitmapSparseArray.put(Heart.YELLOW, bitmap5);
mBitmapSparseArray.put(Heart.BLUE, bitmap6);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
mHeight = getHeight();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isRunning = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
// 回收bitmap
for (int i = 0; i < mBitmapSparseArray.size(); i++) {
if (mBitmapSparseArray.valueAt(i) != null) {
mBitmapSparseArray.valueAt(i).recycle();
}
}
}
@Override
public void run() {
while (isRunning) {
Canvas canvas = null;
try {
canvas = getHolder().lockCanvas();
if (canvas != null) {
drawHeart(canvas);
}
} catch (Exception e) {
Log.e(TAG, "run: " + e.getMessage());
} finally {
if (canvas != null) {
getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
/**
* 画集合内的心形
*
* @param canvas
*/
private void drawHeart(Canvas canvas) {
// 清屏~
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (Heart heart : mHearts) {
if (mBitmapSparseArray.get(heart.getType()) == null) {
continue;
}
// 会覆盖掉之前的x,y数值
mMatrix.setTranslate(0, 0);
// 位移到x,y
mMatrix.postTranslate(heart.getX(), heart.getY());
// 缩放
// mMatrix.postScale();
// 旋转
// mMatrix.postRotate();
// 画bitmap
canvas.drawBitmap(mBitmapSparseArray.get(heart.getType()), mMatrix, mPaint);
// 计算时间
if (heart.getT() < 1) {
heart.setT(heart.getT() + heart.getSpeed());
// 计算下次画的时候,x,y坐标
handleBezierXY(heart);
} else {
removeHeart(heart);
}
}
}
/**
* 计算实时的点坐标
*
* @param heart
*/
private void handleBezierXY(Heart heart) {
// 三阶贝塞尔曲线函数
// x = (float) (Math.pow((1 - t), 3) * start.x + 3 * t * Math.pow((1 - t), 2) * control1.x + 3 * Math.pow(t, 2) * (1 - t) * control2.x + Math.pow(t, 3) * end.x);
// y = (float) (Math.pow((1 - t), 3) * start.y + 3 * t * Math.pow((1 - t), 2) * control1.y + 3 * Math.pow(t, 2) * (1 - t) * control2.y + Math.pow(t, 3) * end.y);
float x = (float) (Math.pow((1 - heart.getT()), 3) * heart.getStartX() + 3 * heart.getT() * Math.pow((1 - heart.getT()), 2) * heart.getControl1X() + 3 * Math.pow(heart.getT(), 2) * (1 - heart.getT()) * heart.getControl2X() + Math.pow(heart.getT(), 3) * heart.getEndX());
float y = (float) (Math.pow((1 - heart.getT()), 3) * heart.getStartY() + 3 * heart.getT() * Math.pow((1 - heart.getT()), 2) * heart.getControl1Y() + 3 * Math.pow(heart.getT(), 2) * (1 - heart.getT()) * heart.getControl2Y() + Math.pow(heart.getT(), 3) * heart.getEndY());
heart.setX(x);
heart.setY(y);
}
/**
* 添加heart
*/
public void addHeart() {
Heart heart = new Heart();
initHeart(heart);
mHearts.add(heart);
}
/**
* 添加heart
*
* @param type
*/
public void addHeart(int type) {
Heart heart = new Heart();
initHeart(heart);
heart.setType(type);
mHearts.add(heart);
}
/**
* 重置
*
* @param heart
*/
private void initHeart(Heart heart) {
heart.initStartAndEnd(mWidth, mHeight);
heart.initControl(mWidth, mHeight);
heart.initSpeed();
}
/**
* 移除Heart
*
* @param heart
*/
private void removeHeart(Heart heart) {
mHearts.remove(heart);
}
}
19
Source : CustomLayoutManager.java
with Apache License 2.0
from zyyoona7
with Apache License 2.0
from zyyoona7
/**
* Created by zyyoona7 on 2016/9/22.
*/
public clreplaced CustomLayoutManager extends RecyclerView.LayoutManager {
private static final String TAG = "CustomLayoutManager";
// 保存所有的Item的上下左右的偏移量信息
private SparseArray<Rect> mAllItemsFrames;
private int mHorizontalScrollOffset = 0;
private int mTotalWidth = 0;
// 每页的行列数
private int mSpanColumns = 1;
private int mSpanRows = 1;
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
// 如果没有item,直接返回
if (gereplacedemCount() <= 0)
return;
// 跳过preLayout,preLayout主要用于支持动画
if (state.isPreLayout()) {
return;
}
// 在布局之前,将所有的子View先Detach掉,放入到Scrap缓存中
detachAndScrapAttachedViews(recycler);
mAllItemsFrames = new SparseArray<>(gereplacedemCount());
int currentWidth = 0;
int currentHeight = 0;
int currentPage = 1;
int maxCurrentWidth = 0;
View scrap = recycler.getViewForPosition(0);
addView(scrap);
measureChildWithMargins(scrap, 0, 0);
int width = getDecoratedMeasuredWidth(scrap);
int height = getDecoratedMeasuredHeight(scrap);
detachAndScrapView(scrap, recycler);
maxCurrentWidth = (getHorizontalSpace() / width) * width;
Log.e(TAG, "onLayoutChildren: itemCount=" + gereplacedemCount());
for (int i = 0; i < gereplacedemCount(); i++) {
// 这里就是从缓存里面取出
// View view = recycler.getViewForPosition(i);
// 将View加入到RecyclerView中
// addView(view);
// measureChildWithMargins(view, 0, 0);
Rect frame = mAllItemsFrames.get(i);
if (frame == null) {
frame = new Rect();
}
if (currentWidth + width > maxCurrentWidth * currentPage) {
currentHeight += height;
currentWidth = maxCurrentWidth * (currentPage - 1);
}
if (currentHeight + height > getVerticalSpace()) {
currentWidth = maxCurrentWidth * currentPage;
currentHeight = 0;
currentPage++;
}
Log.e(TAG, "onLayoutChildren: currentWidth=" + currentWidth + ",currentHeight=" + currentHeight + ",horizontalSpan=" + getHorizontalSpace() + ",verticalSpace=" + getVerticalSpace() + ",currentPage=" + currentPage + ",maxWidth=" + maxCurrentWidth);
frame.set(currentWidth, currentHeight, currentWidth + width, currentHeight + height);
// 将当前的Item的Rect边界数据保存
mAllItemsFrames.put(i, frame);
currentWidth += width;
mTotalWidth = maxCurrentWidth * currentPage;
Log.e(TAG, "onLayoutChildren: totalWidth=" + mTotalWidth);
}
mTotalWidth = Math.max(mTotalWidth, getHorizontalSpace());
recycleAndFillItems(recycler, state);
}
/**
* 回收不需要的Item,并且将需要显示的Item从缓存中取出
*/
private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout()) {
// 跳过preLayout,preLayout主要用于支持动画
return;
}
// 当前scroll offset状态下的显示区域
Rect displayFrame = new Rect(mHorizontalScrollOffset, 0, mHorizontalScrollOffset + getHorizontalSpace(), getVerticalSpace());
/**
* 将滑出屏幕的Items回收到Recycle缓存中
*/
Rect childFrame = new Rect();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
childFrame.left = getDecoratedLeft(child);
childFrame.top = getDecoratedTop(child);
childFrame.right = getDecoratedRight(child);
childFrame.bottom = getDecoratedBottom(child);
// 如果Item没有在显示区域,就说明需要回收
if (!Rect.intersects(displayFrame, childFrame)) {
// 回收掉滑出屏幕的View
removeAndRecycleView(child, recycler);
}
}
// 重新显示需要出现在屏幕的子View
for (int i = 0; i < gereplacedemCount(); i++) {
if (Rect.intersects(displayFrame, mAllItemsFrames.get(i))) {
View scrap = recycler.getViewForPosition(i);
measureChildWithMargins(scrap, 0, 0);
addView(scrap);
Rect frame = mAllItemsFrames.get(i);
// 将这个item布局出来
layoutDecorated(scrap, frame.left - mHorizontalScrollOffset, frame.top, frame.right - mHorizontalScrollOffset, frame.bottom);
}
}
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
// 先detach掉所有的子View
detachAndScrapAttachedViews(recycler);
// 实际要滑动的距离
int travel = dx;
//
// 如果滑动到最左面
if (mHorizontalScrollOffset + dx < 0) {
travel = -mHorizontalScrollOffset;
} else if (mHorizontalScrollOffset + dx > mTotalWidth - getHorizontalSpace()) {
// 如果滑动到最右部
travel = mTotalWidth - getHorizontalSpace() - mHorizontalScrollOffset;
}
//
// 将水平方向的偏移量+travel
mHorizontalScrollOffset += travel;
offsetChildrenHorizontal(-travel);
recycleAndFillItems(recycler, state);
Log.e(TAG, "scrollHorizontallyBy: childCount=" + getChildCount());
return travel;
}
@Override
public boolean canScrollHorizontally() {
return true;
}
private int getVerticalSpace() {
return getHeight() - getPaddingBottom() - getPaddingTop();
}
private int getHorizontalSpace() {
return getWidth() - getPaddingRight() - getPaddingLeft();
}
}
19
Source : HeartView.java
with Apache License 2.0
from zyyoona7
with Apache License 2.0
from zyyoona7
/**
* Created by zyyoona7 on 2017/7/3.
* 直播平台进入直播间点亮功能的心形view
*/
public clreplaced HeartView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private static final String TAG = "HeartView";
private boolean isRunning = false;
private Paint mPaint;
private ConcurrentLinkedQueue<Heart> mHearts = null;
private float mWidth;
private float mHeight;
private Matrix mMatrix;
private SparseArray<Bitmap> mBitmapSparseArray;
public HeartView(Context context) {
this(context, null);
}
public HeartView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(10);
getHolder().addCallback(this);
// 设置背景透明
setZOrderOnTop(true);
getHolder().setFormat(PixelFormat.TRANSLUCENT);
// ----
setFocusable(true);
setKeepScreenOn(true);
setFocusableInTouchMode(true);
mHearts = new ConcurrentLinkedQueue<>();
mMatrix = new Matrix();
mBitmapSparseArray = new SparseArray<>();
Bitmap bitmap1 = BitmapFactory.decodeResource(context.getResources(), R.drawable.heart_default);
Bitmap bitmap2 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart1);
Bitmap bitmap3 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart2);
Bitmap bitmap4 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart3);
Bitmap bitmap5 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart4);
Bitmap bitmap6 = BitmapFactory.decodeResource(context.getResources(), R.drawable.ss_heart5);
mBitmapSparseArray.put(Heart.DEFAULT, bitmap1);
mBitmapSparseArray.put(Heart.PINK, bitmap2);
mBitmapSparseArray.put(Heart.CYAN, bitmap3);
mBitmapSparseArray.put(Heart.GREEN, bitmap4);
mBitmapSparseArray.put(Heart.YELLOW, bitmap5);
mBitmapSparseArray.put(Heart.BLUE, bitmap6);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
mHeight = getHeight();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isRunning = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
// 回收bitmap
for (int i = 0; i < mBitmapSparseArray.size(); i++) {
if (mBitmapSparseArray.valueAt(i) != null) {
mBitmapSparseArray.valueAt(i).recycle();
}
}
}
@Override
public void run() {
while (isRunning) {
Canvas canvas = null;
try {
canvas = getHolder().lockCanvas();
if (canvas != null) {
// 清屏~
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (Heart heart : mHearts) {
if (mBitmapSparseArray.get(heart.getType()) == null) {
continue;
}
mMatrix.setTranslate(0, 0);
// 位移到x,y
mMatrix.postTranslate(heart.getX(), heart.getY());
// 缩放
// mMatrix.postScale()
// 画bitmap
canvas.drawBitmap(mBitmapSparseArray.get(heart.getType()), mMatrix, mPaint);
// 计算时间
if (heart.getT() < 1) {
heart.setT(heart.getT() + heart.getSpeed());
handleBezierXY(heart);
} else {
removeHeart(heart);
}
}
}
} catch (Exception e) {
Log.e(TAG, "run: " + e.getMessage());
} finally {
if (canvas != null) {
getHolder().unlockCanvasAndPost(canvas);
}
}
}
}
/**
* 计算实时的点坐标
*
* @param heart
*/
private void handleBezierXY(Heart heart) {
// 三阶贝塞尔曲线函数
// x = (float) (Math.pow((1 - t), 3) * start.x + 3 * t * Math.pow((1 - t), 2) * control1.x + 3 * Math.pow(t, 2) * (1 - t) * control2.x + Math.pow(t, 3) * end.x);
// y = (float) (Math.pow((1 - t), 3) * start.y + 3 * t * Math.pow((1 - t), 2) * control1.y + 3 * Math.pow(t, 2) * (1 - t) * control2.y + Math.pow(t, 3) * end.y);
float x = (float) (Math.pow((1 - heart.getT()), 3) * heart.getStartX() + 3 * heart.getT() * Math.pow((1 - heart.getT()), 2) * heart.getControl1X() + 3 * Math.pow(heart.getT(), 2) * (1 - heart.getT()) * heart.getControl2X() + Math.pow(heart.getT(), 3) * heart.getEndX());
float y = (float) (Math.pow((1 - heart.getT()), 3) * heart.getStartY() + 3 * heart.getT() * Math.pow((1 - heart.getT()), 2) * heart.getControl1Y() + 3 * Math.pow(heart.getT(), 2) * (1 - heart.getT()) * heart.getControl2Y() + Math.pow(heart.getT(), 3) * heart.getEndY());
heart.setX(x);
heart.setY(y);
}
/**
* 添加heart
*/
public void addHeart() {
Heart heart = new Heart();
initHeart(heart);
mHearts.add(heart);
}
/**
* 添加heart
*
* @param type
*/
public void addHeart(int type) {
Heart heart = new Heart();
initHeart(heart);
heart.setType(type);
mHearts.add(heart);
}
/**
* 重置
*
* @param heart
*/
private void initHeart(Heart heart) {
heart.initStartAndEnd(mWidth, mHeight);
heart.initControl(mWidth, mHeight);
heart.initSpeed();
}
/**
* 移除Heart
*
* @param heart
*/
private void removeHeart(Heart heart) {
mHearts.remove(heart);
}
}
19
Source : CartProvider.java
with Apache License 2.0
from zxuu
with Apache License 2.0
from zxuu
/**
* Created by <a href="http://www.cniao5.com">菜鸟窝</a>
* 一个专业的Android开发在线教育平台
*/
public clreplaced CartProvider {
public static final String CART_JSON = "cart_json";
private SparseArray<ShoppingCart> datas = null;
private Context mContext;
public CartProvider(Context context) {
mContext = context;
// 存储到SparseArray
datas = new SparseArray<>(10);
// 从本地读入到内存
listToSparse();
}
public void put(ShoppingCart cart) {
// key
ShoppingCart temp = datas.get(cart.getId().intValue());
// 如果购物车里存在
if (temp != null) {
temp.setCount(temp.getCount() + 1);
} else {
temp = cart;
temp.setCount(1);
}
datas.put(cart.getId().intValue(), temp);
commit();
}
public void update(ShoppingCart cart) {
datas.put(cart.getId().intValue(), cart);
commit();
}
public void delete(ShoppingCart cart) {
datas.delete(cart.getId().intValue());
commit();
}
public List<ShoppingCart> getAll() {
return getDataFromLocal();
}
public void commit() {
List<ShoppingCart> carts = sparseToList();
// 存储到本地
PreferencesUtils.putString(mContext, CART_JSON, JSONUtil.toJSON(carts));
}
// SparseArray里的数据转换为List
private List<ShoppingCart> sparseToList() {
int size = datas.size();
List<ShoppingCart> list = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
list.add(datas.valueAt(i));
}
return list;
}
// 保存到SparseArray
private void listToSparse() {
List<ShoppingCart> carts = getDataFromLocal();
if (carts != null && carts.size() > 0) {
for (ShoppingCart cart : carts) {
datas.put(cart.getId().intValue(), cart);
}
}
}
public List<ShoppingCart> getDataFromLocal() {
String json = PreferencesUtils.getString(mContext, CART_JSON);
List<ShoppingCart> carts = null;
if (json != null) {
carts = JSONUtil.fromJson(json, new TypeToken<List<ShoppingCart>>() {
}.getType());
}
return carts;
}
}
19
Source : BaseViewHolder.java
with Apache License 2.0
from zxuu
with Apache License 2.0
from zxuu
public clreplaced BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private SparseArray<View> views;
protected BaseAdapter.OnItemClickListener mOnItemClickListener;
public BaseViewHolder(View itemView, BaseAdapter.OnItemClickListener onItemClickListener) {
super(itemView);
itemView.setOnClickListener(this);
this.mOnItemClickListener = onItemClickListener;
this.views = new SparseArray<View>();
}
public TextView getTextView(int viewId) {
return retrieveView(viewId);
}
public Button getButton(int viewId) {
return retrieveView(viewId);
}
public ImageView getImageView(int viewId) {
return retrieveView(viewId);
}
public View getView(int viewId) {
return retrieveView(viewId);
}
protected <T extends View> T retrieveView(int viewId) {
View view = views.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(v, getLayoutPosition());
}
}
}
19
Source : NatSessionManager.java
with GNU General Public License v3.0
from zxc111
with GNU General Public License v3.0
from zxc111
public clreplaced NatSessionManager {
static final int MAX_SESSION_COUNT = 60;
static final long SESSION_TIMEOUT_NS = 60 * 1000000000L;
static final SparseArray<NatSession> Sessions = new SparseArray<NatSession>();
public static NatSession getSession(int portKey) {
return Sessions.get(portKey);
}
public static int getSessionCount() {
return Sessions.size();
}
static void clearExpiredSessions() {
long now = System.nanoTime();
for (int i = Sessions.size() - 1; i >= 0; i--) {
NatSession session = Sessions.valueAt(i);
if (now - session.LastNanoTime > SESSION_TIMEOUT_NS) {
Sessions.removeAt(i);
}
}
}
public static NatSession createSession(int portKey, int remoteIP, short remotePort) {
if (Sessions.size() > MAX_SESSION_COUNT) {
// 清理过期的会话。
clearExpiredSessions();
}
NatSession session = new NatSession();
session.LastNanoTime = System.nanoTime();
session.RemoteIP = remoteIP;
session.RemotePort = remotePort;
if (ProxyConfig.isFakeIP(remoteIP)) {
session.RemoteHost = DnsProxy.reverseLookup(remoteIP);
}
if (session.RemoteHost == null) {
session.RemoteHost = CommonMethods.ipIntToString(remoteIP);
}
Sessions.put(portKey, session);
return session;
}
}
19
Source : StickyRecyclerHeadersDecoration.java
with Apache License 2.0
from zuoweitan
with Apache License 2.0
from zuoweitan
public clreplaced StickyRecyclerHeadersDecoration extends RecyclerView.ItemDecoration {
private final StickyRecyclerHeadersAdapter mAdapter;
private final SparseArray<Rect> mHeaderRects = new SparseArray<>();
private final HeaderProvider mHeaderProvider;
private final OrientationProvider mOrientationProvider;
private final HeaderPositionCalculator mHeaderPositionCalculator;
private final HeaderRenderer mRenderer;
private final DimensionCalculator mDimensionCalculator;
/**
* The following field is used as a buffer for internal calculations. Its sole purpose is to avoid
* allocating new Rect every time we need one.
*/
private final Rect mTempRect = new Rect();
// TODO: Consider preplaceding in orientation to simplify orientation accounting within calculation
public StickyRecyclerHeadersDecoration(StickyRecyclerHeadersAdapter adapter) {
this(adapter, new LinearLayoutOrientationProvider(), new DimensionCalculator());
}
private StickyRecyclerHeadersDecoration(StickyRecyclerHeadersAdapter adapter, OrientationProvider orientationProvider, DimensionCalculator dimensionCalculator) {
this(adapter, orientationProvider, dimensionCalculator, new HeaderRenderer(orientationProvider), new HeaderViewCache(adapter, orientationProvider));
}
private StickyRecyclerHeadersDecoration(StickyRecyclerHeadersAdapter adapter, OrientationProvider orientationProvider, DimensionCalculator dimensionCalculator, HeaderRenderer headerRenderer, HeaderProvider headerProvider) {
this(adapter, headerRenderer, orientationProvider, dimensionCalculator, headerProvider, new HeaderPositionCalculator(adapter, headerProvider, orientationProvider, dimensionCalculator));
}
private StickyRecyclerHeadersDecoration(StickyRecyclerHeadersAdapter adapter, HeaderRenderer headerRenderer, OrientationProvider orientationProvider, DimensionCalculator dimensionCalculator, HeaderProvider headerProvider, HeaderPositionCalculator headerPositionCalculator) {
mAdapter = adapter;
mHeaderProvider = headerProvider;
mOrientationProvider = orientationProvider;
mRenderer = headerRenderer;
mDimensionCalculator = dimensionCalculator;
mHeaderPositionCalculator = headerPositionCalculator;
}
@Override
public void gereplacedemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.gereplacedemOffsets(outRect, view, parent, state);
int itemPosition = parent.getChildAdapterPosition(view);
if (itemPosition == RecyclerView.NO_POSITION) {
return;
}
if (mHeaderPositionCalculator.hasNewHeader(itemPosition, mOrientationProvider.isReverseLayout(parent))) {
View header = getHeaderView(parent, itemPosition);
sereplacedemOffsetsForHeader(outRect, header, mOrientationProvider.getOrientation(parent));
}
}
/**
* Sets the offsets for the first item in a section to make room for the header view
*
* @param itemOffsets rectangle to define offsets for the item
* @param header view used to calculate offset for the item
* @param orientation used to calculate offset for the item
*/
private void sereplacedemOffsetsForHeader(Rect itemOffsets, View header, int orientation) {
mDimensionCalculator.initMargins(mTempRect, header);
if (orientation == LinearLayoutManager.VERTICAL) {
itemOffsets.top = header.getHeight() + mTempRect.top + mTempRect.bottom;
} else {
itemOffsets.left = header.getWidth() + mTempRect.left + mTempRect.right;
}
}
@Override
public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(canvas, parent, state);
final int childCount = parent.getChildCount();
if (childCount <= 0 || mAdapter.gereplacedemCount() <= 0) {
return;
}
for (int i = 0; i < childCount; i++) {
View itemView = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(itemView);
if (position == RecyclerView.NO_POSITION) {
continue;
}
boolean hreplacedtickyHeader = mHeaderPositionCalculator.hreplacedtickyHeader(itemView, mOrientationProvider.getOrientation(parent), position);
if (hreplacedtickyHeader || mHeaderPositionCalculator.hasNewHeader(position, mOrientationProvider.isReverseLayout(parent))) {
View header = mHeaderProvider.getHeader(parent, position);
// re-use existing Rect, if any.
Rect headerOffset = mHeaderRects.get(position);
if (headerOffset == null) {
headerOffset = new Rect();
mHeaderRects.put(position, headerOffset);
}
mHeaderPositionCalculator.initHeaderBounds(headerOffset, parent, header, itemView, hreplacedtickyHeader);
mRenderer.drawHeader(parent, canvas, header, headerOffset);
}
}
}
/**
* Gets the position of the header under the specified (x, y) coordinates.
*
* @param x x-coordinate
* @param y y-coordinate
* @return position of header, or -1 if not found
*/
public int findHeaderPositionUnder(int x, int y) {
for (int i = 0; i < mHeaderRects.size(); i++) {
Rect rect = mHeaderRects.get(mHeaderRects.keyAt(i));
if (rect.contains(x, y)) {
return mHeaderRects.keyAt(i);
}
}
return -1;
}
/**
* Gets the header view for the replacedociated position. If it doesn't exist yet, it will be
* created, measured, and laid out.
*
* @param parent
* @param position
* @return Header view
*/
public View getHeaderView(RecyclerView parent, int position) {
return mHeaderProvider.getHeader(parent, position);
}
/**
* Invalidates cached headers. This does not invalidate the recyclerview, you should do that manually after
* calling this method.
*/
public void invalidateHeaders() {
mHeaderProvider.invalidate();
mHeaderRects.clear();
}
}
19
Source : BaseAutoAdapter.java
with Apache License 2.0
from Zuluft
with Apache License 2.0
from Zuluft
@SuppressWarnings({ "WeakerAccess" })
public abstract clreplaced BaseAutoAdapter<T extends IRenderer> extends RecyclerView.Adapter<AutoViewHolder> implements IAdapter<T> {
private final AutoViewHolderFactory mAutoViewHolderFactory;
private final Map<Clreplaced<? extends IRenderer>, PublishSubject> mItemViewClickBinding = new HashMap<>();
private final Map<Clreplaced<? extends IRenderer>, Map<Integer, PublishSubject>> mChildViewsClickBinding = new HashMap<>();
private final Map<Clreplaced<? extends IRenderer>, PublishSubject> mItemViewLongClickBinding = new HashMap<>();
private final Map<Clreplaced<? extends IRenderer>, Map<Integer, PublishSubject>> mChildViewsLongClickBinding = new HashMap<>();
private final SparseArray<Clreplaced<? extends IRenderer>> mLayoutRendererMapping = new SparseArray<>();
private final List<ViewHolderCreationListener> mViewHolderCreationListeners = new ArrayList<>();
public BaseAutoAdapter(final AutoViewHolderFactory autoViewHolderFactory) {
this.mAutoViewHolderFactory = autoViewHolderFactory;
}
@SuppressWarnings("unused")
public final void addViewHolderCreationListener(@NonNull final ViewHolderCreationListener viewHolderCreationListener) {
mViewHolderCreationListeners.add(viewHolderCreationListener);
}
@NonNull
@Override
public AutoViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, final int layoutId) {
AutoViewHolder viewHolder = mAutoViewHolderFactory.createViewHolder(parent, layoutId);
Clreplaced rendererClreplaced = mLayoutRendererMapping.get(layoutId);
final PublishSubject itemLongClickPublishSubject = mItemViewLongClickBinding.get(rendererClreplaced);
if (itemLongClickPublishSubject != null) {
viewHolder.getViewHolderOnLongClickObservable().subscribe(new Observer<AutoViewHolder>() {
@Override
public void onSubscribe(Disposable d) {
itemLongClickPublishSubject.onSubscribe(d);
}
@Override
@SuppressWarnings("unchecked")
public void onNext(AutoViewHolder autoViewHolder) {
int position = autoViewHolder.getAdapterPosition();
ItemInfo info = new ItemInfo(position, gereplacedem(position), autoViewHolder);
itemLongClickPublishSubject.onNext(info);
}
@SuppressWarnings("unchecked")
@Override
public void onError(Throwable e) {
itemLongClickPublishSubject.onNext(e);
}
@Override
public void onComplete() {
itemLongClickPublishSubject.onComplete();
}
});
}
final PublishSubject itemClickPublishSubject = mItemViewClickBinding.get(rendererClreplaced);
if (itemClickPublishSubject != null) {
viewHolder.getViewHolderOnClickObservable().subscribe(new Observer<AutoViewHolder>() {
@Override
public void onSubscribe(Disposable d) {
itemClickPublishSubject.onSubscribe(d);
}
@SuppressWarnings("unchecked")
@Override
public void onNext(AutoViewHolder autoViewHolder) {
int position = autoViewHolder.getAdapterPosition();
ItemInfo info = new ItemInfo(position, gereplacedem(position), autoViewHolder);
itemClickPublishSubject.onNext(info);
}
@Override
public void onError(Throwable e) {
itemClickPublishSubject.onError(e);
}
@Override
public void onComplete() {
itemClickPublishSubject.onComplete();
}
});
}
final Map<Integer, PublishSubject> childViewsLongClickMap = mChildViewsLongClickBinding.get(rendererClreplaced);
if (childViewsLongClickMap != null) {
for (Map.Entry<Integer, PublishSubject> entry : childViewsLongClickMap.entrySet()) {
final PublishSubject publishSubject = entry.getValue();
viewHolder.getViewHolderOnChildLongClickObservable(entry.getKey()).subscribe(new Observer<AutoViewHolder>() {
@Override
public void onSubscribe(Disposable d) {
publishSubject.onSubscribe(d);
}
@SuppressWarnings("unchecked")
@Override
public void onNext(AutoViewHolder autoViewHolder) {
int position = autoViewHolder.getAdapterPosition();
ItemInfo info = new ItemInfo(position, gereplacedem(position), autoViewHolder);
publishSubject.onNext(info);
}
@Override
public void onError(Throwable e) {
publishSubject.onError(e);
}
@Override
public void onComplete() {
publishSubject.onComplete();
}
});
}
}
final Map<Integer, PublishSubject> childViewsClickMap = mChildViewsClickBinding.get(rendererClreplaced);
if (childViewsClickMap != null) {
for (Map.Entry<Integer, PublishSubject> entry : childViewsClickMap.entrySet()) {
final PublishSubject publishSubject = entry.getValue();
viewHolder.getViewHolderOnChildClickObservable(entry.getKey()).subscribe(new Observer<AutoViewHolder>() {
@Override
public void onSubscribe(Disposable d) {
publishSubject.onSubscribe(d);
}
@SuppressWarnings("unchecked")
@Override
public void onNext(AutoViewHolder autoViewHolder) {
int position = autoViewHolder.getAdapterPosition();
ItemInfo info = new ItemInfo(position, gereplacedem(position), autoViewHolder);
publishSubject.onNext(info);
}
@Override
public void onError(Throwable e) {
publishSubject.onError(e);
}
@Override
public void onComplete() {
publishSubject.onComplete();
}
});
}
}
for (ViewHolderCreationListener mViewHolderCreationListener : mViewHolderCreationListeners) {
mViewHolderCreationListener.onViewHolderCreated(viewHolder);
}
return viewHolder;
}
public final <X extends AutoViewHolder, Y extends IRenderer<X>> PublishSubject<ItemInfo<Y, X>> clicks(@NonNull final Clreplaced<Y> clazz) {
PublishSubject<ItemInfo<Y, X>> publishSubject = PublishSubject.create();
mItemViewClickBinding.put(clazz, publishSubject);
return publishSubject;
}
@SuppressLint("UseSparseArrays")
public final <X extends AutoViewHolder, Y extends IRenderer<X>> PublishSubject<ItemInfo<Y, X>> clicks(@NonNull final Clreplaced<Y> clazz, @IdRes final int viewId) {
PublishSubject<ItemInfo<Y, X>> publishSubject = PublishSubject.create();
Map<Integer, PublishSubject> map = mChildViewsClickBinding.get(clazz);
if (map == null) {
map = new HashMap<>();
mChildViewsClickBinding.put(clazz, map);
}
map.put(viewId, publishSubject);
return publishSubject;
}
@SuppressWarnings("unused")
public final <X extends AutoViewHolder, Y extends IRenderer<X>> PublishSubject<ItemInfo<Y, X>> longClicks(@NonNull final Clreplaced<Y> clazz) {
PublishSubject<ItemInfo<Y, X>> publishSubject = PublishSubject.create();
mItemViewLongClickBinding.put(clazz, publishSubject);
return publishSubject;
}
@SuppressWarnings("unused")
@SuppressLint("UseSparseArrays")
public final <X extends AutoViewHolder, Y extends IRenderer<X>> PublishSubject<ItemInfo<Y, X>> longClicks(@NonNull final Clreplaced<Y> clazz, @IdRes final int viewId) {
PublishSubject<ItemInfo<Y, X>> publishSubject = PublishSubject.create();
Map<Integer, PublishSubject> map = mChildViewsLongClickBinding.get(clazz);
if (map == null) {
map = new HashMap<>();
mChildViewsLongClickBinding.put(clazz, map);
}
map.put(viewId, publishSubject);
return publishSubject;
}
@SuppressWarnings("unchecked")
@Override
public void onBindViewHolder(@NonNull final AutoViewHolder holder, final int position) {
gereplacedem(position).apply(holder);
}
@Override
@LayoutRes
public int gereplacedemViewType(final int position) {
final T item = gereplacedem(position);
final int layoutId = mAutoViewHolderFactory.getLayoutId(gereplacedem(position));
if (mLayoutRendererMapping.indexOfKey(layoutId) < 0) {
mLayoutRendererMapping.put(layoutId, item.getClreplaced());
}
return layoutId;
}
}
19
Source : BannerView.java
with GNU Affero General Public License v3.0
from zood
with GNU Affero General Public License v3.0
from zood
public clreplaced BannerView extends LinearLayout {
private final SparseArray<ItemHolder> items = new SparseArray<>();
public BannerView(Context context) {
super(context);
init();
}
public BannerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BannerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setOrientation(LinearLayout.VERTICAL);
setLayoutTransition(new LayoutTransition());
}
@UiThread
public void addItem(@NonNull String msg, @NonNull String action, int itemId, @NonNull ItemClickListener listener) {
// Check if we already have a view for this item. If so, update it
ItemHolder holder = items.get(itemId);
if (holder != null) {
holder.textView.setText(msg);
holder.button.setText(action);
holder.button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onBannerItemClick(itemId);
}
});
return;
}
// we don't have the view, so inflate a new one and add it
ConstraintLayout itemView = (ConstraintLayout) LayoutInflater.from(getContext()).inflate(R.layout.banner_item, this, false);
holder = new ItemHolder(itemView, itemId);
holder.textView.setText(msg);
holder.button.setText(action);
holder.button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onBannerItemClick(itemId);
}
});
items.put(itemId, holder);
addView(itemView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
@UiThread
public void removeItem(int itemId) {
int size = items.size();
int i = 0;
while (i < size) {
ItemHolder holder = items.valueAt(i);
if (holder.id == itemId) {
removeView(holder.view);
items.delete(holder.id);
break;
}
i++;
}
}
public interface ItemClickListener {
@UiThread
void onBannerItemClick(int id);
}
private clreplaced ItemHolder {
final private ConstraintLayout view;
final int id;
final TextView textView;
final MaterialButton button;
ItemHolder(@NonNull ConstraintLayout view, int id) {
this.view = view;
this.id = id;
textView = view.findViewById(R.id.message);
button = view.findViewById(R.id.button);
}
}
}
19
Source : FriendsBarAdapter.java
with GNU Affero General Public License v3.0
from zood
with GNU Affero General Public License v3.0
from zood
public clreplaced FriendsBarAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int ADDFRIEND_ID = -4;
private static final int DIVIDER_ID = -3;
private static final int MARGIN_ID = -2;
private ArrayList<RecyclerViewAdapterItem> items = new ArrayList<>();
private ArrayList<FriendRecord> friends = new ArrayList<>();
private SparseArray<FriendRecord> friendPositions = new SparseArray<>();
@NonNull
private final Listener listener;
FriendsBarAdapter(@NonNull Listener l) {
this.listener = l;
setHreplacedtableIds(true);
}
@AnyThread
void addFriend(final FriendRecord friend) {
App.runOnUiThread(new UiRunnable() {
@Override
public void run() {
_addFriend(friend);
}
});
}
@UiThread
private void _addFriend(FriendRecord friend) {
// check if we're replacing or adding the friend
int idx = friends.indexOf(friend);
if (idx == -1) {
friends.add(friend);
} else {
friends.set(idx, friend);
}
rebuildItemsList();
notifyDataSetChanged();
}
@Override
public int gereplacedemCount() {
return items.size();
}
@Override
public long gereplacedemId(int position) {
return items.get(position).id;
}
@Override
public int gereplacedemViewType(int position) {
return items.get(position).viewType;
}
@UiThread
void onAvatarUpdated(@Nullable String username) {
if (username == null) {
return;
}
notifyDataSetChanged();
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
RecyclerViewAdapterItem item = items.get(position);
if (holder instanceof AvatarViewHolder) {
FriendRecord friend = friendPositions.get(position);
if (friend == null) {
throw new RuntimeException("Unable to find the friend with id " + item.id);
}
AvatarViewHolder h = (AvatarViewHolder) holder;
Resources rsrcs = h.avatar.getResources();
int imgSize = rsrcs.getDimensionPixelSize(R.dimen.forty);
Context ctx = h.avatar.getContext();
h.avatar.setUsername(friend.user.username);
Picreplacedo.with(ctx).load(AvatarManager.getAvatar(ctx, friend.user.username)).resize(imgSize, imgSize).into(h.avatar);
}
}
@Override
@NonNull
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(viewType, parent, false);
if (viewType == R.layout.friends_bar_friend) {
final AvatarViewHolder h = new AvatarViewHolder(view);
h.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FriendRecord friend = friendPositions.get(h.getAdapterPosition());
if (friend == null) {
throw new RuntimeException("Unable to find friend at position " + h.getAdapterPosition());
}
listener.onFriendSelected(friend);
}
});
return h;
} else if (viewType == R.layout.friends_bar_add_friend) {
final AddFriendViewHolder h = new AddFriendViewHolder(view);
h.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
listener.onAddFriendAction();
}
});
return h;
}
return new NothingViewHolder(view);
}
@UiThread
private void rebuildItemsList() {
items.clear();
int position = 0;
items.add(new RecyclerViewAdapterItem(R.layout.friends_bar_margin, MARGIN_ID));
friendPositions.clear();
for (FriendRecord f : friends) {
position++;
items.add(new RecyclerViewAdapterItem(R.layout.friends_bar_friend, f.id));
friendPositions.put(position, f);
}
items.add(new RecyclerViewAdapterItem(R.layout.friends_bar_divider, DIVIDER_ID));
items.add(new RecyclerViewAdapterItem(R.layout.friends_bar_add_friend, ADDFRIEND_ID));
}
@UiThread
void removeFriend(long friendId) {
for (int i = 0; i < friends.size(); i++) {
if (friends.get(i).id == friendId) {
friends.remove(i);
break;
}
}
rebuildItemsList();
notifyDataSetChanged();
}
@AnyThread
public void setFriends(final ArrayList<FriendRecord> friends) {
App.runOnUiThread(new UiRunnable() {
@Override
public void run() {
_setFriends(friends);
}
});
}
@UiThread
private void _setFriends(ArrayList<FriendRecord> friends) {
this.friends = friends;
rebuildItemsList();
notifyDataSetChanged();
}
private static clreplaced AddFriendViewHolder extends RecyclerView.ViewHolder {
ImageView image;
AddFriendViewHolder(View itemView) {
super(itemView);
image = itemView.findViewById(R.id.image);
image.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, image.getWidth(), image.getHeight(), image.getWidth() / 2.0f);
}
});
image.setClipToOutline(true);
}
}
private static clreplaced AvatarViewHolder extends RecyclerView.ViewHolder {
final AvatarView avatar;
AvatarViewHolder(View itemView) {
super(itemView);
avatar = itemView.findViewById(R.id.avatar_image);
}
}
private static clreplaced NothingViewHolder extends RecyclerView.ViewHolder {
NothingViewHolder(View itemView) {
super(itemView);
}
}
public interface Listener {
@UiThread
void onAddFriendAction();
@UiThread
void onFriendSelected(@NonNull FriendRecord fr);
}
}
19
Source : DecodeTask.java
with Apache License 2.0
from zjutkz
with Apache License 2.0
from zjutkz
/**
* Created by kangzhe on 16/10/20.
*/
public clreplaced DecodeTask extends DefaultTask {
private static final String TAG = "DecodeTask";
private static final int MAGIC_DEX = 0x0A786564;
private static final int MAGIC_035 = 0x00353330;
private static final int MAGIC_036 = 0x00363330;
private static final int VALUE_BYTE = 0;
private static final int VALUE_SHORT = 2;
private static final int VALUE_CHAR = 3;
private static final int VALUE_INT = 4;
private static final int VALUE_LONG = 6;
private static final int VALUE_FLOAT = 16;
private static final int VALUE_DOUBLE = 17;
private static final int VALUE_STRING = 23;
private static final int VALUE_TYPE = 24;
private static final int VALUE_FIELD = 25;
private static final int VALUE_METHOD = 26;
private static final int VALUE_ENUM = 27;
private static final int VALUE_ARRAY = 28;
private static final int VALUE_ANNOTATION = 29;
private static final int VALUE_NULL = 30;
private static final int VALUE_BOOLEAN = 31;
private static int string_ids_size;
private static int type_ids_size;
private static int field_ids_size;
private static int method_ids_size;
private static int clreplaced_def_size;
private static ByteBuffer stringId;
private static ByteBuffer typeId;
private static ByteBuffer protoId;
private static ByteBuffer fieldId;
private static ByteBuffer methodId;
private static ByteBuffer clreplacedDef;
private static ByteBuffer typeList;
private static ByteBuffer stringData;
private static ByteBuffer clreplacedData;
private static ByteBuffer codeData;
private static ByteBuffer annotationsDirectoryItem;
private static ByteBuffer annotationSereplacedem;
private static ByteBuffer annotationItem;
private static ByteBuffer annotationSetRefList;
private static SparseIntArray fieldAnnotationPositions = new SparseIntArray();
private static SparseIntArray methodAnnotationPositions = new SparseIntArray();
private static SparseIntArray paramAnnotationPositions = new SparseIntArray();
private static SparseArray<Clreplaced> id2clreplaced = new SparseArray<>();
private static List<Clreplaced> allClreplacedes = new ArrayList<>();
private static List<Method> allMethods = new ArrayList<>();
private static Map<String, Integer> name2Id = new HashMap<>();
@Override
public void doTask() {
byte[] dexSrc = (byte[]) src;
decodeDexFile(dexSrc);
}
private void decodeDexFile(byte[] src) {
decodeDexFile(ByteBuffer.wrap(src));
}
private static void decodeDexFile(ByteBuffer buffer) {
restore(buffer);
buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
// magic & version
int magic = buffer.getInt();
int version = buffer.getInt();
if (!isDexFile(magic, version)) {
Log.d(TAG, "Finish decoding,it is not a dex file!");
return;
}
// skip checksum
// skip signature
// skip file_size
// skip header_size 0x70
// skip endian_tag
// skip uint link_size
// skip uint link_off
// skip uint map_off
skip(buffer, 4 + 20 + 4 + 4 + 4 + 4 + 4 + 4);
string_ids_size = buffer.getInt();
int string_ids_off = buffer.getInt();
type_ids_size = buffer.getInt();
int type_ids_off = buffer.getInt();
int proto_ids_size = buffer.getInt();
int proto_ids_off = buffer.getInt();
field_ids_size = buffer.getInt();
int field_ids_off = buffer.getInt();
method_ids_size = buffer.getInt();
int method_ids_off = buffer.getInt();
clreplaced_def_size = buffer.getInt();
int clreplaced_def_off = buffer.getInt();
stringId = slice(buffer, string_ids_off, string_ids_size * 4);
typeId = slice(buffer, type_ids_off, type_ids_size * 4);
protoId = slice(buffer, proto_ids_off, proto_ids_size * 12);
fieldId = slice(buffer, field_ids_off, field_ids_size * 8);
methodId = slice(buffer, method_ids_off, method_ids_size * 8);
clreplacedDef = slice(buffer, clreplaced_def_off, clreplaced_def_size * 32);
restore(buffer);
stringData = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
typeList = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
clreplacedData = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
codeData = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
annotationsDirectoryItem = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
annotationSereplacedem = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
annotationItem = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
annotationSetRefList = buffer.duplicate().order(ByteOrder.LITTLE_ENDIAN);
preFetch();
}
private static void preFetch() {
getAllClreplacedes();
int clzSize = allClreplacedes.size();
int methodSize = allMethods.size();
Log.d(TAG, "There are " + clzSize + " clreplacedes with " + methodSize + " methods.");
}
private static boolean isDexFile(int magic, int version) {
return magic == MAGIC_DEX && (version == MAGIC_035 || version == MAGIC_036);
}
public static List<Method> getAllMethodsInClreplaced(String clzName) {
List<Method> allMethods = new ArrayList<>();
Integer clzId = name2Id.get(clzName);
if (clzId != null) {
allMethods = getMethodNamesWithClzId(clzId);
}
return allMethods;
}
public static boolean hasMethod(String clzName, String methodName) {
List<Method> allMethods = getAllMethodsInClreplaced(clzName);
for (Method method : allMethods) {
if (method.name.equals(methodName)) {
return true;
}
}
return false;
}
public static boolean hasClreplaced(String clzName) {
if (name2Id.size() != 0) {
return name2Id.containsKey(clzName);
}
getFileNames();
return name2Id.containsKey(clzName);
}
private static ByteBuffer slice(ByteBuffer in, int offset, int length) {
in.position(offset);
ByteBuffer b = in.slice();
b.limit(length);
b.order(ByteOrder.LITTLE_ENDIAN);
return b;
}
private static void restore(ByteBuffer buffer) {
buffer.position(0);
}
private static void skip(ByteBuffer data, int pos) {
data.position(data.position() + pos);
}
private static List<Method> getMethodNamesWithClzId(int clzId) {
Clreplaced dest = id2clreplaced.get(clzId, null);
List<Method> allMethods = new ArrayList<>();
if (dest != null) {
allMethods.addAll(dest.virtual_methods);
allMethods.addAll(dest.direct_methods);
}
return allMethods;
}
public static Method getMethod(String clzName, String methodName) {
Clreplaced destClz;
Integer clzId = name2Id.get(clzName);
if (clzId != null) {
destClz = id2clreplaced.get(clzId, null);
for (Method method : destClz.direct_methods) {
if (method.name.equals(methodName)) {
return method;
}
}
for (Method method : destClz.virtual_methods) {
if (method.name.equals(methodName)) {
return method;
}
}
}
return null;
}
public static int getMethodCount() {
if (allMethods != null) {
return allMethods.size();
}
return 0;
}
public static Clreplaced getClreplaced(String clzName) {
Integer clzId = name2Id.get(clzName);
if (clzId != null) {
return id2clreplaced.get(clzId, null);
}
return null;
}
public static int getClreplacedCount() {
if (allClreplacedes != null) {
return allClreplacedes.size();
}
return 0;
}
public static void dumpDex() {
StringBuilder sb = new StringBuilder();
sb.append("access_flag:" + "\n");
sb.append(AccessFlag.dump());
if (allClreplacedes != null) {
for (Clreplaced clz : allClreplacedes) {
sb.append(clz.dump());
}
}
Log.d(TAG, sb.toString());
}
public static void dumpDex(String filePath) {
StringBuilder sb = new StringBuilder();
sb.append("access_flag:" + "\n");
sb.append(AccessFlag.dump());
if (allClreplacedes != null) {
for (Clreplaced clz : allClreplacedes) {
sb.append(clz.dump());
}
}
try {
File dump = mkFile(filePath);
FileWriter writer = new FileWriter(dump);
writer.write(sb.toString());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static File mkFile(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
return file;
}
public static List<Method> getAllMethods() {
if (allMethods.size() != 0) {
return allMethods;
}
getAllClreplacedes();
return allMethods;
}
public static void clearAllData() {
allClreplacedes.clear();
allMethods.clear();
name2Id.clear();
}
public static List<Clreplaced> getAllClreplacedes() {
if (allClreplacedes.size() != 0) {
return allClreplacedes;
}
List<Clreplaced> clzs = new ArrayList<>(clreplaced_def_size);
for (int cid = 0; cid < clreplaced_def_size; cid++) {
Clreplaced clz = new Clreplaced();
clreplacedDef.position(cid * 32);
int clreplaced_idx = clreplacedDef.getInt();
clz.clreplaced_name = getType(clreplaced_idx);
clz.access_flags = clreplacedDef.getInt();
clz.super_clreplaced_name = getType(clreplacedDef.getInt());
clz.interfaces = getTypeList(clreplacedDef.getInt());
clz.source_file_name = getString(clreplacedDef.getInt());
int annotations_off = clreplacedDef.getInt();
int clreplaced_data_off = clreplacedDef.getInt();
int static_values_off = clreplacedDef.getInt();
parseAnnotation(clz, annotations_off);
getFieldsAndMethods(clz, clreplaced_data_off, static_values_off);
if (!TextUtils.isEmpty(clz.source_file_name)) {
replaceSourceFileName(clz);
name2Id.put(clz.source_file_name.split("\\.")[0], clreplaced_idx);
}
id2clreplaced.append(clreplaced_idx, clz);
clzs.add(clz);
}
for (Clreplaced clz : allClreplacedes) {
if (!TextUtils.isEmpty(clz.super_clreplaced_name)) {
clz.super_clreplaced = getClreplaced(clz.super_clreplaced_name);
}
}
allClreplacedes = clzs;
return clzs;
}
private static void parseAnnotation(Clreplaced clz, int annotations_off) {
if (annotations_off != 0) {
annotationsDirectoryItem.position(annotations_off);
int clreplaced_annotations_off = annotationsDirectoryItem.getInt();
int field_annotation_size = annotationsDirectoryItem.getInt();
int method_annotation_size = annotationsDirectoryItem.getInt();
int parameter_annotation_size = annotationsDirectoryItem.getInt();
if (clreplaced_annotations_off != 0) {
getAnnotationSereplacedem(clz, clreplaced_annotations_off);
}
for (int i = 0; i < field_annotation_size; i++) {
int field_idx = annotationsDirectoryItem.getInt();
int field_annotations_offset = annotationsDirectoryItem.getInt();
fieldAnnotationPositions.put(field_idx, field_annotations_offset);
}
for (int i = 0; i < method_annotation_size; i++) {
int method_idx = annotationsDirectoryItem.getInt();
int method_annotation_offset = annotationsDirectoryItem.getInt();
methodAnnotationPositions.put(method_idx, method_annotation_offset);
}
for (int i = 0; i < parameter_annotation_size; i++) {
int method_idx = annotationsDirectoryItem.getInt();
int parameter_annotation_offset = annotationsDirectoryItem.getInt();
paramAnnotationPositions.put(method_idx, parameter_annotation_offset);
}
}
}
private static void getAnnotationSereplacedem(Object injected, int annotations_off) {
if (annotations_off != 0) {
ByteBuffer buffer = annotationSereplacedem;
buffer.position(annotations_off);
int size = buffer.getInt();
for (int j = 0; j < size; j++) {
int annotation_off = buffer.getInt();
Annotation annotation = getAnnotation(annotation_off);
if (injected instanceof Clreplaced) {
((Clreplaced) injected).addAnnotation(annotation);
} else if (injected instanceof Field) {
((Field) injected).addAnnotation(annotation);
} else if (injected instanceof Method) {
((Method) injected).addMethodAnnotation(annotation);
}
}
}
}
private static Annotation getAnnotation(int annotation_off) {
ByteBuffer buffer = annotationItem;
buffer.position(annotation_off);
Annotation annotation = new Annotation();
int visibility = 0xFF & buffer.get();
int type_idx = readULeb128(buffer);
int size = readULeb128(buffer);
String type = getType(type_idx);
annotation.visibility = visibility;
annotation.name = type;
for (int i = 0; i < size; i++) {
AnnotationElement element = new AnnotationElement();
int name_idx = readULeb128(buffer);
String name = getString(name_idx);
Object value = readEncodedValue(buffer);
element.name = name;
element.value = value;
annotation.addElement(element);
}
return annotation;
}
private static Object readEncodedValue(ByteBuffer in) {
int b = 0xFF & in.get();
int type = b & 0x1f;
switch(type) {
case VALUE_BYTE:
return new Byte((byte) readIntBits(in, b));
case VALUE_SHORT:
return new Short((short) readIntBits(in, b));
case VALUE_INT:
return new Integer((int) readIntBits(in, b));
case VALUE_LONG:
return new Long(readIntBits(in, b));
case VALUE_CHAR:
return new Character((char) readUIntBits(in, b));
case VALUE_STRING:
return getString((int) readUIntBits(in, b));
case VALUE_FLOAT:
return Float.intBitsToFloat((int) (readFloatBits(in, b) >> 32));
case VALUE_DOUBLE:
return Double.longBitsToDouble(readFloatBits(in, b));
case VALUE_NULL:
return null;
case VALUE_BOOLEAN:
{
return new Boolean(((b >> 5) & 0x3) != 0);
}
case VALUE_ENUM:
{
return getField((int) readUIntBits(in, b), -1);
}
// TODO: 16/10/23 support array
/*case VALUE_ARRAY: {
return getEncodedArray(in);
}*/
default:
return null;
}
}
private static Object[] getEncodedArray(ByteBuffer in) {
int size = readULeb128(in);
Object[] constant = new Object[size];
for (int i = 0; i < size; i++) {
constant[i] = readEncodedValue(in);
}
return constant;
}
private static void replaceSourceFileName(Clreplaced clz) {
if (clz.clreplaced_name.contains("$")) {
String outerClz = clz.source_file_name.split("\\.")[0];
String[] maybeMore = clz.clreplaced_name.split("\\$");
String innerClz = "";
for (int i = 1; i < maybeMore.length; i++) {
if (i == 1) {
innerClz = maybeMore[i];
} else {
innerClz = innerClz + "$" + maybeMore[i];
}
}
// exclude ";"
innerClz = innerClz.substring(0, innerClz.length() - 1);
clz.source_file_name = outerClz + "$" + innerClz + ".java";
}
}
private static void getFieldsAndMethods(Clreplaced clz, int clreplaced_data_off, int static_values_off) {
if (clreplaced_data_off != 0) {
ByteBuffer buffer = clreplacedData;
buffer.position(clreplaced_data_off);
int static_fields = readULeb128(buffer);
int instance_fields = readULeb128(buffer);
int direct_methods = readULeb128(buffer);
int virtual_methods = readULeb128(buffer);
List<Field> staticFields = new ArrayList<>();
List<Field> instanceFields = new ArrayList<>();
List<Method> directMethods = new ArrayList<>();
List<Method> virtualMethods = new ArrayList<>();
int lastField = 0;
for (int i = 0; i < static_fields; i++) {
lastField = findFields(buffer, lastField, staticFields);
}
lastField = 0;
for (int i = 0; i < instance_fields; i++) {
lastField = findFields(buffer, lastField, instanceFields);
}
int lastMethod = 0;
for (int i = 0; i < direct_methods; i++) {
lastMethod = findMethods(buffer, lastMethod, directMethods);
}
lastMethod = 0;
for (int i = 0; i < virtual_methods; i++) {
lastMethod = findMethods(buffer, lastMethod, virtualMethods);
}
clz.static_fields = staticFields;
clz.instance_fields = instanceFields;
clz.direct_methods = directMethods;
clz.virtual_methods = virtualMethods;
allMethods.addAll(directMethods);
allMethods.addAll(virtualMethods);
}
}
private static int findMethods(ByteBuffer buffer, int lastMethod, List<Method> methods) {
if (methods == null) {
methods = new ArrayList<>();
}
int diff = readULeb128(buffer);
// TODO: 16/10/21 support code item
int access_flags = readULeb128(buffer);
int code_off = readULeb128(buffer);
int method_id = lastMethod + diff;
methods.add(getMethod(method_id, access_flags, code_off));
return method_id;
}
private static Method getMethod(int id, int access_flags, int offset) {
methodId.position(id * 8);
int clreplaced_idx = methodId.getShort();
int proto_idx = 0xFFFF & methodId.getShort();
int name_idx = methodId.getInt();
String clzName = getType(clreplaced_idx);
List<String> parameterTypes;
String returnType;
protoId.position(proto_idx * 12 + 4);
int return_type_idx = protoId.getInt();
int parameters_off = protoId.getInt();
returnType = getType(return_type_idx);
parameterTypes = getTypeList(parameters_off);
Method method = new Method(clzName, parameterTypes, returnType, getString(name_idx), access_flags);
Integer method_annotation_offset = methodAnnotationPositions.get(id);
getAnnotationSereplacedem(method, method_annotation_offset);
Integer parameter_annotation_offset = paramAnnotationPositions.get(id);
getAnnotationSetRefList(method, parameter_annotation_offset);
return method;
}
private static void getAnnotationSetRefList(Method method, Integer parameter_annotation_offset) {
if (parameter_annotation_offset != 0) {
ByteBuffer buffer = annotationSetRefList;
buffer.position(parameter_annotation_offset);
int size = buffer.getInt();
for (int j = 0; j < size; j++) {
int param_annotation_offset = buffer.getInt();
if (param_annotation_offset == 0) {
continue;
}
getAnnotationSereplacedem(method, param_annotation_offset);
}
}
}
private static int findFields(ByteBuffer buffer, int lastField, List<Field> fields) {
if (fields == null) {
fields = new ArrayList<>();
}
int diff = readULeb128(buffer);
int field_access_flags = readULeb128(buffer);
int field_id = lastField + diff;
fields.add(getField(field_id, field_access_flags));
return field_id;
}
private static Field getField(int id, int field_access_flags) {
Field field = new Field();
fieldId.position(id * 8);
skip(fieldId, 2);
int type_idx = 0xFFFF & fieldId.getShort();
int name_idx = fieldId.getInt();
String type = getType(type_idx);
String name = getString(name_idx);
Integer annotation_offset = fieldAnnotationPositions.get(id);
getAnnotationSereplacedem(field, annotation_offset);
field.type = type;
field.name = name;
field.access_flag = field_access_flags;
return field;
}
private static void getFileNames() {
ByteBuffer buffer = clreplacedDef;
restore(buffer);
for (int cid = 0; cid < clreplaced_def_size; cid++) {
int clreplaced_idx = buffer.getInt();
// skip access_flags
// skip superclreplaced_idx
// skip interfaces_off
skip(buffer, 4 + 4 + 4);
int source_file_idx = buffer.getInt();
// skip annotations_off
// skip clreplaced_data_off
// skip static_values_off
skip(buffer, 4 + 4 + 4);
String fileName = getString(source_file_idx);
if (!TextUtils.isEmpty(fileName)) {
String clzName = fileName.split("\\.")[0];
name2Id.put(clzName.trim(), clreplaced_idx);
}
}
}
private static List<String> getTypeList(int offset) {
if (offset == 0) {
return new ArrayList<>();
}
typeList.position(offset);
int size = typeList.getInt();
List<String> types = new ArrayList<>();
for (int i = 0; i < size; i++) {
types.add(getType(0xFFFF & typeList.getShort()));
}
return types;
}
private static String getType(int id) {
if (id == -1) {
return null;
}
return getString(typeId.getInt(id * 4));
}
private static String getString(int id) {
if (id == -1) {
return null;
}
int offset = stringId.getInt(id * 4);
stringData.position(offset);
int length = readULeb128(stringData);
try {
StringBuilder buff = new StringBuilder(length);
return UTF8.decode(stringData, buff).trim();
} catch (UTFDataFormatException e) {
e.printStackTrace();
return "";
}
}
private static int readULeb128(ByteBuffer in) {
int value = 0;
int count = 0;
int b = in.get();
while ((b & 0x80) != 0) {
value |= (b & 0x7f) << count;
count += 7;
b = in.get();
}
value |= (b & 0x7f) << count;
return value;
}
private static long readIntBits(ByteBuffer in, int before) {
int length = ((before >> 5) & 0x7) + 1;
long value = 0;
for (int j = 0; j < length; j++) {
value |= ((long) (0xFF & in.get())) << (j * 8);
}
int shift = (8 - length) * 8;
return value << shift >> shift;
}
private static long readUIntBits(ByteBuffer in, int before) {
int length = ((before >> 5) & 0x7) + 1;
long value = 0;
for (int j = 0; j < length; j++) {
value |= ((long) (0xFF & in.get())) << (j * 8);
}
return value;
}
private static long readFloatBits(ByteBuffer in, int before) {
int bytes = ((before >> 5) & 0x7) + 1;
long result = 0L;
for (int i = 0; i < bytes; ++i) {
result |= ((long) (0xFF & in.get())) << (i * 8);
}
result <<= (8 - bytes) * 8;
return result;
}
}
19
Source : SavedState.java
with Apache License 2.0
from Zhuinden
with Apache License 2.0
from Zhuinden
/**
* A container for the view hierarchy state and an optional Bundle.
*
* A {@link SavedState} represents the state of the screen that is bound to a given key.
*/
public clreplaced SavedState {
private Object key;
private SparseArray<Parcelable> viewHierarchyState;
private StateBundle bundle;
private StateBundle viewBundle;
private SavedState() {
}
@Nonnull
public Object getKey() {
return key;
}
@Nonnull
public SparseArray<Parcelable> getViewHierarchyState() {
return viewHierarchyState;
}
public void setViewHierarchyState(SparseArray<Parcelable> viewHierarchyState) {
this.viewHierarchyState = viewHierarchyState;
}
@Nullable
public StateBundle getBundle() {
return bundle;
}
@Nullable
StateBundle getViewBundle() {
return viewBundle;
}
public void setBundle(@Nullable StateBundle bundle) {
// should be non-null
this.bundle = bundle;
}
void setViewBundle(@Nullable StateBundle viewBundle) {
this.viewBundle = viewBundle;
}
public static Builder builder() {
return new Builder();
}
/**
* A builder clreplaced that allows creating SavedState instances.
*
* Keys are not optional.
*/
public static clreplaced Builder {
private Object key;
private SparseArray<Parcelable> viewHierarchyState = new SparseArray<>();
private StateBundle bundle = new StateBundle();
private StateBundle viewBundle;
Builder() {
}
public Builder setKey(@Nonnull Object key) {
if (key == null) {
throw new IllegalArgumentException("Key cannot be null");
}
this.key = key;
return this;
}
public Builder setViewHierarchyState(@Nonnull SparseArray<Parcelable> viewHierarchyState) {
if (viewHierarchyState == null) {
throw new IllegalArgumentException("Provided sparse array for view hierarchy state cannot be null");
}
this.viewHierarchyState = viewHierarchyState;
return this;
}
public Builder setBundle(@Nullable StateBundle bundle) {
// should be non-null
this.bundle = bundle;
return this;
}
Builder setViewBundle(@Nullable StateBundle viewBundle) {
this.viewBundle = viewBundle;
return this;
}
public SavedState build() {
if (key == null) {
throw new IllegalStateException("You cannot create a SavedState without replacedociating a Key with it.");
}
SavedState savedState = new SavedState();
savedState.key = key;
savedState.viewHierarchyState = viewHierarchyState;
savedState.bundle = bundle;
savedState.viewBundle = viewBundle;
return savedState;
}
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instanceof SavedState)) {
return false;
}
return ((SavedState) obj).getKey().equals(this.key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}
19
Source : ParcelledState.java
with Apache License 2.0
from Zhuinden
with Apache License 2.0
from Zhuinden
/**
* Created by Owner on 2017. 02. 28..
*/
clreplaced ParcelledState implements Parcelable {
Parcelable parcelableKey;
SparseArray<Parcelable> viewHierarchyState;
StateBundle bundle;
StateBundle viewBundle;
ParcelledState() {
}
protected ParcelledState(Parcel in) {
parcelableKey = in.readParcelable(getClreplaced().getClreplacedLoader());
// noinspection unchecked
viewHierarchyState = in.readSparseArray(getClreplaced().getClreplacedLoader());
boolean hasBundle = in.readByte() > 0;
if (hasBundle) {
bundle = in.readParcelable(getClreplaced().getClreplacedLoader());
}
boolean hasViewBundle = in.readByte() > 0;
if (hasViewBundle) {
viewBundle = in.readParcelable(getClreplaced().getClreplacedLoader());
}
}
public static final Creator<ParcelledState> CREATOR = new Creator<ParcelledState>() {
@Override
public ParcelledState createFromParcel(Parcel in) {
return new ParcelledState(in);
}
@Override
public ParcelledState[] newArray(int size) {
return new ParcelledState[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(parcelableKey, flags);
// noinspection unchecked
SparseArray<Object> sparseArray = (SparseArray) viewHierarchyState;
dest.writeSparseArray(sparseArray);
dest.writeByte(bundle != null ? (byte) 0x01 : 0x00);
if (bundle != null) {
dest.writeParcelable(bundle, 0);
}
dest.writeByte(viewBundle != null ? (byte) 0x01 : 0x00);
if (viewBundle != null) {
dest.writeParcelable(viewBundle, 0);
}
}
}
19
Source : ParcelledState.java
with Apache License 2.0
from Zhuinden
with Apache License 2.0
from Zhuinden
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(parcelableKey, flags);
// noinspection unchecked
SparseArray<Object> sparseArray = (SparseArray) viewHierarchyState;
dest.writeSparseArray(sparseArray);
dest.writeByte(bundle != null ? (byte) 0x01 : 0x00);
if (bundle != null) {
dest.writeParcelable(bundle, 0);
}
dest.writeByte(viewBundle != null ? (byte) 0x01 : 0x00);
if (viewBundle != null) {
dest.writeParcelable(viewBundle, 0);
}
}
19
Source : BaseViewHolder.java
with Apache License 2.0
from zhpanvip
with Apache License 2.0
from zhpanvip
/**
* <pre>
* Created by zhpan on 2020/4/5.
* Attention:Don't use {@link RecyclerView.ViewHolder#getAdapterPosition}
* method to get position,this method will return a fake position.
* </pre>
*/
@SuppressWarnings("unused")
public clreplaced BaseViewHolder<T> extends RecyclerView.ViewHolder {
private final SparseArray<View> mViews = new SparseArray<>();
public BaseViewHolder(@NonNull View itemView) {
super(itemView);
}
/**
* @deprecated bind data in adapter please.
*/
@Deprecated
public void bindData(T data, int position, int pageSize) {
}
@SuppressWarnings("unchecked")
public <V extends View> V findViewById(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (V) view;
}
public void setText(int viewId, CharSequence text) {
View view = findViewById(viewId);
if (view instanceof TextView) {
((TextView) view).setText(text);
}
}
public void setText(int viewId, @StringRes int textId) {
View view = findViewById(viewId);
if (view instanceof TextView) {
((TextView) view).setText(textId);
}
}
public void setTextColor(int viewId, @ColorInt int colorId) {
View view = findViewById(viewId);
if (view instanceof TextView) {
((TextView) view).setTextColor(colorId);
}
}
public void setTextColorRes(@IdRes int viewId, @ColorRes int colorRes) {
View view = findViewById(viewId);
if (view instanceof TextView) {
((TextView) view).setTextColor(ContextCompat.getColor(itemView.getContext(), colorRes));
}
}
public void setOnClickListener(int viewId, View.OnClickListener clickListener) {
findViewById(viewId).setOnClickListener(clickListener);
}
public void setBackgroundResource(int viewId, @DrawableRes int resId) {
findViewById(viewId).setBackgroundResource(resId);
}
public void setBackgroundColor(int viewId, @ColorInt int colorId) {
findViewById(viewId).setBackgroundColor(colorId);
}
public void setImageResource(@IdRes int viewId, @DrawableRes int resId) {
View view = findViewById(viewId);
if (view instanceof ImageView) {
((ImageView) view).setImageResource(resId);
}
}
public void setImageDrawable(@IdRes int viewId, Drawable drawable) {
View view = findViewById(viewId);
if (view instanceof ImageView) {
((ImageView) view).setImageDrawable(drawable);
}
}
public void setImageBitmap(@IdRes int viewId, Bitmap bitmap) {
ImageView view = findViewById(viewId);
view.setImageBitmap(bitmap);
}
public void setVisibility(@IdRes int resId, @Visibility int visibility) {
findViewById(resId).setVisibility(visibility);
}
}
19
Source : PRouterV4.java
with Apache License 2.0
from zhouhuandev
with Apache License 2.0
from zhouhuandev
/**
* 把OnActivityResult方式转换为Callback方式的空Fragment(V4兼容包)
*
* Created by XiaoFeng on 2018/9/5.
*/
public clreplaced PRouterV4 extends Fragment {
private SparseArray<PLauncher.Callback> mCallbacks = new SparseArray<>();
private Random mCodeGenerator = new Random();
public PRouterV4() {
// Required empty public constructor
}
public static PRouterV4 newInstance() {
return new PRouterV4();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void startActivityForResult(Intent intent, PLauncher.Callback callback) {
int requestCode = makeRequestCode();
mCallbacks.put(requestCode, callback);
startActivityForResult(intent, requestCode);
}
/**
* 随机生成唯一的requestCode,最多尝试10次
*
* @return
*/
private int makeRequestCode() {
int requestCode;
int tryCount = 0;
do {
requestCode = mCodeGenerator.nextInt(0x0000FFFF);
tryCount++;
} while (mCallbacks.indexOfKey(requestCode) >= 0 && tryCount < 10);
return requestCode;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
PLauncher.Callback callback = mCallbacks.get(requestCode);
mCallbacks.remove(requestCode);
if (callback != null) {
callback.onActivityResult(resultCode, data);
}
}
}
19
Source : PRouter.java
with Apache License 2.0
from zhouhuandev
with Apache License 2.0
from zhouhuandev
/**
* 把OnActivityResult方式转换为Callback方式的空Fragment(标准SDK)
*
* Created by XiaoFeng on 2018/9/5.
*/
public clreplaced PRouter extends Fragment {
private SparseArray<PLauncher.Callback> mCallbacks = new SparseArray<>();
private Random mCodeGenerator = new Random();
public PRouter() {
// Required empty public constructor
}
public static PRouter newInstance() {
return new PRouter();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void startActivityForResult(Intent intent, PLauncher.Callback callback) {
int requestCode = makeRequestCode();
mCallbacks.put(requestCode, callback);
startActivityForResult(intent, requestCode);
}
/**
* 随机生成唯一的requestCode,最多尝试10次
*
* @return
*/
private int makeRequestCode() {
int requestCode;
int tryCount = 0;
do {
requestCode = mCodeGenerator.nextInt(0x0000FFFF);
tryCount++;
} while (mCallbacks.indexOfKey(requestCode) >= 0 && tryCount < 10);
return requestCode;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
PLauncher.Callback callback = mCallbacks.get(requestCode);
mCallbacks.remove(requestCode);
if (callback != null) {
callback.onActivityResult(resultCode, data);
}
}
}
19
Source : RecycleViewHolder.java
with Apache License 2.0
from zhonglikui
with Apache License 2.0
from zhonglikui
/**
* RecycleView使用的ViewHolder
* Created by zhong on 2017/4/1.
*/
public clreplaced RecycleViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;
private View mConvertView;
public RecycleViewHolder(Context context, View itemView, ViewGroup parent) {
super(itemView);
mConvertView = itemView;
mViews = new SparseArray<>();
}
public static RecycleViewHolder get(Context context, ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(context).inflate(layoutId, parent, false);
return new RecycleViewHolder(context, itemView, parent);
}
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
* @return 返回convertView
*/
public View getConvertView() {
return mConvertView;
}
}
19
Source : ListViewHolder.java
with Apache License 2.0
from zhonglikui
with Apache License 2.0
from zhonglikui
/**
* Created by zhong on 2015/10/12.
* BaseListAdapter的辅助工具类
*/
public clreplaced ListViewHolder {
private final View mConvertView;
private final SparseArray<View> mViews;
/**
* 初始化ViewHolder
*
* @param context Context
* @param convertViewId item的layout
*/
private ListViewHolder(Context context, int convertViewId) {
mViews = new SparseArray<>();
mConvertView = View.inflate(context, convertViewId, null);
mConvertView.setTag(this);
}
/**
* 获取ViewHolder对象
*
* @param convertView layout
* @return ViewHolder
*/
static ListViewHolder get(Context context, View convertView, int converViewId) {
if (convertView == null) {
return new ListViewHolder(context, converViewId);
} else {
return (ListViewHolder) convertView.getTag();
}
}
/**
* 通过控件的id获取相对应的控件
*
* @param viewId 需要查找的View的id
* @param <T> View的类型
* @return 查找的结果
*/
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
/**
* @return 返回convertView
*/
View getConvertView() {
return mConvertView;
}
}
19
Source : PreviewPagerAdapter.java
with MIT License
from zhongjhATC
with MIT License
from zhongjhATC
public clreplaced PreviewPagerAdapter extends FragmentPagerAdapter {
private ArrayList<MultiMedia> mItems = new ArrayList<>();
private SparseArray<Fragment> fragments;
private OnPrimaryItemSetListener mListener;
public PreviewPagerAdapter(FragmentManager manager, OnPrimaryItemSetListener listener) {
super(manager);
mListener = listener;
fragments = new SparseArray<>(getCount());
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
fragments.put(position, fragment);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
fragments.remove(position);
}
public Fragment getFragment(int position) {
return fragments.get(position);
}
@Override
public Fragment gereplacedem(int position) {
return PreviewItemFragment.newInstance(mItems.get(position));
}
@Override
public int getCount() {
return mItems.size();
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (mListener != null) {
mListener.onPrimaryItemSet(position);
}
}
public MultiMedia getMediaItem(int position) {
return mItems.get(position);
}
public void setMediaItem(int position, MultiMedia multiMedia) {
mItems.set(position, multiMedia);
}
public void addAll(List<MultiMedia> items) {
mItems.addAll(items);
}
public ArrayList<MultiMedia> getmItems() {
return mItems;
}
interface OnPrimaryItemSetListener {
void onPrimaryItemSet(int position);
}
}
19
Source : UiSelector.java
with MIT License
from ZhongAnTech
with MIT License
from ZhongAnTech
/**
* Specifies the elements in the layout hierarchy for tests to target, filtered
* by properties such as text value, content-description, clreplaced name, and state
* information. You can also target an element by its location in a layout
* hierarchy.
* @since API Level 16
*/
public clreplaced UiSelector {
static final int SELECTOR_NIL = 0;
static final int SELECTOR_TEXT = 1;
static final int SELECTOR_START_TEXT = 2;
static final int SELECTOR_CONTAINS_TEXT = 3;
static final int SELECTOR_CLreplaced = 4;
static final int SELECTOR_DESCRIPTION = 5;
static final int SELECTOR_START_DESCRIPTION = 6;
static final int SELECTOR_CONTAINS_DESCRIPTION = 7;
static final int SELECTOR_INDEX = 8;
static final int SELECTOR_INSTANCE = 9;
static final int SELECTOR_ENABLED = 10;
static final int SELECTOR_FOCUSED = 11;
static final int SELECTOR_FOCUSABLE = 12;
static final int SELECTOR_SCROLLABLE = 13;
static final int SELECTOR_CLICKABLE = 14;
static final int SELECTOR_CHECKED = 15;
static final int SELECTOR_SELECTED = 16;
static final int SELECTOR_ID = 17;
static final int SELECTOR_PACKAGE_NAME = 18;
static final int SELECTOR_CHILD = 19;
static final int SELECTOR_CONTAINER = 20;
static final int SELECTOR_PATTERN = 21;
static final int SELECTOR_PARENT = 22;
static final int SELECTOR_COUNT = 23;
static final int SELECTOR_LONG_CLICKABLE = 24;
static final int SELECTOR_TEXT_REGEX = 25;
static final int SELECTOR_CLreplaced_REGEX = 26;
static final int SELECTOR_DESCRIPTION_REGEX = 27;
static final int SELECTOR_PACKAGE_NAME_REGEX = 28;
static final int SELECTOR_RESOURCE_ID = 29;
static final int SELECTOR_CHECKABLE = 30;
static final int SELECTOR_RESOURCE_ID_REGEX = 31;
private SparseArray<Object> mSelectorAttributes = new SparseArray<Object>();
/**
* @since API Level 16
*/
public UiSelector() {
}
UiSelector(UiSelector selector) {
mSelectorAttributes = selector.cloneSelector().mSelectorAttributes;
}
/**
* @since API Level 17
*/
protected UiSelector cloneSelector() {
UiSelector ret = new UiSelector();
ret.mSelectorAttributes = mSelectorAttributes.clone();
if (hasChildSelector())
ret.mSelectorAttributes.put(SELECTOR_CHILD, new UiSelector(getChildSelector()));
if (hasParentSelector())
ret.mSelectorAttributes.put(SELECTOR_PARENT, new UiSelector(getParentSelector()));
if (hasPatternSelector())
ret.mSelectorAttributes.put(SELECTOR_PATTERN, new UiSelector(getPatternSelector()));
return ret;
}
static UiSelector patternBuilder(UiSelector selector) {
if (!selector.hasPatternSelector()) {
return new UiSelector().patternSelector(selector);
}
return selector;
}
static UiSelector patternBuilder(UiSelector container, UiSelector pattern) {
return new UiSelector(new UiSelector().containerSelector(container).patternSelector(pattern));
}
/**
* Set the search criteria to match the visible text displayed
* in a widget (for example, the text label to launch an app).
*
* The text for the element must match exactly with the string in your input
* argument. Matching is case-sensitive.
*
* @param text Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector text(String text) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
return buildSelector(SELECTOR_TEXT, text);
}
/**
* Set the search criteria to match the visible text displayed in a layout
* element, using a regular expression.
*
* The text in the widget must match exactly with the string in your
* input argument.
*
* @param regex a regular expression
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
public UiSelector textMatches(String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
return buildSelector(SELECTOR_TEXT_REGEX, Pattern.compile(regex));
}
/**
* Set the search criteria to match visible text in a widget that is
* prefixed by the text parameter.
*
* The matching is case-insensitive.
*
* @param text Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector textStartsWith(String text) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
return buildSelector(SELECTOR_START_TEXT, text);
}
/**
* Set the search criteria to match the visible text in a widget
* where the visible text must contain the string in your input argument.
*
* The matching is case-sensitive.
*
* @param text Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector textContains(String text) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
return buildSelector(SELECTOR_CONTAINS_TEXT, text);
}
/**
* Set the search criteria to match the clreplaced property
* for a widget (for example, "android.widget.Button").
*
* @param clreplacedName Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector clreplacedName(String clreplacedName) {
if (clreplacedName == null) {
throw new IllegalArgumentException("clreplacedName cannot be null");
}
return buildSelector(SELECTOR_CLreplaced, clreplacedName);
}
/**
* Set the search criteria to match the clreplaced property
* for a widget, using a regular expression.
*
* @param regex a regular expression
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
public UiSelector clreplacedNameMatches(String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
return buildSelector(SELECTOR_CLreplaced_REGEX, Pattern.compile(regex));
}
/**
* Set the search criteria to match the clreplaced property
* for a widget (for example, "android.widget.Button").
*
* @param type type
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
public <T> UiSelector clreplacedName(Clreplaced<T> type) {
if (type == null) {
throw new IllegalArgumentException("type cannot be null");
}
return buildSelector(SELECTOR_CLreplaced, type.getName());
}
/**
* Set the search criteria to match the content-description
* property for a widget.
*
* The content-description is typically used
* by the Android Accessibility framework to
* provide an audio prompt for the widget when
* the widget is selected. The content-description
* for the widget must match exactly
* with the string in your input argument.
*
* Matching is case-sensitive.
*
* @param desc Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector description(String desc) {
if (desc == null) {
throw new IllegalArgumentException("desc cannot be null");
}
return buildSelector(SELECTOR_DESCRIPTION, desc);
}
/**
* Set the search criteria to match the content-description
* property for a widget.
*
* The content-description is typically used
* by the Android Accessibility framework to
* provide an audio prompt for the widget when
* the widget is selected. The content-description
* for the widget must match exactly
* with the string in your input argument.
*
* @param regex a regular expression
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
public UiSelector descriptionMatches(String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
return buildSelector(SELECTOR_DESCRIPTION_REGEX, Pattern.compile(regex));
}
/**
* Set the search criteria to match the content-description
* property for a widget.
*
* The content-description is typically used
* by the Android Accessibility framework to
* provide an audio prompt for the widget when
* the widget is selected. The content-description
* for the widget must start
* with the string in your input argument.
*
* Matching is case-insensitive.
*
* @param desc Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector descriptionStartsWith(String desc) {
if (desc == null) {
throw new IllegalArgumentException("desc cannot be null");
}
return buildSelector(SELECTOR_START_DESCRIPTION, desc);
}
/**
* Set the search criteria to match the content-description
* property for a widget.
*
* The content-description is typically used
* by the Android Accessibility framework to
* provide an audio prompt for the widget when
* the widget is selected. The content-description
* for the widget must contain
* the string in your input argument.
*
* Matching is case-insensitive.
*
* @param desc Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector descriptionContains(String desc) {
if (desc == null) {
throw new IllegalArgumentException("desc cannot be null");
}
return buildSelector(SELECTOR_CONTAINS_DESCRIPTION, desc);
}
/**
* Set the search criteria to match the given resource ID.
*
* @param id Value to match
* @return UiSelector with the specified search criteria
* @since API Level 18
*/
public UiSelector resourceId(String id) {
if (id == null) {
throw new IllegalArgumentException("id cannot be null");
}
return buildSelector(SELECTOR_RESOURCE_ID, id);
}
/**
* Set the search criteria to match the resource ID
* of the widget, using a regular expression.
*
* @param regex a regular expression
* @return UiSelector with the specified search criteria
* @since API Level 18
*/
public UiSelector resourceIdMatches(String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
return buildSelector(SELECTOR_RESOURCE_ID_REGEX, Pattern.compile(regex));
}
/**
* Set the search criteria to match the widget by its node
* index in the layout hierarchy.
*
* The index value must be 0 or greater.
*
* Using the index can be unreliable and should only
* be used as a last resort for matching. Instead,
* consider using the {@link #instance(int)} method.
*
* @param index Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector index(final int index) {
return buildSelector(SELECTOR_INDEX, index);
}
/**
* Set the search criteria to match the
* widget by its instance number.
*
* The instance value must be 0 or greater, where
* the first instance is 0.
*
* For example, to simulate a user click on
* the third image that is enabled in a UI screen, you
* could specify a a search criteria where the instance is
* 2, the {@link #clreplacedName(String)} matches the image
* widget clreplaced, and {@link #enabled(boolean)} is true.
* The code would look like this:
* <code>
* new UiSelector().clreplacedName("android.widget.ImageView")
* .enabled(true).instance(2);
* </code>
*
* @param instance Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector instance(final int instance) {
return buildSelector(SELECTOR_INSTANCE, instance);
}
/**
* Set the search criteria to match widgets that are enabled.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector enabled(boolean val) {
return buildSelector(SELECTOR_ENABLED, val);
}
/**
* Set the search criteria to match widgets that have focus.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector focused(boolean val) {
return buildSelector(SELECTOR_FOCUSED, val);
}
/**
* Set the search criteria to match widgets that are focusable.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector focusable(boolean val) {
return buildSelector(SELECTOR_FOCUSABLE, val);
}
/**
* Set the search criteria to match widgets that are scrollable.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector scrollable(boolean val) {
return buildSelector(SELECTOR_SCROLLABLE, val);
}
/**
* Set the search criteria to match widgets that
* are currently selected.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector selected(boolean val) {
return buildSelector(SELECTOR_SELECTED, val);
}
/**
* Set the search criteria to match widgets that
* are currently checked (usually for checkboxes).
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector checked(boolean val) {
return buildSelector(SELECTOR_CHECKED, val);
}
/**
* Set the search criteria to match widgets that are clickable.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector clickable(boolean val) {
return buildSelector(SELECTOR_CLICKABLE, val);
}
/**
* Set the search criteria to match widgets that are checkable.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 18
*/
public UiSelector checkable(boolean val) {
return buildSelector(SELECTOR_CHECKABLE, val);
}
/**
* Set the search criteria to match widgets that are long-clickable.
*
* Typically, using this search criteria alone is not useful.
* You should also include additional criteria, such as text,
* content-description, or the clreplaced name for a widget.
*
* If no other search criteria is specified, and there is more
* than one matching widget, the first widget in the tree
* is selected.
*
* @param val Value to match
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
public UiSelector longClickable(boolean val) {
return buildSelector(SELECTOR_LONG_CLICKABLE, val);
}
/**
* Adds a child UiSelector criteria to this selector.
*
* Use this selector to narrow the search scope to
* child widgets under a specific parent widget.
*
* @param selector
* @return UiSelector with this added search criterion
* @since API Level 16
*/
public UiSelector childSelector(UiSelector selector) {
if (selector == null) {
throw new IllegalArgumentException("selector cannot be null");
}
return buildSelector(SELECTOR_CHILD, selector);
}
private UiSelector patternSelector(UiSelector selector) {
return buildSelector(SELECTOR_PATTERN, selector);
}
private UiSelector containerSelector(UiSelector selector) {
return buildSelector(SELECTOR_CONTAINER, selector);
}
/**
* Adds a child UiSelector criteria to this selector which is used to
* start search from the parent widget.
*
* Use this selector to narrow the search scope to
* sibling widgets as well all child widgets under a parent.
*
* @param selector
* @return UiSelector with this added search criterion
* @since API Level 16
*/
public UiSelector fromParent(UiSelector selector) {
if (selector == null) {
throw new IllegalArgumentException("selector cannot be null");
}
return buildSelector(SELECTOR_PARENT, selector);
}
/**
* Set the search criteria to match the package name
* of the application that contains the widget.
*
* @param name Value to match
* @return UiSelector with the specified search criteria
* @since API Level 16
*/
public UiSelector packageName(String name) {
if (name == null) {
throw new IllegalArgumentException("name cannot be null");
}
return buildSelector(SELECTOR_PACKAGE_NAME, name);
}
/**
* Set the search criteria to match the package name
* of the application that contains the widget.
*
* @param regex a regular expression
* @return UiSelector with the specified search criteria
* @since API Level 17
*/
public UiSelector packageNameMatches(String regex) {
if (regex == null) {
throw new IllegalArgumentException("regex cannot be null");
}
return buildSelector(SELECTOR_PACKAGE_NAME_REGEX, Pattern.compile(regex));
}
/**
* Building a UiSelector always returns a new UiSelector and never modifies the
* existing UiSelector being used.
*/
private UiSelector buildSelector(int selectorId, Object selectorValue) {
UiSelector selector = new UiSelector(this);
if (selectorId == SELECTOR_CHILD || selectorId == SELECTOR_PARENT)
selector.getLastSubSelector().mSelectorAttributes.put(selectorId, selectorValue);
else
selector.mSelectorAttributes.put(selectorId, selectorValue);
return selector;
}
/**
* Selectors may have a hierarchy defined by specifying child nodes to be matched.
* It is not necessary that every selector have more than one level. A selector
* can also be a single level referencing only one node. In such cases the return
* it null.
*
* @return a child selector if one exists. Else null if this selector does not
* reference child node.
*/
UiSelector getChildSelector() {
UiSelector selector = (UiSelector) mSelectorAttributes.get(UiSelector.SELECTOR_CHILD, null);
if (selector != null)
return new UiSelector(selector);
return null;
}
UiSelector getPatternSelector() {
UiSelector selector = (UiSelector) mSelectorAttributes.get(UiSelector.SELECTOR_PATTERN, null);
if (selector != null)
return new UiSelector(selector);
return null;
}
UiSelector getContainerSelector() {
UiSelector selector = (UiSelector) mSelectorAttributes.get(UiSelector.SELECTOR_CONTAINER, null);
if (selector != null)
return new UiSelector(selector);
return null;
}
UiSelector getParentSelector() {
UiSelector selector = (UiSelector) mSelectorAttributes.get(UiSelector.SELECTOR_PARENT, null);
if (selector != null)
return new UiSelector(selector);
return null;
}
int getInstance() {
return getInt(UiSelector.SELECTOR_INSTANCE);
}
String getString(int criterion) {
return (String) mSelectorAttributes.get(criterion, null);
}
boolean getBoolean(int criterion) {
return (Boolean) mSelectorAttributes.get(criterion, false);
}
int getInt(int criterion) {
return (Integer) mSelectorAttributes.get(criterion, 0);
}
Pattern getPattern(int criterion) {
return (Pattern) mSelectorAttributes.get(criterion, null);
}
boolean isMatchFor(AccessibilityNodeInfo node, int index) {
int size = mSelectorAttributes.size();
for (int x = 0; x < size; x++) {
CharSequence s = null;
int criterion = mSelectorAttributes.keyAt(x);
switch(criterion) {
case UiSelector.SELECTOR_INDEX:
if (index != this.getInt(criterion))
return false;
break;
case UiSelector.SELECTOR_CHECKED:
if (node.isChecked() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_CLreplaced:
s = node.getClreplacedName();
if (s == null || !s.toString().contentEquals(getString(criterion))) {
return false;
}
break;
case UiSelector.SELECTOR_CLreplaced_REGEX:
s = node.getClreplacedName();
if (s == null || !getPattern(criterion).matcher(s).matches()) {
return false;
}
break;
case UiSelector.SELECTOR_CLICKABLE:
if (node.isClickable() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_CHECKABLE:
if (node.isCheckable() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_LONG_CLICKABLE:
if (node.isLongClickable() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_CONTAINS_DESCRIPTION:
s = node.getContentDescription();
if (s == null || !s.toString().toLowerCase().contains(getString(criterion).toLowerCase())) {
return false;
}
break;
case UiSelector.SELECTOR_START_DESCRIPTION:
s = node.getContentDescription();
if (s == null || !s.toString().toLowerCase().startsWith(getString(criterion).toLowerCase())) {
return false;
}
break;
case UiSelector.SELECTOR_DESCRIPTION:
s = node.getContentDescription();
if (s == null || !s.toString().contentEquals(getString(criterion))) {
return false;
}
break;
case UiSelector.SELECTOR_DESCRIPTION_REGEX:
s = node.getContentDescription();
if (s == null || !getPattern(criterion).matcher(s).matches()) {
return false;
}
break;
case UiSelector.SELECTOR_CONTAINS_TEXT:
s = node.getText();
if (s == null || !s.toString().toLowerCase().contains(getString(criterion).toLowerCase())) {
return false;
}
break;
case UiSelector.SELECTOR_START_TEXT:
s = node.getText();
if (s == null || !s.toString().toLowerCase().startsWith(getString(criterion).toLowerCase())) {
return false;
}
break;
case UiSelector.SELECTOR_TEXT:
s = node.getText();
if (s == null || !s.toString().contentEquals(getString(criterion))) {
return false;
}
break;
case UiSelector.SELECTOR_TEXT_REGEX:
s = node.getText();
if (s == null || !getPattern(criterion).matcher(s).matches()) {
return false;
}
break;
case UiSelector.SELECTOR_ENABLED:
if (node.isEnabled() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_FOCUSABLE:
if (node.isFocusable() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_FOCUSED:
if (node.isFocused() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_ID:
// TODO: do we need this for AccessibilityNodeInfo.id?
break;
case UiSelector.SELECTOR_PACKAGE_NAME:
s = node.getPackageName();
if (s == null || !s.toString().contentEquals(getString(criterion))) {
return false;
}
break;
case UiSelector.SELECTOR_PACKAGE_NAME_REGEX:
s = node.getPackageName();
if (s == null || !getPattern(criterion).matcher(s).matches()) {
return false;
}
break;
case UiSelector.SELECTOR_SCROLLABLE:
if (node.isScrollable() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_SELECTED:
if (node.isSelected() != getBoolean(criterion)) {
return false;
}
break;
case UiSelector.SELECTOR_RESOURCE_ID:
s = node.getViewIdResourceName();
if (s == null || !s.toString().contentEquals(getString(criterion))) {
return false;
}
break;
case UiSelector.SELECTOR_RESOURCE_ID_REGEX:
s = node.getViewIdResourceName();
if (s == null || !getPattern(criterion).matcher(s).matches()) {
return false;
}
break;
}
}
return matchOrUpdateInstance();
}
private boolean matchOrUpdateInstance() {
int currentSelectorCounter = 0;
int currentSelectorInstance = 0;
// matched attributes - now check for matching instance number
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_INSTANCE) >= 0) {
currentSelectorInstance = (Integer) mSelectorAttributes.get(UiSelector.SELECTOR_INSTANCE);
}
// instance is required. Add count if not already counting
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_COUNT) >= 0) {
currentSelectorCounter = (Integer) mSelectorAttributes.get(UiSelector.SELECTOR_COUNT);
}
// Verify
if (currentSelectorInstance == currentSelectorCounter) {
return true;
}
// Update count
if (currentSelectorInstance > currentSelectorCounter) {
mSelectorAttributes.put(UiSelector.SELECTOR_COUNT, ++currentSelectorCounter);
}
return false;
}
/**
* Leaf selector indicates no more child or parent selectors
* are declared in the this selector.
* @return true if is leaf.
*/
boolean isLeaf() {
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CHILD) < 0 && mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PARENT) < 0) {
return true;
}
return false;
}
boolean hasChildSelector() {
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CHILD) < 0) {
return false;
}
return true;
}
boolean hasPatternSelector() {
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PATTERN) < 0) {
return false;
}
return true;
}
boolean hasContainerSelector() {
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CONTAINER) < 0) {
return false;
}
return true;
}
boolean hasParentSelector() {
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PARENT) < 0) {
return false;
}
return true;
}
/**
* Returns the deepest selector in the chain of possible sub selectors.
* A chain of selector is created when either of {@link UiSelector#childSelector(UiSelector)}
* or {@link UiSelector#fromParent(UiSelector)} are used once or more in the construction of
* a selector.
* @return last UiSelector in chain
*/
private UiSelector getLastSubSelector() {
if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_CHILD) >= 0) {
UiSelector child = (UiSelector) mSelectorAttributes.get(UiSelector.SELECTOR_CHILD);
if (child.getLastSubSelector() == null) {
return child;
}
return child.getLastSubSelector();
} else if (mSelectorAttributes.indexOfKey(UiSelector.SELECTOR_PARENT) >= 0) {
UiSelector parent = (UiSelector) mSelectorAttributes.get(UiSelector.SELECTOR_PARENT);
if (parent.getLastSubSelector() == null) {
return parent;
}
return parent.getLastSubSelector();
}
return this;
}
@Override
public String toString() {
return dumpToString(true);
}
String dumpToString(boolean all) {
StringBuilder builder = new StringBuilder();
builder.append(UiSelector.clreplaced.getSimpleName() + "[");
final int criterionCount = mSelectorAttributes.size();
for (int i = 0; i < criterionCount; i++) {
if (i > 0) {
builder.append(", ");
}
final int criterion = mSelectorAttributes.keyAt(i);
switch(criterion) {
case SELECTOR_TEXT:
builder.append("TEXT=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_TEXT_REGEX:
builder.append("TEXT_REGEX=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_START_TEXT:
builder.append("START_TEXT=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CONTAINS_TEXT:
builder.append("CONTAINS_TEXT=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CLreplaced:
builder.append("CLreplaced=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CLreplaced_REGEX:
builder.append("CLreplaced_REGEX=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_DESCRIPTION:
builder.append("DESCRIPTION=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_DESCRIPTION_REGEX:
builder.append("DESCRIPTION_REGEX=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_START_DESCRIPTION:
builder.append("START_DESCRIPTION=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CONTAINS_DESCRIPTION:
builder.append("CONTAINS_DESCRIPTION=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_INDEX:
builder.append("INDEX=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_INSTANCE:
builder.append("INSTANCE=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_ENABLED:
builder.append("ENABLED=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_FOCUSED:
builder.append("FOCUSED=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_FOCUSABLE:
builder.append("FOCUSABLE=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_SCROLLABLE:
builder.append("SCROLLABLE=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CLICKABLE:
builder.append("CLICKABLE=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CHECKABLE:
builder.append("CHECKABLE=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_LONG_CLICKABLE:
builder.append("LONG_CLICKABLE=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CHECKED:
builder.append("CHECKED=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_SELECTED:
builder.append("SELECTED=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_ID:
builder.append("ID=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_CHILD:
if (all)
builder.append("CHILD=").append(mSelectorAttributes.valueAt(i));
else
builder.append("CHILD[..]");
break;
case SELECTOR_PATTERN:
if (all)
builder.append("PATTERN=").append(mSelectorAttributes.valueAt(i));
else
builder.append("PATTERN[..]");
break;
case SELECTOR_CONTAINER:
if (all)
builder.append("CONTAINER=").append(mSelectorAttributes.valueAt(i));
else
builder.append("CONTAINER[..]");
break;
case SELECTOR_PARENT:
if (all)
builder.append("PARENT=").append(mSelectorAttributes.valueAt(i));
else
builder.append("PARENT[..]");
break;
case SELECTOR_COUNT:
builder.append("COUNT=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_PACKAGE_NAME:
builder.append("PACKAGE NAME=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_PACKAGE_NAME_REGEX:
builder.append("PACKAGE_NAME_REGEX=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_RESOURCE_ID:
builder.append("RESOURCE_ID=").append(mSelectorAttributes.valueAt(i));
break;
case SELECTOR_RESOURCE_ID_REGEX:
builder.append("RESOURCE_ID_REGEX=").append(mSelectorAttributes.valueAt(i));
break;
default:
builder.append("UNDEFINED=" + criterion + " ").append(mSelectorAttributes.valueAt(i));
}
}
builder.append("]");
return builder.toString();
}
}
19
Source : SugarAdapter.java
with Apache License 2.0
from zhihu
with Apache License 2.0
from zhihu
@SuppressWarnings({ "unused", "WeakerAccess" })
public final clreplaced SugarAdapter extends RecyclerView.Adapter<SugarHolder> {
private static final String TAG = "SugarAdapter";
public static final clreplaced Builder {
private List<?> mList;
private SparseArray<Container> mContainerArray;
private boolean mPreInflate;
@NonNull
public static Builder with(@NonNull List<?> list) {
return new Builder(list);
}
private Builder(@NonNull List<?> list) {
mList = list;
mContainerArray = new SparseArray<>();
}
@NonNull
public <SH extends SugarHolder> Builder add(@NonNull Clreplaced<SH> holderClreplaced) {
return add(holderClreplaced, null);
}
@NonNull
public <SH extends SugarHolder> Builder add(@NonNull Clreplaced<SH> holderClreplaced, @Nullable SugarHolder.OnCreatedCallback<SH> callback) {
ContainerDelegate delegate = Sugar.INSTANCE.getContainerDelegate();
Clreplaced dataClreplaced = delegate.getDataClreplaced(holderClreplaced);
int layoutRes = delegate.getLayoutRes(holderClreplaced);
if (layoutRes == 0) {
throw new IllegalStateException(holderClreplaced.getCanonicalName() + " must have an annotation @Layout(R.layout.*)");
}
mContainerArray.put(holderClreplaced.hashCode(), new Container(holderClreplaced, dataClreplaced, layoutRes, callback));
return this;
}
@NonNull
public <SH extends SugarHolder> Builder preInflate(boolean enable) {
mPreInflate = enable;
return this;
}
@NonNull
public SugarAdapter build() {
if (mContainerArray.size() <= 0) {
throw new IllegalStateException("must add at least one Clreplaced<? extends SugarHolder>");
}
return new SugarAdapter(mList, mContainerArray, mPreInflate);
}
}
public interface ExtraDelegate {
void onAttachedToRecyclerView(@NonNull RecyclerView view);
void onDetachedFromRecyclerView(@NonNull RecyclerView view);
}
public interface PreInflateListener {
void onPreInflateExecuted(@LayoutRes int layoutRes);
void onPreInflateConsumed(@LayoutRes int layoutRes, boolean fallback);
}
public static abstract clreplaced Dispatcher<T> {
// return null to use default rule
@Nullable
public abstract Clreplaced<? extends SugarHolder> dispatch(@NonNull T data);
// https://stackoverflow.com/q/3437897
@Deprecated
@NonNull
private Clreplaced<T> ofType() {
try {
// noinspection ConstantConditions
String clreplacedName = ((ParameterizedType) getClreplaced().getGenericSuperclreplaced()).getActualTypeArguments()[0].toString().split(" ")[1];
// noinspection unchecked
return (Clreplaced<T>) Clreplaced.forName(clreplacedName);
} catch (@NonNull Exception e) {
throw new RuntimeException(e);
}
}
}
public static abstract clreplaced SugarHolderListener<SH extends SugarHolder> {
private Clreplaced<SH> mSugarHolderClreplaced = ofType();
// https://stackoverflow.com/q/3437897
@NonNull
private Clreplaced<SH> ofType() {
try {
// noinspection ConstantConditions
String clreplacedName = ((ParameterizedType) getClreplaced().getGenericSuperclreplaced()).getActualTypeArguments()[0].toString().split(" ")[1];
// noinspection unchecked
return (Clreplaced<SH>) Clreplaced.forName(clreplacedName);
} catch (@NonNull Exception e) {
throw new RuntimeException(e);
}
}
private boolean isInstance(@Nullable Object object) {
return mSugarHolderClreplaced.isInstance(object);
}
public void onSugarHolderCreated(@NonNull SH holder) {
}
public void onSugarHolderBindData(@NonNull SH holder) {
}
public void onSugarHolderViewAttachedToWindow(@NonNull SH holder) {
}
public void onSugarHolderViewDetachedFromWindow(@NonNull SH holder) {
}
public void onSugarHolderViewRecycled(@NonNull SH holder) {
}
}
private List<?> mList;
private SparseArray<Container> mContainerArray;
private Map<Clreplaced<?>, Dispatcher<?>> mDispatcherMap;
private List<ExtraDelegate> mExtraDelegateList;
private List<PreInflateListener> mPreInflateListenerList;
private List<SugarHolderListener<?>> mSugarHolderListenerList;
private MessageQueue.IdleHandler mPreInflateHandler;
private SparseArray<View> mPreInflateArray;
private SugarAdapter(@NonNull List<?> list, @NonNull SparseArray<Container> containerArray, boolean preInflate) {
mList = list;
mContainerArray = containerArray;
mDispatcherMap = new HashMap<>();
mExtraDelegateList = new ArrayList<>();
mPreInflateListenerList = new ArrayList<>();
mSugarHolderListenerList = new ArrayList<>();
if (preInflate) {
mPreInflateArray = new SparseArray<>();
for (int i = 0; i < mContainerArray.size(); i++) {
int key = mContainerArray.keyAt(i);
Container container = mContainerArray.get(key);
mPreInflateArray.put(container.getLayoutRes(), null);
}
}
}
// <editor-fold desc="Dispatcher">
@Deprecated
@NonNull
public <T> SugarAdapter addDispatcher(@NonNull Dispatcher<T> dispatcher) {
return addDispatcher(dispatcher.ofType(), dispatcher);
}
@NonNull
public <T> SugarAdapter addDispatcher(@NonNull Clreplaced<T> clazz, @NonNull Dispatcher<T> dispatcher) {
if (mDispatcherMap.containsKey(clazz)) {
Log.d(TAG, "addDispatcher repeated" + ", SugarAdapter already has a dispatcher of " + clazz.getCanonicalName() + ", new dispatcher will cover the old one.");
}
mDispatcherMap.put(clazz, dispatcher);
return this;
}
@Deprecated
@NonNull
public <T> SugarAdapter removeDispatcher(@NonNull Dispatcher<T> dispatcher) {
return removeDispatcher(dispatcher.ofType());
}
@NonNull
public <T> SugarAdapter removeDispatcher(@NonNull Clreplaced<T> clazz) {
mDispatcherMap.remove(clazz);
return this;
}
@NonNull
public SugarAdapter clearDispatcher() {
mDispatcherMap.clear();
return this;
}
// </editor-fold>
// <editor-fold desc="ExtraDelegate">
@NonNull
public SugarAdapter addExtraDelegate(@NonNull ExtraDelegate delegate) {
if (!mExtraDelegateList.contains(delegate)) {
mExtraDelegateList.add(delegate);
}
return this;
}
@NonNull
public SugarAdapter removeExtraDelegate(@NonNull ExtraDelegate delegate) {
mExtraDelegateList.remove(delegate);
return this;
}
@NonNull
public SugarAdapter clearExtraDelegate() {
mExtraDelegateList.clear();
return this;
}
// </editor-fold>
// <editor-fold desc="PreInflateListener">
@NonNull
public SugarAdapter addPreInflateListener(@NonNull PreInflateListener listener) {
if (!mPreInflateListenerList.contains(listener)) {
mPreInflateListenerList.add(listener);
}
return this;
}
@NonNull
public SugarAdapter removePreInflateListener(@NonNull PreInflateListener listener) {
mPreInflateListenerList.remove(listener);
return this;
}
@NonNull
public SugarAdapter clearPreInflateListener() {
mPreInflateListenerList.clear();
return this;
}
@NonNull
public SugarAdapter clearPreInflateViews() {
if (mPreInflateArray != null) {
mPreInflateArray.clear();
}
return this;
}
// </editor-fold>
// <editor-fold desc="SugarHolderListener">
@SuppressWarnings("UnusedReturnValue")
@NonNull
public <SH extends SugarHolder> SugarAdapter addSugarHolderListener(@NonNull SugarHolderListener<SH> listener) {
if (!mSugarHolderListenerList.contains(listener)) {
mSugarHolderListenerList.add(listener);
}
return this;
}
@NonNull
public SugarAdapter removeSugarHolderListener(@NonNull SugarHolderListener<?> listener) {
mSugarHolderListenerList.remove(listener);
return this;
}
@NonNull
public SugarAdapter clearSugarHolderListener() {
mSugarHolderListenerList.clear();
return this;
}
// </editor-fold>
@NonNull
public List<?> getList() {
return mList;
}
@Override
public int gereplacedemCount() {
return mList.size();
}
@SuppressWarnings({ "ConstantConditions", "unchecked" })
@Override
public int gereplacedemViewType(@IntRange(from = 0) int position) {
Object data = mList.get(position);
Clreplaced<? extends SugarHolder> holderClreplaced = null;
if (mDispatcherMap.containsKey(data.getClreplaced())) {
Dispatcher dispatcher = mDispatcherMap.get(data.getClreplaced());
holderClreplaced = dispatcher.dispatch(data);
}
if (holderClreplaced != null) {
int key = holderClreplaced.hashCode();
if (mContainerArray.indexOfKey(key) < 0) {
throw new RuntimeException("gereplacedemViewType() failed, holder: " + holderClreplaced.getCanonicalName() + ", please make sure you have added it when build SugarAdapter.");
}
mContainerArray.get(key).setData(data);
return key;
}
for (int i = 0; i < mContainerArray.size(); i++) {
int key = mContainerArray.keyAt(i);
Container container = mContainerArray.get(key);
if (container.getDataClreplaced() == data.getClreplaced()) {
container.setData(data);
return key;
}
}
throw new RuntimeException("gereplacedemViewType() failed, data: " + data.getClreplaced().getCanonicalName() + ", please make sure you have replacedociated it with a Clreplaced<? extends SugarHolder>");
}
@SuppressWarnings("unchecked")
@Override
@NonNull
public SugarHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Container container = mContainerArray.get(viewType);
try {
View view = null;
int layoutRes = container.getLayoutRes();
if (mPreInflateArray != null) {
view = mPreInflateArray.get(layoutRes);
// preInflate the layoutRes when next MainThread idle in case for needed
mPreInflateArray.put(layoutRes, null);
for (PreInflateListener listener : mPreInflateListenerList) {
if (listener != null) {
listener.onPreInflateConsumed(layoutRes, view == null);
}
}
}
if (view == null) {
view = inflateView(layoutRes, parent);
}
SugarHolder holder = container.getHolderClreplaced().getDeclaredConstructor(View.clreplaced).newInstance(view);
holder.setAdapter(this);
// makes SugarHolder#getData non-null
holder.setData(container.getData());
SugarHolder.OnCreatedCallback callback = container.getCallback();
if (callback != null) {
callback.onCreated(holder);
}
holder.getLifecycleRegistry().handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
for (SugarHolderListener listener : mSugarHolderListenerList) {
if (listener.isInstance(holder)) {
listener.onSugarHolderCreated(holder);
}
}
return holder;
} catch (@NonNull Exception e) {
Log.e(TAG, "onCreateViewHolder failed, holder: " + container.getHolderClreplaced().getCanonicalName());
throw new RuntimeException(e);
}
}
@NonNull
private View inflateView(@LayoutRes int layoutRes, @NonNull ViewGroup parent) {
return LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false);
}
@Override
public void onBindViewHolder(@NonNull SugarHolder holder, int position, @NonNull List<Object> payloads) {
onBindViewHolderInternal(holder, position, payloads);
}
@Override
public void onBindViewHolder(@NonNull SugarHolder holder, int position) {
onBindViewHolderInternal(holder, position, null);
}
@SuppressWarnings("unchecked")
private void onBindViewHolderInternal(@NonNull SugarHolder holder, int position, @Nullable List<Object> payloads) {
Object data = mList.get(position);
// double check
holder.setData(data);
if (payloads == null || payloads.isEmpty()) {
holder.onBindData(data, Collections.emptyList());
} else {
holder.onBindData(data, payloads);
}
holder.getLifecycleRegistry().handleLifecycleEvent(Lifecycle.Event.ON_START);
for (SugarHolderListener listener : mSugarHolderListenerList) {
if (listener.isInstance(holder)) {
listener.onSugarHolderBindData(holder);
}
}
}
@Override
public void onViewAttachedToWindow(@NonNull SugarHolder holder) {
// holder.onViewAttachedToWindow();
}
@Override
public void onViewDetachedFromWindow(@NonNull SugarHolder holder) {
// holder.onViewDetachedFromWindow();
}
@SuppressWarnings("unchecked")
@Override
public void onViewRecycled(@NonNull SugarHolder holder) {
holder.onViewRecycled();
holder.getLifecycleRegistry().handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
for (SugarHolderListener listener : mSugarHolderListenerList) {
if (listener.isInstance(holder)) {
listener.onSugarHolderViewRecycled(holder);
}
}
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView view) {
// preInflate XML when MainThread idle
if (mPreInflateArray != null && mPreInflateHandler == null) {
mPreInflateHandler = () -> {
for (int i = 0; i < mPreInflateArray.size(); i++) {
int layoutRes = mPreInflateArray.keyAt(i);
if (mPreInflateArray.get(layoutRes) == null) {
mPreInflateArray.put(layoutRes, inflateView(layoutRes, view));
for (PreInflateListener listener : mPreInflateListenerList) {
if (listener != null) {
listener.onPreInflateExecuted(layoutRes);
}
}
// only one at a time, avoid blocking MainThread
break;
}
}
return true;
};
Looper.myQueue().addIdleHandler(mPreInflateHandler);
}
for (ExtraDelegate delegate : mExtraDelegateList) {
if (delegate != null) {
delegate.onAttachedToRecyclerView(view);
}
}
}
@Override
public void onDetachedFromRecyclerView(@NonNull RecyclerView view) {
if (mPreInflateHandler != null) {
Looper.myQueue().removeIdleHandler(mPreInflateHandler);
mPreInflateHandler = null;
}
for (ExtraDelegate delegate : mExtraDelegateList) {
if (delegate != null) {
delegate.onDetachedFromRecyclerView(view);
}
}
}
@SuppressWarnings("unchecked")
protected void onSugarHolderViewAttachedToWindow(@NonNull SugarHolder holder) {
for (SugarHolderListener listener : mSugarHolderListenerList) {
if (listener.isInstance(holder)) {
listener.onSugarHolderViewAttachedToWindow(holder);
}
}
}
@SuppressWarnings("unchecked")
protected void onSugarHolderViewDetachedFromWindow(@NonNull SugarHolder holder) {
for (SugarHolderListener listener : mSugarHolderListenerList) {
if (listener.isInstance(holder)) {
listener.onSugarHolderViewDetachedFromWindow(holder);
}
}
}
@Override
public boolean onFailedToRecycleView(@NonNull SugarHolder holder) {
return holder.onFailedToRecycleView();
}
}
19
Source : Notification.java
with MIT License
from zhangyd-c
with MIT License
from zhangyd-c
/**
* Wrapper clreplaced around OS notification clreplaced. Handles basic operations
* like show, delete, cancel for a single local notification instance.
*/
public final clreplaced Notification {
// Used to differ notifications by their life cycle state
public enum Type {
ALL, SCHEDULED, TRIGGERED
}
// Extra key for the id
public static final String EXTRA_ID = "NOTIFICATION_ID";
// Extra key for the update flag
public static final String EXTRA_UPDATE = "NOTIFICATION_UPDATE";
// Key for private preferences
static final String PREF_KEY_ID = "NOTIFICATION_ID";
// Key for private preferences
private static final String PREF_KEY_PID = "NOTIFICATION_PID";
// Cache for the builder instances
private static SparseArray<NotificationCompat.Builder> cache = null;
// Application context preplaceded by constructor
private final Context context;
// Notification options preplaceded by JS
private final Options options;
// Builder with full configuration
private final NotificationCompat.Builder builder;
/**
* Constructor
*
* @param context Application context.
* @param options Parsed notification options.
* @param builder Pre-configured notification builder.
*/
Notification(Context context, Options options, NotificationCompat.Builder builder) {
this.context = context;
this.options = options;
this.builder = builder;
}
/**
* Constructor
*
* @param context Application context.
* @param options Parsed notification options.
*/
public Notification(Context context, Options options) {
this.context = context;
this.options = options;
this.builder = null;
}
/**
* Get application context.
*/
public Context getContext() {
return context;
}
/**
* Get notification options.
*/
public Options getOptions() {
return options;
}
/**
* Get notification ID.
*/
public int getId() {
return options.getId();
}
/**
* If it's a repeating notification.
*/
private boolean isRepeating() {
return getOptions().getTrigger().has("every");
}
/**
* Notification type can be one of triggered or scheduled.
*/
public Type getType() {
Manager mgr = Manager.getInstance(context);
StatusBarNotification[] toasts = mgr.getActiveNotifications();
int id = getId();
for (StatusBarNotification toast : toasts) {
if (toast.getId() == id) {
return Type.TRIGGERED;
}
}
return Type.SCHEDULED;
}
/**
* Schedule the local notification.
*
* @param request Set of notification options.
* @param receiver Receiver to handle the trigger event.
*/
void schedule(Request request, Clreplaced<?> receiver) {
List<Pair<Date, Intent>> intents = new ArrayList<Pair<Date, Intent>>();
Set<String> ids = new ArraySet<String>();
AlarmManager mgr = getAlarmMgr();
cancelScheduledAlarms();
do {
Date date = request.getTriggerDate();
if (date == null)
continue;
Intent intent = new Intent(context, receiver).setAction(PREF_KEY_ID + request.getIdentifier()).putExtra(Notification.EXTRA_ID, options.getId()).putExtra(Request.EXTRA_OCCURRENCE, request.getOccurrence());
ids.add(intent.getAction());
intents.add(new Pair<Date, Intent>(date, intent));
} while (request.moveNext());
if (intents.isEmpty()) {
unpersist();
return;
}
persist(ids);
if (!options.isInfiniteTrigger()) {
Intent last = intents.get(intents.size() - 1).second;
last.putExtra(Request.EXTRA_LAST, true);
}
for (Pair<Date, Intent> pair : intents) {
Date date = pair.first;
long time = date.getTime();
Intent intent = pair.second;
if (!date.after(new Date()) && trigger(intent, receiver))
continue;
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, FLAG_CANCEL_CURRENT);
try {
switch(options.getPriority()) {
case IMPORTANCE_MIN:
mgr.setExact(RTC, time, pi);
break;
case IMPORTANCE_MAX:
if (SDK_INT >= M) {
mgr.setExactAndAllowWhileIdle(RTC_WAKEUP, time, pi);
} else {
mgr.setExact(RTC, time, pi);
}
break;
default:
mgr.setExact(RTC_WAKEUP, time, pi);
break;
}
} catch (Exception ignore) {
// Samsung devices have a known bug where a 500 alarms limit
// can crash the app
}
}
}
/**
* Trigger local notification specified by options.
*
* @param intent The intent to broadcast.
* @param cls The broadcast clreplaced.
*
* @return false if the receiver could not be invoked.
*/
private boolean trigger(Intent intent, Clreplaced<?> cls) {
BroadcastReceiver receiver;
try {
receiver = (BroadcastReceiver) cls.newInstance();
} catch (InstantiationException e) {
return false;
} catch (IllegalAccessException e) {
return false;
}
receiver.onReceive(context, intent);
return true;
}
/**
* Clear the local notification without canceling repeating alarms.
*/
public void clear() {
getNotMgr().cancel(getId());
if (isRepeating())
return;
unpersist();
}
/**
* Cancel the local notification.
*/
public void cancel() {
cancelScheduledAlarms();
unpersist();
getNotMgr().cancel(getId());
clearCache();
}
/**
* Cancel the scheduled future local notification.
*
* Create an intent that looks similar, to the one that was registered
* using schedule. Making sure the notification id in the action is the
* same. Now we can search for such an intent using the 'getService'
* method and cancel it.
*/
private void cancelScheduledAlarms() {
SharedPreferences prefs = getPrefs(PREF_KEY_PID);
String id = options.getIdentifier();
Set<String> actions = prefs.getStringSet(id, null);
if (actions == null)
return;
for (String action : actions) {
Intent intent = new Intent(action);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
if (pi != null) {
getAlarmMgr().cancel(pi);
}
}
}
/**
* Present the local notification to user.
*/
public void show() {
if (builder == null)
return;
if (options.isWithProgressBar()) {
cacheBuilder();
}
grantPermissionToPlaySoundFromExternal();
getNotMgr().notify(getId(), builder.build());
}
/**
* Update the notification properties.
*
* @param updates The properties to update.
* @param receiver Receiver to handle the trigger event.
*/
void update(JSONObject updates, Clreplaced<?> receiver) {
mergeJSONObjects(updates);
persist(null);
if (getType() != Type.TRIGGERED)
return;
Intent intent = new Intent(context, receiver).setAction(PREF_KEY_ID + options.getId()).putExtra(Notification.EXTRA_ID, options.getId()).putExtra(Notification.EXTRA_UPDATE, true);
trigger(intent, receiver);
}
/**
* Encode options to JSON.
*/
public String toString() {
JSONObject dict = options.getDict();
JSONObject json = new JSONObject();
try {
json = new JSONObject(dict.toString());
} catch (JSONException e) {
e.printStackTrace();
}
return json.toString();
}
/**
* Persist the information of this notification to the Android Shared
* Preferences. This will allow the application to restore the notification
* upon device reboot, app restart, retrieve notifications, aso.
*
* @param ids List of intent actions to persist.
*/
private void persist(Set<String> ids) {
String id = options.getIdentifier();
SharedPreferences.Editor editor;
editor = getPrefs(PREF_KEY_ID).edit();
editor.putString(id, options.toString());
editor.apply();
if (ids == null)
return;
editor = getPrefs(PREF_KEY_PID).edit();
editor.putStringSet(id, ids);
editor.apply();
}
/**
* Remove the notification from the Android shared Preferences.
*/
private void unpersist() {
String[] keys = { PREF_KEY_ID, PREF_KEY_PID };
String id = options.getIdentifier();
SharedPreferences.Editor editor;
for (String key : keys) {
editor = getPrefs(key).edit();
editor.remove(id);
editor.apply();
}
}
/**
* Since Android 7 the app will crash if an external process has no
* permission to access the referenced sound file.
*/
private void grantPermissionToPlaySoundFromExternal() {
if (builder == null)
return;
String sound = builder.getExtras().getString(Options.EXTRA_SOUND);
Uri soundUri = Uri.parse(sound);
context.grantUriPermission("com.android.systemui", soundUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
/**
* Merge two JSON objects.
*/
private void mergeJSONObjects(JSONObject updates) {
JSONObject dict = options.getDict();
Iterator it = updates.keys();
while (it.hasNext()) {
try {
String key = (String) it.next();
dict.put(key, updates.opt(key));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
/**
* Caches the builder instance so it can be used later.
*/
private void cacheBuilder() {
if (cache == null) {
cache = new SparseArray<NotificationCompat.Builder>();
}
cache.put(getId(), builder);
}
/**
* Find the cached builder instance.
*
* @param key The key under where to look for the builder.
*
* @return null if no builder instance could be found.
*/
static NotificationCompat.Builder getCachedBuilder(int key) {
return (cache != null) ? cache.get(key) : null;
}
/**
* Caches the builder instance so it can be used later.
*/
private void clearCache() {
if (cache != null) {
cache.delete(getId());
}
}
/**
* Shared private preferences for the application.
*/
private SharedPreferences getPrefs(String key) {
return context.getSharedPreferences(key, Context.MODE_PRIVATE);
}
/**
* Notification manager for the application.
*/
private NotificationManager getNotMgr() {
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
/**
* Alarm manager for the application.
*/
private AlarmManager getAlarmMgr() {
return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
}
19
Source : PendingRequests.java
with MIT License
from zhangyd-c
with MIT License
from zhangyd-c
/**
* Holds pending runtime permission requests
*/
clreplaced PendingRequests {
private int currentReqId = 0;
private SparseArray<Request> requests = new SparseArray<Request>();
/**
* Creates a request and adds it to the array of pending requests. Each created request gets a
* unique result code for use with requestPermission()
* @param rawArgs The raw arguments preplaceded to the plugin
* @param action The action this request corresponds to (get file, etc.)
* @param callbackContext The CallbackContext for this plugin call
* @return The request code that can be used to retrieve the Request object
*/
public synchronized int createRequest(String rawArgs, int action, CallbackContext callbackContext) {
Request req = new Request(rawArgs, action, callbackContext);
requests.put(req.requestCode, req);
return req.requestCode;
}
/**
* Gets the request corresponding to this request code and removes it from the pending requests
* @param requestCode The request code for the desired request
* @return The request corresponding to the given request code or null if such a
* request is not found
*/
public synchronized Request getAndRemove(int requestCode) {
Request result = requests.get(requestCode);
requests.remove(requestCode);
return result;
}
/**
* Holds the options and CallbackContext for a call made to the plugin.
*/
public clreplaced Request {
// Unique int used to identify this request in any Android permission callback
private int requestCode;
// Action to be performed after permission request result
private int action;
// Raw arguments preplaceded to plugin
private String rawArgs;
// The callback context for this plugin request
private CallbackContext callbackContext;
private Request(String rawArgs, int action, CallbackContext callbackContext) {
this.rawArgs = rawArgs;
this.action = action;
this.callbackContext = callbackContext;
this.requestCode = currentReqId++;
}
public int getAction() {
return this.action;
}
public String getRawArgs() {
return rawArgs;
}
public CallbackContext getCallbackContext() {
return callbackContext;
}
}
}
19
Source : CallbackMap.java
with MIT License
from zhangyd-c
with MIT License
from zhangyd-c
/**
* Provides a collection that maps unique request codes to CordovaPlugins and Integers.
* Used to ensure that when plugins make requests for runtime permissions, those requests do not
* collide with requests from other plugins that use the same request code value.
*/
public clreplaced CallbackMap {
private int currentCallbackId = 0;
private SparseArray<Pair<CordovaPlugin, Integer>> callbacks;
public CallbackMap() {
this.callbacks = new SparseArray<Pair<CordovaPlugin, Integer>>();
}
/**
* Stores a CordovaPlugin and request code and returns a new unique request code to use
* in a permission request.
*
* @param receiver The plugin that is making the request
* @param requestCode The original request code used by the plugin
* @return A unique request code that can be used to retrieve this callback
* with getAndRemoveCallback()
*/
public synchronized int registerCallback(CordovaPlugin receiver, int requestCode) {
int mappedId = this.currentCallbackId++;
callbacks.put(mappedId, new Pair<CordovaPlugin, Integer>(receiver, requestCode));
return mappedId;
}
/**
* Retrieves and removes a callback stored in the map using the mapped request code
* obtained from registerCallback()
*
* @param mappedId The request code obtained from registerCallback()
* @return The CordovaPlugin and orignal request code that correspond to the
* given mappedCode
*/
public synchronized Pair<CordovaPlugin, Integer> getAndRemoveCallback(int mappedId) {
Pair<CordovaPlugin, Integer> callback = callbacks.get(mappedId);
callbacks.remove(mappedId);
return callback;
}
}
19
Source : ServerInterface.java
with Apache License 2.0
from zhangke3016
with Apache License 2.0
from zhangke3016
/**
* @author zk
* @date 创建时间:2019/2/1
* @Description:
* @other 修改历史:
*/
public clreplaced ServerInterface {
private Clreplaced<?> interfaceClreplaced;
private final SparseArray<IPCMethod> codeToInterfaceMethod;
private final Map<Method, IPCMethod> methodToIPCMethodMap;
public ServerInterface(Clreplaced<?> interfaceClreplaced) {
this.interfaceClreplaced = interfaceClreplaced;
Method[] methods = interfaceClreplaced.getMethods();
codeToInterfaceMethod = new SparseArray<>(methods.length);
methodToIPCMethodMap = new HashMap<>(methods.length);
for (int i = 0; i < methods.length; i++) {
int code = Binder.FIRST_CALL_TRANSACTION + i;
IPCMethod ipcMethod = new IPCMethod(code, methods[i], interfaceClreplaced.getName());
codeToInterfaceMethod.put(code, ipcMethod);
methodToIPCMethodMap.put(methods[i], ipcMethod);
}
}
public Clreplaced<?> getInterfaceClreplaced() {
return interfaceClreplaced;
}
public String getInterfaceName() {
return interfaceClreplaced.getName();
}
public IPCMethod getIPCMethod(int code) {
return codeToInterfaceMethod.get(code);
}
public IPCMethod getIPCMethod(Method method) {
return methodToIPCMethodMap.get(method);
}
}
19
Source : Constants.java
with Apache License 2.0
from zhangjianli
with Apache License 2.0
from zhangjianli
public final clreplaced Constants {
public static final int MSG_START_ACTIVITY = 1;
public static final int MSG_LIFECYCLE = 2;
public static final int MSG_MESSAGE = 3;
public static final int LIFECYCLE_CREATED = 1;
public static final int LIFECYCLE_STARTED = 2;
public static final int LIFECYCLE_RESUMED = 3;
public static final int LIFECYCLE_PAUSED = 4;
public static final int LIFECYCLE_STOPPED = 5;
public static final int LIFECYCLE_DESTROYED = 6;
public static final int LIFECYCLE_STATE_SAVED = 7;
public static final int COLOR_NORMAL = Color.parseColor("#27C47A");
public static final int COLOR_WARNING = Color.parseColor("#ED4337");
public static final int COLOR_INFO = Color.parseColor("#222222");
public static final SparseArray<String> sActivityThreadMsgMap = new SparseArray<>();
static {
sActivityThreadMsgMap.put(100, "LAUNCH_ACTIVITY");
sActivityThreadMsgMap.put(101, "PAUSE_ACTIVITY");
sActivityThreadMsgMap.put(102, "PAUSE_ACTIVITY_FINISHING");
sActivityThreadMsgMap.put(103, "STOP_ACTIVITY_SHOW");
sActivityThreadMsgMap.put(104, "STOP_ACTIVITY_HIDE");
sActivityThreadMsgMap.put(105, "SHOW_WINDOW");
sActivityThreadMsgMap.put(106, "HIDE_WINDOW");
sActivityThreadMsgMap.put(107, "RESUME_ACTIVITY");
sActivityThreadMsgMap.put(108, "SEND_RESULT");
sActivityThreadMsgMap.put(109, "DESTROY_ACTIVITY");
sActivityThreadMsgMap.put(110, "BIND_APPLICATION");
sActivityThreadMsgMap.put(111, "EXIT_APPLICATION");
sActivityThreadMsgMap.put(112, "NEW_INTENT");
sActivityThreadMsgMap.put(113, "RECEIVER");
sActivityThreadMsgMap.put(114, "CREATE_SERVICE");
sActivityThreadMsgMap.put(115, "SERVICE_ARGS");
sActivityThreadMsgMap.put(116, "STOP_SERVICE");
sActivityThreadMsgMap.put(118, "CONFIGURATION_CHANGED");
sActivityThreadMsgMap.put(119, "CLEAN_UP_CONTEXT");
sActivityThreadMsgMap.put(120, "GC_WHEN_IDLE");
sActivityThreadMsgMap.put(121, "BIND_SERVICE");
sActivityThreadMsgMap.put(122, "UNBIND_SERVICE");
sActivityThreadMsgMap.put(123, "DUMP_SERVICE");
sActivityThreadMsgMap.put(124, "LOW_MEMORY");
sActivityThreadMsgMap.put(125, "ACTIVITY_CONFIGURATION_CHANGED");
sActivityThreadMsgMap.put(126, "RELAUNCH_ACTIVITY");
sActivityThreadMsgMap.put(127, "PROFILER_CONTROL");
sActivityThreadMsgMap.put(128, "CREATE_BACKUP_AGENT");
sActivityThreadMsgMap.put(129, "DESTROY_BACKUP_AGENT");
sActivityThreadMsgMap.put(130, "SUICIDE");
sActivityThreadMsgMap.put(131, "REMOVE_PROVIDER");
sActivityThreadMsgMap.put(132, "ENABLE_JIT");
sActivityThreadMsgMap.put(133, "DISPATCH_PACKAGE_BROADCAST");
sActivityThreadMsgMap.put(134, "SCHEDULE_CRASH");
sActivityThreadMsgMap.put(135, "DUMP_HEAP");
sActivityThreadMsgMap.put(136, "DUMP_ACTIVITY");
sActivityThreadMsgMap.put(137, "SLEEPING");
sActivityThreadMsgMap.put(138, "SET_CORE_SETTINGS");
sActivityThreadMsgMap.put(139, "UPDATE_PACKAGE_COMPATIBILITY_INFO");
sActivityThreadMsgMap.put(140, "TRIM_MEMORY");
sActivityThreadMsgMap.put(141, "DUMP_PROVIDER");
sActivityThreadMsgMap.put(142, "UNSTABLE_PROVIDER_DIED");
sActivityThreadMsgMap.put(143, "REQUEST_replacedIST_CONTEXT_EXTRAS");
sActivityThreadMsgMap.put(144, "TRANSLUCENT_CONVERSION_COMPLETE");
sActivityThreadMsgMap.put(145, "INSTALL_PROVIDER");
sActivityThreadMsgMap.put(146, "ON_NEW_ACTIVITY_OPTIONS");
sActivityThreadMsgMap.put(147, "CANCEL_VISIBLE_BEHIND");
sActivityThreadMsgMap.put(148, "BACKGROUND_VISIBLE_BEHIND_CHANGED");
sActivityThreadMsgMap.put(149, "ENTER_ANIMATION_COMPLETE");
sActivityThreadMsgMap.put(150, "START_BINDER_TRACKING");
sActivityThreadMsgMap.put(151, "STOP_BINDER_TRACKING_AND_DUMP");
sActivityThreadMsgMap.put(152, "MULTI_WINDOW_MODE_CHANGED");
sActivityThreadMsgMap.put(153, "PICTURE_IN_PICTURE_MODE_CHANGED");
sActivityThreadMsgMap.put(154, "LOCAL_VOICE_INTERACTION_STARTED");
sActivityThreadMsgMap.put(155, "ATTACH_AGENT");
sActivityThreadMsgMap.put(156, "APPLICATION_INFO_CHANGED");
sActivityThreadMsgMap.put(157, "ACTIVITY_MOVED_TO_DISPLAY");
}
public static final SparseArray<String> sViewRootImplMsgMap = new SparseArray<>();
static {
sViewRootImplMsgMap.put(1, "MSG_INVALIDATE");
sViewRootImplMsgMap.put(2, "MSG_INVALIDATE_RECT");
sViewRootImplMsgMap.put(3, "MSG_DIE");
sViewRootImplMsgMap.put(4, "MSG_RESIZED");
sViewRootImplMsgMap.put(5, "MSG_RESIZED_REPORT");
sViewRootImplMsgMap.put(6, "MSG_WINDOW_FOCUS_CHANGED");
sViewRootImplMsgMap.put(7, "MSG_DISPATCH_INPUT_EVENT");
sViewRootImplMsgMap.put(8, "MSG_DISPATCH_APP_VISIBILITY");
sViewRootImplMsgMap.put(9, "MSG_DISPATCH_GET_NEW_SURFACE");
sViewRootImplMsgMap.put(11, "MSG_DISPATCH_KEY_FROM_IME");
sViewRootImplMsgMap.put(12, "MSG_FINISH_INPUT_CONNECTION");
sViewRootImplMsgMap.put(13, "MSG_CHECK_FOCUS");
sViewRootImplMsgMap.put(14, "MSG_CLOSE_SYSTEM_DIALOGS");
sViewRootImplMsgMap.put(15, "MSG_DISPATCH_DRAG_EVENT");
sViewRootImplMsgMap.put(16, "MSG_DISPATCH_DRAG_LOCATION_EVENT");
sViewRootImplMsgMap.put(17, "MSG_DISPATCH_SYSTEM_UI_VISIBILITY");
sViewRootImplMsgMap.put(18, "MSG_UPDATE_CONFIGURATION");
sViewRootImplMsgMap.put(19, "MSG_PROCESS_INPUT_EVENTS");
sViewRootImplMsgMap.put(21, "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST");
sViewRootImplMsgMap.put(22, "MSG_INVALIDATE_WORLD");
sViewRootImplMsgMap.put(23, "MSG_WINDOW_MOVED");
sViewRootImplMsgMap.put(24, "MSG_SYNTHESIZE_INPUT_EVENT");
sViewRootImplMsgMap.put(25, "MSG_DISPATCH_WINDOW_SHOWN");
sViewRootImplMsgMap.put(26, "MSG_DISPATCH_WINDOW_ANIMATION_STOPPED");
sViewRootImplMsgMap.put(27, "MSG_DISPATCH_WINDOW_ANIMATION_STARTED");
}
private Constants() {
}
}
19
Source : FragmentViewPagerActivity.java
with Apache License 2.0
from zguop
with Apache License 2.0
from zguop
/**
* auth aboom
* date 2019-12-28
*/
public clreplaced FragmentViewPagerActivity extends AppCompatActivity {
public static final int TAB_TEST1 = R.id.test1;
public static final int TAB_TEST2 = R.id.test2;
public static final int TAB_TEST3 = R.id.test3;
private SparseArray<View> tabView = new SparseArray<>();
private View selectedButton;
private int currentTag;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.setStatusBarColor(this, Color.WHITE);
int currentTag = getIntent().getIntExtra("currentTag", TAB_TEST1);
setContentView(R.layout.activity_fvp);
binding();
clickTab(tabView.get(currentTag));
}
private void clickTab(View view) {
if (selectedButton != null) {
selectedButton.setSelected(Boolean.FALSE);
}
selectedButton = view;
selectedButton.setSelected(Boolean.TRUE);
selectFragment(view.getId());
}
private void binding() {
tabView.put(TAB_TEST1, findViewById(TAB_TEST1));
tabView.put(TAB_TEST2, findViewById(TAB_TEST2));
tabView.put(TAB_TEST3, findViewById(TAB_TEST3));
for (int i = 0; i < tabView.size(); i++) {
tabView.get(tabView.keyAt(i)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clickTab(v);
}
});
}
}
private void selectFragment(int tag) {
FragmentManager supportFragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = supportFragmentManager.beginTransaction();
if (tag == currentTag) {
return;
}
Fragment currentFragment = supportFragmentManager.findFragmentByTag(String.valueOf(currentTag));
if (currentFragment != null) {
transaction.hide(currentFragment);
}
Fragment fragment = supportFragmentManager.findFragmentByTag(String.valueOf(tag));
if (fragment == null) {
transaction.add(R.id.main_content, getFragment(tag), String.valueOf(tag));
} else {
transaction.show(fragment);
}
currentTag = tag;
transaction.commitAllowingStateLoss();
}
@NonNull
private Fragment getFragment(int tag) {
switch(tag) {
case TAB_TEST1:
return new Test1Fragment();
case TAB_TEST2:
return new Test2Fragment();
case TAB_TEST3:
return new Test3Fragment();
}
return new Test1Fragment();
}
}
19
Source : AdapterItemUtil.java
with Apache License 2.0
from zaihuishou
with Apache License 2.0
from zaihuishou
/**
* creater: zaihuishou
* create time: 7/13/16.
* email:[email protected]
* collect item type
*/
public clreplaced AdapterItemUtil {
private SparseArray<Object> typeSArr = new SparseArray<>();
/**
* @param type item type
* @return int type of object corresponding
*/
public int getIntType(Object type) {
int index = typeSArr.indexOfValue(type);
if (index == -1) {
index = typeSArr.size();
typeSArr.put(index, type);
}
return index;
}
}
19
Source : AdapterItemUtil.java
with Apache License 2.0
from zaihuishou
with Apache License 2.0
from zaihuishou
/**
* creater: zaihuishou
* create time: 7/13/16.
* email:[email protected]
*/
public clreplaced AdapterItemUtil {
private SparseArray<Object> typeSArr = new SparseArray<>();
/**
* @param type item的类型
* @return 通过object类型的type来得到int类型的type
*/
public int getIntType(Object type) {
int index = typeSArr.indexOfValue(type);
if (index == -1) {
index = typeSArr.size();
// 如果没用这个type,就存入这个type
typeSArr.put(index, type);
}
return index;
}
}
19
Source : TvContractUtils.java
with Apache License 2.0
from zaclimon
with Apache License 2.0
from zaclimon
/**
* Static helper methods for working with {@link android.media.tv.TvContract}.
*/
public final clreplaced TvContractUtils {
/**
* Indicates that no source type has been defined for this video yet
*/
public static final int SOURCE_TYPE_INVALID = -1;
/**
* Indicates that the video will use MPEG-DASH (Dynamic Adaptive Streaming over HTTP) for
* playback.
*/
public static final int SOURCE_TYPE_MPEG_DASH = 0;
/**
* Indicates that the video will use SS (Smooth Streaming) for playback.
*/
public static final int SOURCE_TYPE_SS = 1;
/**
* Indicates that the video will use HLS (HTTP Live Streaming) for playback.
*/
public static final int SOURCE_TYPE_HLS = 2;
/**
* Indicates that the video will use HTTP Progressive for playback.
*/
public static final int SOURCE_TYPE_HTTP_PROGRESSIVE = 3;
private static final String TAG = "TvContractUtils";
private static final boolean DEBUG = false;
private static final SparseArray<String> VIDEO_HEIGHT_TO_FORMAT_MAP = new SparseArray<>();
static {
VIDEO_HEIGHT_TO_FORMAT_MAP.put(480, TvContract.Channels.VIDEO_FORMAT_480P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(576, TvContract.Channels.VIDEO_FORMAT_576P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(720, TvContract.Channels.VIDEO_FORMAT_720P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(1080, TvContract.Channels.VIDEO_FORMAT_1080P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(2160, TvContract.Channels.VIDEO_FORMAT_2160P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(4320, TvContract.Channels.VIDEO_FORMAT_4320P);
}
private static void insertUrl(Context context, Uri contentUri, URL sourceUrl) {
if (DEBUG) {
Log.d(TAG, "Inserting " + sourceUrl + " to " + contentUri);
}
InputStream is = null;
OutputStream os = null;
try {
is = sourceUrl.openStream();
os = context.getContentResolver().openOutputStream(contentUri);
copy(is, os);
} catch (IOException ioe) {
Log.e(TAG, "Failed to write " + sourceUrl + " to " + contentUri, ioe);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Ignore exception.
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
// Ignore exception.
}
}
}
}
private static void copy(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
/**
* Parses a string of comma-separated ratings into an array of {@link TvContentRating}.
*
* @param commaSeparatedRatings String containing various ratings, separated by commas.
* @return An array of TvContentRatings.
* @hide
*/
public static TvContentRating[] stringToContentRatings(String commaSeparatedRatings) {
if (TextUtils.isEmpty(commaSeparatedRatings)) {
return null;
}
String[] ratings = commaSeparatedRatings.split("\\s*,\\s*");
TvContentRating[] contentRatings = new TvContentRating[ratings.length];
for (int i = 0; i < contentRatings.length; ++i) {
contentRatings[i] = TvContentRating.unflattenFromString(ratings[i]);
}
return contentRatings;
}
/**
* Flattens an array of {@link TvContentRating} into a String to be inserted into a database.
*
* @param contentRatings An array of TvContentRatings.
* @return A comma-separated String of ratings.
* @hide
*/
public static String contentRatingsToString(TvContentRating[] contentRatings) {
if (contentRatings == null || contentRatings.length == 0) {
return null;
}
final String DELIMITER = ",";
StringBuilder ratings = new StringBuilder(contentRatings[0].flattenToString());
for (int i = 1; i < contentRatings.length; ++i) {
ratings.append(DELIMITER);
ratings.append(contentRatings[i].flattenToString());
}
return ratings.toString();
}
private TvContractUtils() {
}
/**
* AsyncTask to insert logos.
*/
public static clreplaced InsertLogosTask extends AsyncTask<Map<Uri, String>, Void, Void> {
private final Context mContext;
public InsertLogosTask(Context context) {
mContext = context;
}
@Override
public Void doInBackground(Map<Uri, String>... logosList) {
for (Map<Uri, String> logos : logosList) {
for (Uri uri : logos.keySet()) {
try {
insertUrl(mContext, uri, new URL(logos.get(uri)));
} catch (MalformedURLException e) {
Log.e(TAG, "Can't load " + logos.get(uri), e);
}
}
}
return null;
}
}
}
19
Source : EpgSyncJobService.java
with Apache License 2.0
from zaclimon
with Apache License 2.0
from zaclimon
/**
* Service to handle callbacks from JobScheduler. This service will be called by the system to
* update the EPG with channels and programs periodically.
*
* <p>You can extend this clreplaced and add it your app by including it in your app's
* AndroidManfiest.xml:
*
* <pre>
* <service
* android:name=".SampleJobService"
* android:permission="android.permission.BIND_JOB_SERVICE"
* android:exported="true" />
* </pre>
*
* You will need to implement several methods in your EpgSyncJobService to return your content.
*
* <p>To start periodically syncing data, call {@link #setUpPeriodicSync(Context, String,
* ComponentName, long, long)}.
*
* <p>To sync manually, call {@link #requestImmediateSync(Context, String, long, ComponentName)}.
*/
public abstract clreplaced EpgSyncJobService extends JobService {
private static final String TAG = "EpgSyncJobService";
private static final boolean DEBUG = false;
/**
* The action that will be broadcast when the job service's status changes.
*/
public static final String ACTION_SYNC_STATUS_CHANGED = EpgSyncJobService.clreplaced.getPackage().getName() + ".ACTION_SYNC_STATUS_CHANGED";
/**
* The key representing the component name for the app's TvInputService.
*/
public static final String BUNDLE_KEY_INPUT_ID = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_input_id";
/**
* The key representing the number of channels that have been scanned and populated in the EPG.
*/
public static final String BUNDLE_KEY_CHANNELS_SCANNED = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_channels_scanned";
/**
* The key representing the total number of channels for this input.
*/
public static final String BUNDLE_KEY_CHANNEL_COUNT = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_channel_count";
/**
* The key representing the most recently scanned channel display name.
*/
public static final String BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NAME = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_scanned_channel_display_name";
/**
* The key representing the most recently scanned channel display number.
*/
public static final String BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NUMBER = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_scanned_channel_display_number";
/**
* The key representing the error that occurred during an EPG sync
*/
public static final String BUNDLE_KEY_ERROR_REASON = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_error_reason";
/**
* The name for the {@link android.content.SharedPreferences} file used for storing syncing
* metadata.
*/
public static final String PREFERENCE_EPG_SYNC = EpgSyncJobService.clreplaced.getPackage().getName() + ".preference_epg_sync";
/**
* The status of the job service when syncing has begun.
*/
public static final String SYNC_STARTED = "sync_started";
/**
* The status of the job service when a channel has been scanned and the EPG for that channel
* has been populated.
*/
public static final String SYNC_SCANNED = "sync_scanned";
/**
* The status of the job service when syncing has completed.
*/
public static final String SYNC_FINISHED = "sync_finished";
/**
* The status of the job when a problem occurs during syncing. A {@link #SYNC_FINISHED}
* broadcast will still be sent when the service is done. This status can be used to identify
* specific issues in your EPG sync.
*/
public static final String SYNC_ERROR = "sync_error";
/**
* The key corresponding to the job service's status.
*/
public static final String SYNC_STATUS = "sync_status";
/**
* Indicates that the EPG sync was canceled before being completed.
*/
public static final int ERROR_EPG_SYNC_CANCELED = 1;
/**
* Indicates that the input id was not defined and the EPG sync cannot complete.
*/
public static final int ERROR_INPUT_ID_NULL = 2;
/**
* Indicates that no programs were found.
*/
public static final int ERROR_NO_PROGRAMS = 3;
/**
* Indicates that no channels were found.
*/
public static final int ERROR_NO_CHANNELS = 4;
/**
* Indicates an error occurred when updating programs in the database
*/
public static final int ERROR_DATABASE_INSERT = 5;
/**
* Subclreplacedes start their custom error numbers with this value
*/
public static final int ERROR_START_CUSTOM = 1000;
/**
* The default period between full EPG syncs, one day.
*/
// 12 hour
private static final long DEFAULT_SYNC_PERIOD_MILLIS = 1000 * 60 * 60 * 12;
// 1 Hour
private static final long DEFAULT_IMMEDIATE_EPG_DURATION_MILLIS = 1000 * 60 * 60;
// 48 Hour
private static final long DEFAULT_PERIODIC_EPG_DURATION_MILLIS = 1000 * 60 * 60 * 48;
private static final int PERIODIC_SYNC_JOB_ID = 0;
private static final int REQUEST_SYNC_JOB_ID = 1;
private static final int BATCH_OPERATION_COUNT = 100;
// 1 second
private static final long OVERRIDE_DEADLINE_MILLIS = 1000;
private static final String BUNDLE_KEY_SYNC_PERIOD = "bundle_key_sync_period";
private static final ExecutorService SINGLE_THREAD_EXECUTOR = Executors.newSingleThreadExecutor();
private final SparseArray<EpgSyncTask> mTaskArray = new SparseArray<>();
private static final Object mContextLock = new Object();
private Context mContext;
/**
* Returns the channels that your app contains.
*
* @return The list of channels for your app.
*/
public abstract List<Channel> getChannels() throws EpgSyncException;
/**
* Returns the programs that will appear for each channel.
*
* @param channelUri The Uri corresponding to the channel.
* @param channel The channel your programs will appear on.
* @param startMs The starting time in milliseconds since the epoch to generate programs. If
* your program starts before this starting time, it should be be included.
* @param endMs The ending time in milliseconds since the epoch to generate programs. If your
* program starts before this ending time, it should be be included.
* @return A list of programs for a given channel.
*/
public abstract List<Program> getProgramsForChannel(Uri channelUri, Channel channel, long startMs, long endMs) throws EpgSyncException;
@Override
public void onCreate() {
super.onCreate();
if (DEBUG) {
Log.d(TAG, "Created EpgSyncJobService");
}
synchronized (mContextLock) {
if (mContext == null) {
mContext = getApplicationContext();
}
}
}
@Override
public boolean onStartJob(JobParameters params) {
if (DEBUG) {
Log.d(TAG, "onStartJob(" + params.getJobId() + ")");
}
// Broadcast status
Intent intent = createSyncStartedIntent(params.getExtras().getString(BUNDLE_KEY_INPUT_ID));
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
EpgSyncTask epgSyncTask = new EpgSyncTask(params);
synchronized (mTaskArray) {
mTaskArray.put(params.getJobId(), epgSyncTask);
}
// Run the task on a single threaded custom executor in order not to block the AsyncTasks
// running on application side.
epgSyncTask.executeOnExecutor(SINGLE_THREAD_EXECUTOR);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
synchronized (mTaskArray) {
int jobId = params.getJobId();
EpgSyncTask epgSyncTask = mTaskArray.get(jobId);
if (epgSyncTask != null) {
epgSyncTask.cancel(true);
mTaskArray.delete(params.getJobId());
}
}
return false;
}
/**
* Returns {@code true} if the {@code oldProgram} program is the same as the {@code newProgram}
* program but should update metadata. This updates the database instead of deleting and
* inserting a new program to keep the user's intent, eg. recording this program.
*/
public boolean shouldUpdateProgramMetadata(Program oldProgram, Program newProgram) {
// NOTE: Here, we update the old program if it has the same replacedle and overlaps with the
// new program. The test logic is just an example and you can modify this. E.g. check
// whether the both programs have the same program ID if your EPG supports any ID for
// the programs.
return oldProgram.getreplacedle().equals(newProgram.getreplacedle()) && oldProgram.getStartTimeUtcMillis() <= newProgram.getEndTimeUtcMillis() && newProgram.getStartTimeUtcMillis() <= oldProgram.getEndTimeUtcMillis();
}
/**
* Send the job to JobScheduler.
*/
private static void scheduleJob(Context context, JobInfo job) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = jobScheduler.schedule(job);
if (DEBUG) {
Log.d(TAG, "Scheduling result is " + result);
}
}
/**
* Initializes a job that will periodically update the app's channels and programs with a
* default period of 24 hours.
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param jobServiceComponent The {@link EpgSyncJobService} component name that will run.
*/
public static void setUpPeriodicSync(Context context, String inputId, ComponentName jobServiceComponent) {
setUpPeriodicSync(context, inputId, jobServiceComponent, DEFAULT_SYNC_PERIOD_MILLIS, DEFAULT_PERIODIC_EPG_DURATION_MILLIS);
}
/**
* Initializes a job that will periodically update the app's channels and programs.
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param jobServiceComponent The {@link EpgSyncJobService} component name that will run.
* @param fullSyncPeriod The period between when the job will run a full background sync in
* milliseconds.
* @param syncDuration The duration of EPG content to fetch in milliseconds. For a manual sync,
* this should be relatively short. For a background sync this should be long.
*/
public static void setUpPeriodicSync(Context context, String inputId, ComponentName jobServiceComponent, long fullSyncPeriod, long syncDuration) {
if (jobServiceComponent.getClreplaced().isreplacedignableFrom(EpgSyncJobService.clreplaced)) {
throw new IllegalArgumentException("This clreplaced does not extend EpgSyncJobService");
}
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putString(EpgSyncJobService.BUNDLE_KEY_INPUT_ID, inputId);
persistableBundle.putLong(EpgSyncJobService.BUNDLE_KEY_SYNC_PERIOD, syncDuration);
JobInfo.Builder builder = new JobInfo.Builder(PERIODIC_SYNC_JOB_ID, jobServiceComponent);
JobInfo jobInfo = builder.setExtras(persistableBundle).setPeriodic(fullSyncPeriod).setPersisted(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
scheduleJob(context, jobInfo);
if (DEBUG) {
Log.d(TAG, "Job has been scheduled for every " + fullSyncPeriod + "ms");
}
}
/**
* Manually requests a job to run now to retrieve EPG content for the next hour.
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param jobServiceComponent The {@link EpgSyncJobService} clreplaced that will run.
*/
public static void requestImmediateSync(Context context, String inputId, ComponentName jobServiceComponent) {
requestImmediateSync(context, inputId, DEFAULT_IMMEDIATE_EPG_DURATION_MILLIS, jobServiceComponent);
}
/**
* Manually requests a job to run now.
*
* <p>To check the current status of the sync, register a {@link
* android.content.BroadcastReceiver} with an {@link android.content.IntentFilter} which checks
* for the action {@link #ACTION_SYNC_STATUS_CHANGED}.
*
* <p>The sync status is an extra parameter in the {@link Intent} with key {@link #SYNC_STATUS}.
* The sync status is either {@link #SYNC_STARTED} or {@link #SYNC_FINISHED}.
*
* <p>Check that the value of {@link #BUNDLE_KEY_INPUT_ID} matches your {@link
* android.media.tv.TvInputService}. If you're calling this from your setup activity, you can
* get the extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
*
* <p>
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param syncDuration The duration of EPG content to fetch in milliseconds. For a manual sync,
* this should be relatively short. For a background sync this should be long.
* @param jobServiceComponent The {@link EpgSyncJobService} clreplaced that will run.
*/
public static void requestImmediateSync(Context context, String inputId, long syncDuration, ComponentName jobServiceComponent) {
if (jobServiceComponent.getClreplaced().isreplacedignableFrom(EpgSyncJobService.clreplaced)) {
throw new IllegalArgumentException("This clreplaced does not extend EpgSyncJobService");
}
PersistableBundle persistableBundle = new PersistableBundle();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
persistableBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
persistableBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
}
persistableBundle.putString(EpgSyncJobService.BUNDLE_KEY_INPUT_ID, inputId);
persistableBundle.putLong(EpgSyncJobService.BUNDLE_KEY_SYNC_PERIOD, syncDuration);
JobInfo.Builder builder = new JobInfo.Builder(REQUEST_SYNC_JOB_ID, jobServiceComponent);
JobInfo jobInfo = builder.setExtras(persistableBundle).setOverrideDeadline(EpgSyncJobService.OVERRIDE_DEADLINE_MILLIS).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
scheduleJob(context, jobInfo);
if (DEBUG) {
Log.d(TAG, "Single job scheduled");
}
}
/**
* Cancels all pending jobs.
*
* @param context Application's context.
*/
public static void cancelAllSyncRequests(Context context) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.cancelAll();
}
/**
* @hide
*/
public clreplaced EpgSyncTask extends AsyncTask<Void, Void, Void> {
private final JobParameters params;
private String mInputId;
public EpgSyncTask(JobParameters params) {
this.params = params;
}
@Override
public Void doInBackground(Void... voids) {
PersistableBundle extras = params.getExtras();
mInputId = extras.getString(BUNDLE_KEY_INPUT_ID);
if (mInputId == null) {
broadcastError(ERROR_INPUT_ID_NULL);
return null;
}
if (isCancelled()) {
broadcastError(ERROR_EPG_SYNC_CANCELED);
return null;
}
List<Channel> tvChannels;
try {
tvChannels = getChannels();
} catch (EpgSyncException e) {
broadcastError(e.getReason());
return null;
}
ModelUtils.updateChannels(mContext, mInputId, tvChannels, new OnChannelDeletedCallback() {
@Override
public void onChannelDeleted(long rowId) {
SharedPreferences.Editor editor = mContext.getSharedPreferences(Constants.PREFERENCES_FILE_KEY, Context.MODE_PRIVATE).edit();
editor.remove(Constants.SHARED_PREFERENCES_KEY_LAST_CHANNEL_AD_PLAY + rowId);
editor.apply();
}
});
LongSparseArray<Channel> channelMap = ModelUtils.buildChannelMap(mContext.getContentResolver(), mInputId);
if (channelMap == null) {
broadcastError(ERROR_NO_CHANNELS);
return null;
}
// Default to one hour sync
long durationMs = extras.getLong(BUNDLE_KEY_SYNC_PERIOD, DEFAULT_IMMEDIATE_EPG_DURATION_MILLIS);
long startMs = System.currentTimeMillis();
long endMs = startMs + durationMs;
ChangeCount runningChangeCount = new ChangeCount();
for (int i = 0; i < channelMap.size(); ++i) {
Uri channelUri = TvContract.buildChannelUri(channelMap.keyAt(i));
if (isCancelled()) {
broadcastError(ERROR_EPG_SYNC_CANCELED);
return null;
}
List<Program> programs;
try {
programs = getProgramsForChannel(channelUri, channelMap.valueAt(i), startMs, endMs);
} catch (EpgSyncException e) {
broadcastError(e.getReason());
return null;
}
if (programs != null) {
if (DEBUG) {
Log.d(TAG, programs.toString());
}
for (int index = 0; index < programs.size(); index++) {
if (programs.get(index).getChannelId() == -1) {
// Automatically set the channel id if not set
programs.set(index, new Program.Builder(programs.get(index)).setChannelId(channelMap.valueAt(i).getId()).build());
}
}
// Double check if the job is cancelled, so that this task can be finished faster
// after cancel() is called.
if (isCancelled()) {
broadcastError(ERROR_EPG_SYNC_CANCELED);
return null;
}
updatePrograms(channelUri, programs, runningChangeCount);
}
Intent intent = createSyncScannedIntent(mInputId, i + 1, channelMap.size(), channelMap.valueAt(i).getDisplayName(), channelMap.valueAt(i).getDisplayNumber());
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
Log.i(TAG, mInputId + " synced " + runningChangeCount.total + " programs. Deleted " + runningChangeCount.deleteCount + " updated " + runningChangeCount.updateCount + " added " + runningChangeCount.addCount);
return null;
}
@Override
public void onPostExecute(Void success) {
finishEpgSync(params);
}
@Override
public void onCancelled(Void ignore) {
finishEpgSync(params);
}
private void finishEpgSync(JobParameters jobParams) {
if (DEBUG) {
Log.d(TAG, "taskFinished(" + jobParams.getJobId() + ")");
}
mTaskArray.delete(jobParams.getJobId());
jobFinished(jobParams, false);
if (DEBUG) {
Log.d(TAG, "Send out broadcast");
}
Intent intent = createSyncFinishedIntent(jobParams.getExtras().getString(BUNDLE_KEY_INPUT_ID));
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
private void broadcastError(int reason) {
Intent intent = createSyncErrorIntent(mInputId, reason);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
/**
* Returns a list of programs for the given time range.
*
* @param channel The {@link Channel} for the programs to return.
* @param programs The feed fetched from cloud.
* @param startTimeMs The start time of the range requested.
* @param endTimeMs The end time of the range requested.
* @return A list of programs for the channel within the specifed range. They may be
* repeated.
* @hide
*/
@VisibleForTesting
public List<Program> getPrograms(Channel channel, List<Program> programs, long startTimeMs, long endTimeMs) {
if (startTimeMs > endTimeMs) {
throw new IllegalArgumentException("Start time must be before end time");
}
List<Program> programForGivenTime = new ArrayList<>();
if (channel.getInternalProviderData() != null && !channel.getInternalProviderData().isRepeatable()) {
for (Program program : programs) {
if (program.getStartTimeUtcMillis() <= endTimeMs && program.getEndTimeUtcMillis() >= startTimeMs) {
programForGivenTime.add(new Program.Builder(program).setChannelId(channel.getId()).build());
}
}
return programForGivenTime;
}
// If repeat-programs is on, schedule the programs sequentially in a loop. To make every
// device play the same program in a given channel and time, we replacedumes the loop started
// from the epoch time.
long totalDurationMs = 0;
for (Program program : programs) {
totalDurationMs += (program.getEndTimeUtcMillis() - program.getStartTimeUtcMillis());
}
if (totalDurationMs <= 0) {
throw new IllegalArgumentException("The duration of all programs must be greater " + "than 0ms.");
}
long programStartTimeMs = startTimeMs - startTimeMs % totalDurationMs;
int i = 0;
final int programCount = programs.size();
while (programStartTimeMs < endTimeMs) {
Program programInfo = programs.get(i++ % programCount);
long programEndTimeMs = programStartTimeMs + totalDurationMs;
if (programInfo.getEndTimeUtcMillis() > -1 && programInfo.getStartTimeUtcMillis() > -1) {
programEndTimeMs = programStartTimeMs + (programInfo.getEndTimeUtcMillis() - programInfo.getStartTimeUtcMillis());
}
if (programEndTimeMs < startTimeMs) {
programStartTimeMs = programEndTimeMs;
continue;
}
// Shift advertisement time to match current program time.
InternalProviderData updateInternalProviderData = programInfo.getInternalProviderData();
shiftAdsTimeWithProgram(updateInternalProviderData, programInfo.getStartTimeUtcMillis(), programStartTimeMs);
programForGivenTime.add(new Program.Builder(programInfo).setChannelId(channel.getId()).setStartTimeUtcMillis(programStartTimeMs).setEndTimeUtcMillis(programEndTimeMs).setInternalProviderData(updateInternalProviderData).build());
programStartTimeMs = programEndTimeMs;
}
return programForGivenTime;
}
/**
* Shift advertisement time to match program playback time. For channels with repeated program,
* the time for current program may vary from what it was defined previously.
*
* @param oldProgramStartTimeMs Outdated program start time.
* @param newProgramStartTimeMs Updated program start time.
*/
private void shiftAdsTimeWithProgram(InternalProviderData internalProviderData, long oldProgramStartTimeMs, long newProgramStartTimeMs) {
if (internalProviderData == null) {
return;
}
long timeShift = newProgramStartTimeMs - oldProgramStartTimeMs;
List<Advertisement> oldAds = internalProviderData.getAds();
List<Advertisement> newAds = new ArrayList<>();
for (Advertisement oldAd : oldAds) {
newAds.add(new Advertisement.Builder(oldAd).setStartTimeUtcMillis(oldAd.getStartTimeUtcMillis() + timeShift).setStopTimeUtcMillis(oldAd.getStopTimeUtcMillis() + timeShift).build());
}
internalProviderData.setAds(newAds);
}
/**
* Updates the system database, TvProvider, with the given programs.
*
* <p>If there is any overlap between the given and existing programs, the existing ones
* will be updated with the given ones if they have the same replacedle or replaced.
*
* @param channelUri The channel where the program info will be added.
* @param newPrograms A list of {@link Program} instances which includes program
* information.
*/
private void updatePrograms(Uri channelUri, List<Program> newPrograms, ChangeCount runningChangeCount) {
final int fetchedProgramsCount = newPrograms.size();
runningChangeCount.total += fetchedProgramsCount;
if (fetchedProgramsCount == 0) {
broadcastError(ERROR_NO_PROGRAMS);
return;
}
List<Program> oldPrograms = ModelUtils.getPrograms(mContext.getContentResolver(), channelUri);
Program firstNewProgram = newPrograms.get(0);
int oldProgramsIndex = 0;
int newProgramsIndex = 0;
// Skip the past programs. They will be automatically removed by the system.
for (Program program : oldPrograms) {
if (program.getEndTimeUtcMillis() < System.currentTimeMillis() || program.getEndTimeUtcMillis() < firstNewProgram.getStartTimeUtcMillis()) {
oldProgramsIndex++;
} else {
break;
}
}
// Compare the new programs with old programs one by one and update/delete the old one
// or insert new program if there is no matching program in the database.
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
if (isCancelled()) {
return;
}
while (newProgramsIndex < fetchedProgramsCount) {
Program oldProgram = oldProgramsIndex < oldPrograms.size() ? oldPrograms.get(oldProgramsIndex) : null;
Program newProgram = newPrograms.get(newProgramsIndex);
boolean addNewProgram = false;
if (oldProgram != null) {
if (oldProgram.equals(newProgram)) {
// Exact match. No need to update. Move on to the next programs.
oldProgramsIndex++;
newProgramsIndex++;
} else if (shouldUpdateProgramMetadata(oldProgram, newProgram)) {
// Partial match. Update the old program with the new one.
// NOTE: Use 'update' in this case instead of 'insert' and 'delete'. There
// could be application specific settings which belong to the old program.
ops.add(ContentProviderOperation.newUpdate(TvContract.buildProgramUri(oldProgram.getId())).withValues(newProgram.toContentValues()).build());
runningChangeCount.updateCount++;
oldProgramsIndex++;
newProgramsIndex++;
} else if (oldProgram.getEndTimeUtcMillis() < newProgram.getEndTimeUtcMillis()) {
// No match. Remove the old program first to see if the next program in
// {@code oldPrograms} partially matches the new program.
ops.add(ContentProviderOperation.newDelete(TvContract.buildProgramUri(oldProgram.getId())).build());
runningChangeCount.deleteCount++;
oldProgramsIndex++;
} else {
// No match. The new program does not match any of the old programs. Insert
// it as a new program.
addNewProgram = true;
newProgramsIndex++;
}
} else {
// No old programs. Just insert new programs.
addNewProgram = true;
newProgramsIndex++;
}
if (addNewProgram) {
ops.add(ContentProviderOperation.newInsert(TvContract.Programs.CONTENT_URI).withValues(newProgram.toContentValues()).build());
runningChangeCount.addCount++;
}
// Throttle the batch operation not to cause TransactionTooLargeException.
if (ops.size() > BATCH_OPERATION_COUNT || newProgramsIndex >= fetchedProgramsCount) {
try {
mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Failed to insert programs.", e);
broadcastError(ERROR_DATABASE_INSERT);
return;
}
ops.clear();
}
}
}
}
@VisibleForTesting
public static Intent createSyncStartedIntent(String inputId) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(SYNC_STATUS, SYNC_STARTED);
return intent;
}
@VisibleForTesting
public static Intent createSyncScannedIntent(String inputId, int channelsScanned, int channelCount, String displayName, String displayNumber) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_CHANNELS_SCANNED, channelsScanned);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_CHANNEL_COUNT, channelCount);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NAME, displayName);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NUMBER, displayNumber);
intent.putExtra(EpgSyncJobService.SYNC_STATUS, EpgSyncJobService.SYNC_SCANNED);
return intent;
}
@VisibleForTesting
public static Intent createSyncFinishedIntent(String inputId) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(SYNC_STATUS, SYNC_FINISHED);
return intent;
}
@VisibleForTesting
public static Intent createSyncErrorIntent(String inputId, int reason) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(SYNC_STATUS, SYNC_ERROR);
intent.putExtra(BUNDLE_KEY_ERROR_REASON, reason);
return intent;
}
/**
* Propagates the reason for an EpgSync failure.
*/
public static clreplaced EpgSyncException extends Exception {
private final int reason;
/**
* Create EpgSyncException with the given {@code reason}.
*
* <p>This {@link EpgSyncJobService} sends reason such as {@link #ERROR_EPG_SYNC_CANCELED},
* {@link #ERROR_DATABASE_INSERT}, etc. Clreplacedes that extend the {@code EpgSyncJobService},
* should use custom reasons that start at {@link #ERROR_START_CUSTOM}.
*
* @param reason The reason sync failed.
*/
public EpgSyncException(int reason) {
this.reason = reason;
}
public int getReason() {
return reason;
}
}
/**
* Struct to hold change counts
*/
private static clreplaced ChangeCount {
long total = 0;
long deleteCount = 0;
long updateCount = 0;
long addCount = 0;
}
}
19
Source : ProviderTvFragment.java
with Apache License 2.0
from zaclimon
with Apache License 2.0
from zaclimon
/**
* Main fragment used for the Android TV variant of the application
*
* @author zaclimon
* Creation date: 20/06/17
*/
public abstract clreplaced ProviderTvFragment extends BrowseSupportFragment {
private ArrayObjectAdapter mRowsAdapter;
private SparseArray<RowsSupportFragment> mFragmentSparseArray;
/**
* Gets the application name
*
* @return the application name
*/
protected abstract String getAppName();
/**
* Returns a map containing one or multiple {@link RowsSupportFragment} alongside their respective
* header replacedle(s).
*
* @return the list of RowsFragment mapped by replacedle
*/
protected abstract Map<String, RowsSupportFragment> getFragmentMap();
protected abstract Clreplaced<? extends ProviderSearchActivity> getSearchActivity();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
mFragmentSparseArray = new SparseArray<>();
setupUI();
showRows();
getMainFragmentRegistry().registerFragment(PageRow.clreplaced, new TvFragmentFactory());
if (getSearchActivity() != null) {
TypedValue value = new TypedValue();
TypedArray array = getActivity().obtainStyledAttributes(value.data, new int[] { android.R.attr.colorAccent });
setSearchAffordanceColor(array.getColor(0, 0));
setOnSearchClickedListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getActivity(), getSearchActivity());
startActivity(intent);
}
});
array.recycle();
}
}
}
/**
* Sets the user interface (UI) for the Fragment.
*/
private void setupUI() {
setreplacedle(getAppName());
setHeadersState(HEADERS_ENABLED);
setHeadersTransitionOnBackEnabled(true);
}
/**
* Shows the different rows of the Fragment.
*/
private void showRows() {
Map<String, RowsSupportFragment> tempMap = getFragmentMap();
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
int i = 0;
for (Map.Entry<String, RowsSupportFragment> entry : tempMap.entrySet()) {
HeaderItem header = new HeaderItem(i, entry.getKey());
PageRow row = new PageRow(header);
mRowsAdapter.add(row);
mFragmentSparseArray.append(i, entry.getValue());
i++;
}
setAdapter(mRowsAdapter);
}
/**
* Private clreplaced acting as a Fragment factory in order to implement custom fragments
* into Leanback.
*
* @author zaclimon
* Creation date: 02/07/17
*/
private clreplaced TvFragmentFactory extends FragmentFactory {
@Override
public Fragment createFragment(Object row) {
Row tempRow = (Row) row;
Fragment fragment = mFragmentSparseArray.get((int) tempRow.getId());
if (fragment != null) {
return (fragment);
} else {
throw new IllegalArgumentException("Invalid row: " + row);
}
}
}
}
19
Source : TvContractUtils.java
with GNU General Public License v3.0
from zaclimon
with GNU General Public License v3.0
from zaclimon
/**
* Static helper methods for working with {@link android.media.tv.TvContract}.
*/
public final clreplaced TvContractUtils {
/**
* Indicates that no source type has been defined for this video yet
*/
public static final int SOURCE_TYPE_INVALID = -1;
/**
* Indicates that the video will use MPEG-DASH (Dynamic Adaptive Streaming over HTTP) for
* playback.
*/
public static final int SOURCE_TYPE_MPEG_DASH = 0;
/**
* Indicates that the video will use SS (Smooth Streaming) for playback.
*/
public static final int SOURCE_TYPE_SS = 1;
/**
* Indicates that the video will use HLS (HTTP Live Streaming) for playback.
*/
public static final int SOURCE_TYPE_HLS = 2;
/**
* Indicates that the video will use HTTP Progressive for playback.
*/
public static final int SOURCE_TYPE_HTTP_PROGRESSIVE = 3;
private static final String TAG = "TvContractUtils";
private static final boolean DEBUG = false;
private static final SparseArray<String> VIDEO_HEIGHT_TO_FORMAT_MAP = new SparseArray<>();
static {
VIDEO_HEIGHT_TO_FORMAT_MAP.put(480, TvContract.Channels.VIDEO_FORMAT_480P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(576, TvContract.Channels.VIDEO_FORMAT_576P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(720, TvContract.Channels.VIDEO_FORMAT_720P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(1080, TvContract.Channels.VIDEO_FORMAT_1080P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(2160, TvContract.Channels.VIDEO_FORMAT_2160P);
VIDEO_HEIGHT_TO_FORMAT_MAP.put(4320, TvContract.Channels.VIDEO_FORMAT_4320P);
}
private static void insertUrl(Context context, Uri contentUri, URL sourceUrl) {
if (DEBUG) {
Log.d(TAG, "Inserting " + sourceUrl + " to " + contentUri);
}
InputStream is = null;
OutputStream os = null;
try {
is = sourceUrl.openStream();
os = context.getContentResolver().openOutputStream(contentUri);
if (is != null && os != null) {
copy(is, os);
}
} catch (IOException ioe) {
Log.e(TAG, "Failed to write " + sourceUrl + " to " + contentUri, ioe);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Ignore exception.
}
}
if (os != null) {
try {
os.close();
} catch (IOException e) {
// Ignore exception.
}
}
}
}
private static void copy(InputStream is, OutputStream os) throws IOException {
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
/**
* Parses a string of comma-separated ratings into an array of {@link TvContentRating}.
*
* @param commaSeparatedRatings String containing various ratings, separated by commas.
* @return An array of TvContentRatings.
* @hide
*/
public static TvContentRating[] stringToContentRatings(String commaSeparatedRatings) {
if (TextUtils.isEmpty(commaSeparatedRatings)) {
return null;
}
String[] ratings = commaSeparatedRatings.split("\\s*,\\s*");
TvContentRating[] contentRatings = new TvContentRating[ratings.length];
for (int i = 0; i < contentRatings.length; ++i) {
contentRatings[i] = TvContentRating.unflattenFromString(ratings[i]);
}
return contentRatings;
}
/**
* Flattens an array of {@link TvContentRating} into a String to be inserted into a database.
*
* @param contentRatings An array of TvContentRatings.
* @return A comma-separated String of ratings.
* @hide
*/
public static String contentRatingsToString(TvContentRating[] contentRatings) {
if (contentRatings == null || contentRatings.length == 0) {
return null;
}
final String DELIMITER = ",";
StringBuilder ratings = new StringBuilder(contentRatings[0].flattenToString());
for (int i = 1; i < contentRatings.length; ++i) {
ratings.append(DELIMITER);
ratings.append(contentRatings[i].flattenToString());
}
return ratings.toString();
}
private TvContractUtils() {
}
/**
* AsyncTask to insert logos.
*/
public static clreplaced InsertLogosTask extends AsyncTask<Map<Uri, String>, Void, Void> {
private final Context mContext;
public InsertLogosTask(Context context) {
mContext = context;
}
@Override
public Void doInBackground(Map<Uri, String>... logosList) {
for (Map<Uri, String> logos : logosList) {
for (Uri uri : logos.keySet()) {
try {
insertUrl(mContext, uri, new URL(logos.get(uri)));
} catch (MalformedURLException e) {
Log.e(TAG, "Can't load " + logos.get(uri), e);
}
}
}
return null;
}
}
}
19
Source : EpgSyncJobService.java
with GNU General Public License v3.0
from zaclimon
with GNU General Public License v3.0
from zaclimon
/**
* Service to handle callbacks from JobScheduler. This service will be called by the system to
* update the EPG with channels and programs periodically.
*
* <p>You can extend this clreplaced and add it your app by including it in your app's
* AndroidManfiest.xml:
*
* <pre>
* <service
* android:name=".SampleJobService"
* android:permission="android.permission.BIND_JOB_SERVICE"
* android:exported="true" />
* </pre>
*
* You will need to implement several methods in your EpgSyncJobService to return your content.
*
* <p>To start periodically syncing data, call {@link #setUpPeriodicSync(Context, String,
* ComponentName, long, long)}.
*
* <p>To sync manually, call {@link #requestImmediateSync(Context, String, long, ComponentName)}.
*/
public abstract clreplaced EpgSyncJobService extends JobService {
private static final String TAG = "EpgSyncJobService";
private static final boolean DEBUG = false;
/**
* The action that will be broadcast when the job service's status changes.
*/
public static final String ACTION_SYNC_STATUS_CHANGED = EpgSyncJobService.clreplaced.getPackage().getName() + ".ACTION_SYNC_STATUS_CHANGED";
/**
* The key representing the component name for the app's TvInputService.
*/
public static final String BUNDLE_KEY_INPUT_ID = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_input_id";
/**
* The key representing the number of channels that have been scanned and populated in the EPG.
*/
public static final String BUNDLE_KEY_CHANNELS_SCANNED = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_channels_scanned";
/**
* The key representing the total number of channels for this input.
*/
public static final String BUNDLE_KEY_CHANNEL_COUNT = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_channel_count";
/**
* The key representing the most recently scanned channel display name.
*/
public static final String BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NAME = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_scanned_channel_display_name";
/**
* The key representing the most recently scanned channel display number.
*/
public static final String BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NUMBER = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_scanned_channel_display_number";
/**
* The key representing the error that occurred during an EPG sync
*/
public static final String BUNDLE_KEY_ERROR_REASON = EpgSyncJobService.clreplaced.getPackage().getName() + ".bundle_key_error_reason";
/**
* The name for the {@link android.content.SharedPreferences} file used for storing syncing
* metadata.
*/
public static final String PREFERENCE_EPG_SYNC = EpgSyncJobService.clreplaced.getPackage().getName() + ".preference_epg_sync";
/**
* The status of the job service when syncing has begun.
*/
public static final String SYNC_STARTED = "sync_started";
/**
* The status of the job service when a channel has been scanned and the EPG for that channel
* has been populated.
*/
public static final String SYNC_SCANNED = "sync_scanned";
/**
* The status of the job service when syncing has completed.
*/
public static final String SYNC_FINISHED = "sync_finished";
/**
* The status of the job when a problem occurs during syncing. A {@link #SYNC_FINISHED}
* broadcast will still be sent when the service is done. This status can be used to identify
* specific issues in your EPG sync.
*/
public static final String SYNC_ERROR = "sync_error";
/**
* The key corresponding to the job service's status.
*/
public static final String SYNC_STATUS = "sync_status";
/**
* Indicates that the EPG sync was canceled before being completed.
*/
public static final int ERROR_EPG_SYNC_CANCELED = 1;
/**
* Indicates that the input id was not defined and the EPG sync cannot complete.
*/
public static final int ERROR_INPUT_ID_NULL = 2;
/**
* Indicates that no programs were found.
*/
public static final int ERROR_NO_PROGRAMS = 3;
/**
* Indicates that no channels were found.
*/
public static final int ERROR_NO_CHANNELS = 4;
/**
* Indicates an error occurred when updating programs in the database
*/
public static final int ERROR_DATABASE_INSERT = 5;
/**
* Subclreplacedes start their custom error numbers with this value
*/
public static final int ERROR_START_CUSTOM = 1000;
/**
* The default period between full EPG syncs, one day.
*/
// 12 hour
private static final long DEFAULT_SYNC_PERIOD_MILLIS = 1000 * 60 * 60 * 12;
// 1 Hour
private static final long DEFAULT_IMMEDIATE_EPG_DURATION_MILLIS = 1000 * 60 * 60;
// 48 Hour
private static final long DEFAULT_PERIODIC_EPG_DURATION_MILLIS = 1000 * 60 * 60 * 48;
private static final int PERIODIC_SYNC_JOB_ID = 0;
private static final int REQUEST_SYNC_JOB_ID = 1;
private static final int BATCH_OPERATION_COUNT = 100;
// 1 second
private static final long OVERRIDE_DEADLINE_MILLIS = 1000;
private static final String BUNDLE_KEY_SYNC_PERIOD = "bundle_key_sync_period";
private static final ExecutorService SINGLE_THREAD_EXECUTOR = Executors.newSingleThreadExecutor();
private final SparseArray<EpgSyncTask> mTaskArray = new SparseArray<>();
private static final Object mContextLock = new Object();
private Context mContext;
/**
* Returns the channels that your app contains.
*
* @return The list of channels for your app.
*/
public abstract List<Channel> getChannels() throws EpgSyncException;
/**
* Returns the programs that will appear for each channel.
*
* @param channelUri The Uri corresponding to the channel.
* @param channel The channel your programs will appear on.
* @param startMs The starting time in milliseconds since the epoch to generate programs. If
* your program starts before this starting time, it should be be included.
* @param endMs The ending time in milliseconds since the epoch to generate programs. If your
* program starts before this ending time, it should be be included.
* @return A list of programs for a given channel.
*/
public abstract List<Program> getProgramsForChannel(Uri channelUri, Channel channel, long startMs, long endMs) throws EpgSyncException;
@Override
public void onCreate() {
super.onCreate();
if (DEBUG) {
Log.d(TAG, "Created EpgSyncJobService");
}
synchronized (mContextLock) {
if (mContext == null) {
mContext = getApplicationContext();
}
}
}
@Override
public boolean onStartJob(JobParameters params) {
if (DEBUG) {
Log.d(TAG, "onStartJob(" + params.getJobId() + ")");
}
// Broadcast status
Intent intent = createSyncStartedIntent(params.getExtras().getString(BUNDLE_KEY_INPUT_ID));
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
EpgSyncTask epgSyncTask = new EpgSyncTask(params);
synchronized (mTaskArray) {
mTaskArray.put(params.getJobId(), epgSyncTask);
}
// Run the task on a single threaded custom executor in order not to block the AsyncTasks
// running on application side.
epgSyncTask.executeOnExecutor(SINGLE_THREAD_EXECUTOR);
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
synchronized (mTaskArray) {
int jobId = params.getJobId();
EpgSyncTask epgSyncTask = mTaskArray.get(jobId);
if (epgSyncTask != null) {
epgSyncTask.cancel(true);
mTaskArray.delete(params.getJobId());
}
}
return false;
}
/**
* Returns {@code true} if the {@code oldProgram} program is the same as the {@code newProgram}
* program but should update metadata. This updates the database instead of deleting and
* inserting a new program to keep the user's intent, eg. recording this program.
*/
public boolean shouldUpdateProgramMetadata(Program oldProgram, Program newProgram) {
// NOTE: Here, we update the old program if it has the same replacedle and overlaps with the
// new program. The test logic is just an example and you can modify this. E.g. check
// whether the both programs have the same program ID if your EPG supports any ID for
// the programs.
return oldProgram.getreplacedle().equals(newProgram.getreplacedle()) && oldProgram.getStartTimeUtcMillis() <= newProgram.getEndTimeUtcMillis() && newProgram.getStartTimeUtcMillis() <= oldProgram.getEndTimeUtcMillis();
}
/**
* Send the job to JobScheduler.
*/
private static void scheduleJob(Context context, JobInfo job) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = jobScheduler.schedule(job);
if (DEBUG) {
Log.d(TAG, "Scheduling result is " + result);
}
}
/**
* Initializes a job that will periodically update the app's channels and programs with a
* default period of 24 hours.
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param jobServiceComponent The {@link EpgSyncJobService} component name that will run.
*/
public static void setUpPeriodicSync(Context context, String inputId, ComponentName jobServiceComponent) {
setUpPeriodicSync(context, inputId, jobServiceComponent, DEFAULT_SYNC_PERIOD_MILLIS, DEFAULT_PERIODIC_EPG_DURATION_MILLIS);
}
/**
* Initializes a job that will periodically update the app's channels and programs.
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param jobServiceComponent The {@link EpgSyncJobService} component name that will run.
* @param fullSyncPeriod The period between when the job will run a full background sync in
* milliseconds.
* @param syncDuration The duration of EPG content to fetch in milliseconds. For a manual sync,
* this should be relatively short. For a background sync this should be long.
*/
public static void setUpPeriodicSync(Context context, String inputId, ComponentName jobServiceComponent, long fullSyncPeriod, long syncDuration) {
if (jobServiceComponent.getClreplaced().isreplacedignableFrom(EpgSyncJobService.clreplaced)) {
throw new IllegalArgumentException("This clreplaced does not extend EpgSyncJobService");
}
PersistableBundle persistableBundle = new PersistableBundle();
persistableBundle.putString(EpgSyncJobService.BUNDLE_KEY_INPUT_ID, inputId);
persistableBundle.putLong(EpgSyncJobService.BUNDLE_KEY_SYNC_PERIOD, syncDuration);
JobInfo.Builder builder = new JobInfo.Builder(PERIODIC_SYNC_JOB_ID, jobServiceComponent);
JobInfo jobInfo = builder.setExtras(persistableBundle).setPeriodic(fullSyncPeriod).setPersisted(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
scheduleJob(context, jobInfo);
if (DEBUG) {
Log.d(TAG, "Job has been scheduled for every " + fullSyncPeriod + "ms");
}
}
/**
* Manually requests a job to run now to retrieve EPG content for the next hour.
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param jobServiceComponent The {@link EpgSyncJobService} clreplaced that will run.
*/
public static void requestImmediateSync(Context context, String inputId, ComponentName jobServiceComponent) {
requestImmediateSync(context, inputId, DEFAULT_IMMEDIATE_EPG_DURATION_MILLIS, jobServiceComponent);
}
/**
* Manually requests a job to run now.
*
* <p>To check the current status of the sync, register a {@link
* android.content.BroadcastReceiver} with an {@link android.content.IntentFilter} which checks
* for the action {@link #ACTION_SYNC_STATUS_CHANGED}.
*
* <p>The sync status is an extra parameter in the {@link Intent} with key {@link #SYNC_STATUS}.
* The sync status is either {@link #SYNC_STARTED} or {@link #SYNC_FINISHED}.
*
* <p>Check that the value of {@link #BUNDLE_KEY_INPUT_ID} matches your {@link
* android.media.tv.TvInputService}. If you're calling this from your setup activity, you can
* get the extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
*
* <p>
*
* @param context Application's context.
* @param inputId Component name for the app's TvInputService. This can be received through an
* Intent extra parameter {@link TvInputInfo#EXTRA_INPUT_ID}.
* @param syncDuration The duration of EPG content to fetch in milliseconds. For a manual sync,
* this should be relatively short. For a background sync this should be long.
* @param jobServiceComponent The {@link EpgSyncJobService} clreplaced that will run.
*/
public static void requestImmediateSync(Context context, String inputId, long syncDuration, ComponentName jobServiceComponent) {
if (jobServiceComponent.getClreplaced().isreplacedignableFrom(EpgSyncJobService.clreplaced)) {
throw new IllegalArgumentException("This clreplaced does not extend EpgSyncJobService");
}
PersistableBundle persistableBundle = new PersistableBundle();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
persistableBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
persistableBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
}
persistableBundle.putString(EpgSyncJobService.BUNDLE_KEY_INPUT_ID, inputId);
persistableBundle.putLong(EpgSyncJobService.BUNDLE_KEY_SYNC_PERIOD, syncDuration);
JobInfo.Builder builder = new JobInfo.Builder(REQUEST_SYNC_JOB_ID, jobServiceComponent);
JobInfo jobInfo = builder.setExtras(persistableBundle).setOverrideDeadline(EpgSyncJobService.OVERRIDE_DEADLINE_MILLIS).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
scheduleJob(context, jobInfo);
if (DEBUG) {
Log.d(TAG, "Single job scheduled");
}
}
/**
* Cancels all pending jobs.
*
* @param context Application's context.
*/
public static void cancelAllSyncRequests(Context context) {
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.cancelAll();
}
/**
* @hide
*/
public clreplaced EpgSyncTask extends AsyncTask<Void, Void, Void> {
private final JobParameters params;
private String mInputId;
public EpgSyncTask(JobParameters params) {
this.params = params;
}
@Override
public Void doInBackground(Void... voids) {
PersistableBundle extras = params.getExtras();
mInputId = extras.getString(BUNDLE_KEY_INPUT_ID);
if (mInputId == null) {
broadcastError(ERROR_INPUT_ID_NULL);
return null;
}
if (isCancelled()) {
broadcastError(ERROR_EPG_SYNC_CANCELED);
return null;
}
List<Channel> tvChannels;
try {
tvChannels = getChannels();
} catch (EpgSyncException e) {
broadcastError(e.getReason());
return null;
}
ModelUtils.updateChannels(mContext, mInputId, tvChannels, new OnChannelDeletedCallback() {
@Override
public void onChannelDeleted(long rowId) {
SharedPreferences.Editor editor = mContext.getSharedPreferences(Constants.PREFERENCES_FILE_KEY, Context.MODE_PRIVATE).edit();
editor.remove(Constants.SHARED_PREFERENCES_KEY_LAST_CHANNEL_AD_PLAY + rowId);
editor.apply();
}
});
LongSparseArray<Channel> channelMap = ModelUtils.buildChannelMap(mContext.getContentResolver(), mInputId);
if (channelMap == null) {
broadcastError(ERROR_NO_CHANNELS);
return null;
}
// Default to one hour sync
long durationMs = extras.getLong(BUNDLE_KEY_SYNC_PERIOD, DEFAULT_IMMEDIATE_EPG_DURATION_MILLIS);
long startMs = System.currentTimeMillis();
long endMs = startMs + durationMs;
ChangeCount runningChangeCount = new ChangeCount();
for (int i = 0; i < channelMap.size(); ++i) {
Uri channelUri = TvContract.buildChannelUri(channelMap.keyAt(i));
if (isCancelled()) {
broadcastError(ERROR_EPG_SYNC_CANCELED);
return null;
}
List<Program> programs;
try {
programs = getProgramsForChannel(channelUri, channelMap.valueAt(i), startMs, endMs);
} catch (EpgSyncException e) {
broadcastError(e.getReason());
return null;
}
if (programs != null) {
if (DEBUG) {
Log.d(TAG, programs.toString());
}
for (int index = 0; index < programs.size(); index++) {
if (programs.get(index).getChannelId() == -1) {
// Automatically set the channel id if not set
programs.set(index, new Program.Builder(programs.get(index)).setChannelId(channelMap.valueAt(i).getId()).build());
}
}
// Double check if the job is cancelled, so that this task can be finished faster
// after cancel() is called.
if (isCancelled()) {
broadcastError(ERROR_EPG_SYNC_CANCELED);
return null;
}
updatePrograms(channelUri, programs, runningChangeCount);
}
Intent intent = createSyncScannedIntent(mInputId, i + 1, channelMap.size(), channelMap.valueAt(i).getDisplayName(), channelMap.valueAt(i).getDisplayNumber());
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
Log.i(TAG, mInputId + " synced " + runningChangeCount.total + " programs. Deleted " + runningChangeCount.deleteCount + " updated " + runningChangeCount.updateCount + " added " + runningChangeCount.addCount);
return null;
}
@Override
public void onPostExecute(Void success) {
finishEpgSync(params);
}
@Override
public void onCancelled(Void ignore) {
finishEpgSync(params);
}
private void finishEpgSync(JobParameters jobParams) {
if (DEBUG) {
Log.d(TAG, "taskFinished(" + jobParams.getJobId() + ")");
}
mTaskArray.delete(jobParams.getJobId());
jobFinished(jobParams, false);
if (DEBUG) {
Log.d(TAG, "Send out broadcast");
}
Intent intent = createSyncFinishedIntent(jobParams.getExtras().getString(BUNDLE_KEY_INPUT_ID));
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
private void broadcastError(int reason) {
Intent intent = createSyncErrorIntent(mInputId, reason);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
/**
* Returns a list of programs for the given time range.
*
* @param channel The {@link Channel} for the programs to return.
* @param programs The feed fetched from cloud.
* @param startTimeMs The start time of the range requested.
* @param endTimeMs The end time of the range requested.
* @return A list of programs for the channel within the specifed range. They may be
* repeated.
* @hide
*/
@VisibleForTesting
public List<Program> getPrograms(Channel channel, List<Program> programs, long startTimeMs, long endTimeMs) {
if (startTimeMs > endTimeMs) {
throw new IllegalArgumentException("Start time must be before end time");
}
List<Program> programForGivenTime = new ArrayList<>();
if (channel.getInternalProviderData() != null && !channel.getInternalProviderData().isRepeatable()) {
for (Program program : programs) {
if (program.getStartTimeUtcMillis() <= endTimeMs && program.getEndTimeUtcMillis() >= startTimeMs) {
programForGivenTime.add(new Program.Builder(program).setChannelId(channel.getId()).build());
}
}
return programForGivenTime;
}
// If repeat-programs is on, schedule the programs sequentially in a loop. To make every
// device play the same program in a given channel and time, we replacedumes the loop started
// from the epoch time.
long totalDurationMs = 0;
for (Program program : programs) {
totalDurationMs += (program.getEndTimeUtcMillis() - program.getStartTimeUtcMillis());
}
if (totalDurationMs <= 0) {
throw new IllegalArgumentException("The duration of all programs must be greater " + "than 0ms.");
}
long programStartTimeMs = startTimeMs - startTimeMs % totalDurationMs;
int i = 0;
final int programCount = programs.size();
while (programStartTimeMs < endTimeMs) {
Program programInfo = programs.get(i++ % programCount);
long programEndTimeMs = programStartTimeMs + totalDurationMs;
if (programInfo.getEndTimeUtcMillis() > -1 && programInfo.getStartTimeUtcMillis() > -1) {
programEndTimeMs = programStartTimeMs + (programInfo.getEndTimeUtcMillis() - programInfo.getStartTimeUtcMillis());
}
if (programEndTimeMs < startTimeMs) {
programStartTimeMs = programEndTimeMs;
continue;
}
// Shift advertisement time to match current program time.
InternalProviderData updateInternalProviderData = programInfo.getInternalProviderData();
shiftAdsTimeWithProgram(updateInternalProviderData, programInfo.getStartTimeUtcMillis(), programStartTimeMs);
programForGivenTime.add(new Program.Builder(programInfo).setChannelId(channel.getId()).setStartTimeUtcMillis(programStartTimeMs).setEndTimeUtcMillis(programEndTimeMs).setInternalProviderData(updateInternalProviderData).build());
programStartTimeMs = programEndTimeMs;
}
return programForGivenTime;
}
/**
* Shift advertisement time to match program playback time. For channels with repeated program,
* the time for current program may vary from what it was defined previously.
*
* @param oldProgramStartTimeMs Outdated program start time.
* @param newProgramStartTimeMs Updated program start time.
*/
private void shiftAdsTimeWithProgram(InternalProviderData internalProviderData, long oldProgramStartTimeMs, long newProgramStartTimeMs) {
if (internalProviderData == null) {
return;
}
long timeShift = newProgramStartTimeMs - oldProgramStartTimeMs;
List<Advertisement> oldAds = internalProviderData.getAds();
List<Advertisement> newAds = new ArrayList<>();
for (Advertisement oldAd : oldAds) {
newAds.add(new Advertisement.Builder(oldAd).setStartTimeUtcMillis(oldAd.getStartTimeUtcMillis() + timeShift).setStopTimeUtcMillis(oldAd.getStopTimeUtcMillis() + timeShift).build());
}
internalProviderData.setAds(newAds);
}
/**
* Updates the system database, TvProvider, with the given programs.
*
* <p>If there is any overlap between the given and existing programs, the existing ones
* will be updated with the given ones if they have the same replacedle or replaced.
*
* @param channelUri The channel where the program info will be added.
* @param newPrograms A list of {@link Program} instances which includes program
* information.
*/
private void updatePrograms(Uri channelUri, List<Program> newPrograms, ChangeCount runningChangeCount) {
final int fetchedProgramsCount = newPrograms.size();
runningChangeCount.total += fetchedProgramsCount;
if (fetchedProgramsCount == 0) {
broadcastError(ERROR_NO_PROGRAMS);
return;
}
List<Program> oldPrograms = ModelUtils.getPrograms(mContext.getContentResolver(), channelUri);
Program firstNewProgram = newPrograms.get(0);
int oldProgramsIndex = 0;
int newProgramsIndex = 0;
// Skip the past programs. They will be automatically removed by the system.
for (Program program : oldPrograms) {
if (program.getEndTimeUtcMillis() < System.currentTimeMillis() || program.getEndTimeUtcMillis() < firstNewProgram.getStartTimeUtcMillis()) {
oldProgramsIndex++;
} else {
break;
}
}
// Compare the new programs with old programs one by one and update/delete the old one
// or insert new program if there is no matching program in the database.
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
if (isCancelled()) {
return;
}
while (newProgramsIndex < fetchedProgramsCount) {
Program oldProgram = oldProgramsIndex < oldPrograms.size() ? oldPrograms.get(oldProgramsIndex) : null;
Program newProgram = newPrograms.get(newProgramsIndex);
boolean addNewProgram = false;
if (oldProgram != null && newProgram != null) {
if (oldProgram.equals(newProgram)) {
// Exact match. No need to update. Move on to the next programs.
oldProgramsIndex++;
newProgramsIndex++;
} else if (shouldUpdateProgramMetadata(oldProgram, newProgram)) {
// Partial match. Update the old program with the new one.
// NOTE: Use 'update' in this case instead of 'insert' and 'delete'. There
// could be application specific settings which belong to the old program.
ops.add(ContentProviderOperation.newUpdate(TvContract.buildProgramUri(oldProgram.getId())).withValues(newProgram.toContentValues()).build());
runningChangeCount.updateCount++;
oldProgramsIndex++;
newProgramsIndex++;
} else if (oldProgram.getEndTimeUtcMillis() < newProgram.getEndTimeUtcMillis()) {
// No match. Remove the old program first to see if the next program in
// {@code oldPrograms} partially matches the new program.
ops.add(ContentProviderOperation.newDelete(TvContract.buildProgramUri(oldProgram.getId())).build());
runningChangeCount.deleteCount++;
oldProgramsIndex++;
} else {
// No match. The new program does not match any of the old programs. Insert
// it as a new program.
addNewProgram = true;
newProgramsIndex++;
}
} else {
// No old programs. Just insert new programs.
addNewProgram = true;
newProgramsIndex++;
}
if (addNewProgram) {
ops.add(ContentProviderOperation.newInsert(TvContract.Programs.CONTENT_URI).withValues(newProgram.toContentValues()).build());
runningChangeCount.addCount++;
}
// Throttle the batch operation not to cause TransactionTooLargeException.
if (ops.size() > BATCH_OPERATION_COUNT || newProgramsIndex >= fetchedProgramsCount) {
try {
mContext.getContentResolver().applyBatch(TvContract.AUTHORITY, ops);
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Failed to insert programs.", e);
broadcastError(ERROR_DATABASE_INSERT);
return;
}
ops.clear();
}
}
}
}
@VisibleForTesting
public static Intent createSyncStartedIntent(String inputId) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(SYNC_STATUS, SYNC_STARTED);
return intent;
}
@VisibleForTesting
public static Intent createSyncScannedIntent(String inputId, int channelsScanned, int channelCount, String displayName, String displayNumber) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_CHANNELS_SCANNED, channelsScanned);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_CHANNEL_COUNT, channelCount);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NAME, displayName);
intent.putExtra(EpgSyncJobService.BUNDLE_KEY_SCANNED_CHANNEL_DISPLAY_NUMBER, displayNumber);
intent.putExtra(EpgSyncJobService.SYNC_STATUS, EpgSyncJobService.SYNC_SCANNED);
return intent;
}
@VisibleForTesting
public static Intent createSyncFinishedIntent(String inputId) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(SYNC_STATUS, SYNC_FINISHED);
return intent;
}
@VisibleForTesting
public static Intent createSyncErrorIntent(String inputId, int reason) {
Intent intent = new Intent(ACTION_SYNC_STATUS_CHANGED);
intent.putExtra(BUNDLE_KEY_INPUT_ID, inputId);
intent.putExtra(SYNC_STATUS, SYNC_ERROR);
intent.putExtra(BUNDLE_KEY_ERROR_REASON, reason);
return intent;
}
/**
* Propagates the reason for an EpgSync failure.
*/
public static clreplaced EpgSyncException extends Exception {
private final int reason;
/**
* Create EpgSyncException with the given {@code reason}.
*
* <p>This {@link EpgSyncJobService} sends reason such as {@link #ERROR_EPG_SYNC_CANCELED},
* {@link #ERROR_DATABASE_INSERT}, etc. Clreplacedes that extend the {@code EpgSyncJobService},
* should use custom reasons that start at {@link #ERROR_START_CUSTOM}.
*
* @param reason The reason sync failed.
*/
public EpgSyncException(int reason) {
this.reason = reason;
}
public int getReason() {
return reason;
}
}
/**
* Struct to hold change counts
*/
private static clreplaced ChangeCount {
long total = 0;
long deleteCount = 0;
long updateCount = 0;
long addCount = 0;
}
}
19
Source : USBVendorId.java
with Apache License 2.0
from z-jc
with Apache License 2.0
from z-jc
public clreplaced USBVendorId {
private static final SparseArray<String> IDS = new SparseArray<String>();
public static String vendorName(final int vendor_id) {
return IDS.get(vendor_id);
}
static {
IDS.put(10006, "YUEN DA ELECTRONIC PRODUCTS FACTORY");
IDS.put(10013, "Gionee Communication Equipment Co., Ltd. ShenZhen");
IDS.put(10022, "Universal Electronics Inc. (dba: TVIEW)");
IDS.put(1003, "Atmel Corporation");
IDS.put(1006, "Mitsumi");
IDS.put(1008, "HP Inc.");
IDS.put(10112, "M31 Technology Corp.");
IDS.put(10113, "Liteconn Co., Ltd.");
IDS.put(10121, "Suzhou WEIJU Electronics Technology Co., Ltd.");
IDS.put(10144, "Mondokey Limited");
IDS.put(10149, "Advantest Corporation");
IDS.put(10150, "iRobot Corporation");
IDS.put(1020, "Elitegroup Computer Systems");
IDS.put(1021, "Xilinx Inc.");
IDS.put(10226, "Sibridge Tech.");
IDS.put(1026, "ALi Corporation");
IDS.put(1027, "Future Technology Devices International Limited");
IDS.put(10275, "Dongguan Jiumutong Industry Co., Ltd.");
IDS.put(10289, "Power Integrations");
IDS.put(10291, "Oculus VR, Inc.");
IDS.put(10300, "HIGH TEK HARNESS ENTERPRISE CO., LTD.");
IDS.put(10316, "Full in Hope Co., Ltd.");
IDS.put(1032, "Quanta Computer Inc.");
IDS.put(10329, "Viconn Technology (HK) Co., Ltd.");
IDS.put(1033, "NEC Corporation");
IDS.put(1035, "Weltrend Semiconductor");
IDS.put(1037, "VIA Technologies, Inc.");
IDS.put(10374, "Seeed Technology Co., Ltd.");
IDS.put(10375, "Specwerkz");
IDS.put(1038, "MCCI Corporation");
IDS.put(10398, "Esselte Leitz GmbH & Co. KG");
IDS.put(10406, "E-SEEK Inc.");
IDS.put(1041, "BUFFALO INC.");
IDS.put(10423, "Pleora Technologies Inc.");
IDS.put(10431, "Vitetech Int'l Co., Ltd.");
IDS.put(1044, "Giga-Byte Technology Co., Ltd.");
IDS.put(10446, "Changzhou Shi Wujin Miqi East Electronic Co., Ltd.");
IDS.put(10457, "Shenzhen Ourconn Technology Co., Ltd.");
IDS.put(10458, "G.SKILL Int'l Enterprice Co., Ltd.");
IDS.put(1046, "Nuvoton Technology Corp.");
IDS.put(10466, "Surplus Electronic Technology Co., Ltd.");
IDS.put(10470, "BIAMP SYSTEMS");
IDS.put(10509, "IBCONN Technologies (Shenzhen) Co., Ltd.");
IDS.put(10510, "Fugoo Inc.");
IDS.put(10519, "Pan Xin Precision Electronics Co., Ltd.");
IDS.put(10530, "Dongguan Digi-in Digital Technology Co., Ltd.");
IDS.put(1054, "Creative Labs");
IDS.put(10540, "GENUSION, Inc.");
IDS.put(10544, "Ineda Systems Inc.");
IDS.put(10545, "Jolla Ltd.");
IDS.put(10546, "Peraso Technologies, Inc.");
IDS.put(10549, "Nanjing Magewell Electronics Co., Ltd.");
IDS.put(10560, "Shenzhen Yiwanda Electronics Co., Ltd.");
IDS.put(1057, "Nokia Corporation");
IDS.put(10575, "Dollar Connection Ltd.");
IDS.put(10595, "BIO-key International, Inc.");
IDS.put(1060, "Microchip-SMSC");
IDS.put(10603, "Xacti Corporation");
IDS.put(10615, "Shenzhen Zowee Technology Co., Ltd.");
IDS.put(10643, "ADPlaus Technology Limited");
IDS.put(10646, "Unwired Technology");
IDS.put(1065, "Cirrus Logic Inc.");
IDS.put(10657, "Union Electric Plug & Connector Corp.");
IDS.put(10674, "Canova Tech");
IDS.put(10685, "Silicon Works");
IDS.put(10695, "HANRICO ANFU ELECTRONICS CO., LTD.");
IDS.put(10700, "Kodak Alaris");
IDS.put(10702, "JGR Optics Inc.");
IDS.put(10703, "Richtek Technology Corporation");
IDS.put(10705, "Binatone Electronics Int. Ltd.");
IDS.put(1071, "Molex Inc.");
IDS.put(10715, "Shenzhen iBoard Technology Co., Ltd.");
IDS.put(10719, "SMIT(HK) Limited");
IDS.put(1072, "Fujitsu Component Limited");
IDS.put(10725, "Dongguan Kechenda Electronic Technology Co., Ltd.");
IDS.put(10726, "Fengshun Peiying Electro-Acoustic Co., Ltd.");
IDS.put(10744, "MD ELEKTRONIK GmbH");
IDS.put(10749, "Bad Elf, LLC");
IDS.put(10770, "Vreo Limited");
IDS.put(10772, "Kanex");
IDS.put(10781, "Oxford Nanopore Technologies");
IDS.put(10782, "Obsidian Technology");
IDS.put(10783, "Lucent Trans Electronics Co., Ltd.");
IDS.put(10784, "GUOGUANG GROUP CO., LTD.");
IDS.put(10788, "CNPLUS");
IDS.put(10789, "Fourstar Group");
IDS.put(10790, "Tragant International Co., Ltd.");
IDS.put(10791, "DongGuan LianGang Optoelectronic Technology Co., Ltd.");
IDS.put(10797, "Atrust Computer Corp.");
IDS.put(10798, "VIA Alliance Semiconductor Co., Ltd.");
IDS.put(10799, "BSUN Electronics Co., Ltd.");
IDS.put(1080, "Advanced Micro Devices");
IDS.put(10807, "RTD Embedded Technologies, Inc.");
IDS.put(10816, "Shenzhen Choseal Industrial Co., Ltd.");
IDS.put(10817, "Canyon Semiconductor");
IDS.put(10818, "Spectra7 Microsystems Corp.");
IDS.put(10821, "Meizu Technology Co., Ltd.");
IDS.put(10822, "Hubei Yingtong Telecommunication Cable Inc.");
IDS.put(10829, "Wilder Technologies");
IDS.put(10837, "Diodes Inc.");
IDS.put(10846, "DuPont");
IDS.put(1085, "Lexmark International Inc.");
IDS.put(10852, "Zhejiang Songcheng Electronics Co., Ltd.");
IDS.put(10859, "VSN Mobil");
IDS.put(10875, "Bellwether Electronic Corp.");
IDS.put(10878, "VAIO Corporation");
IDS.put(10879, "Perixx Computer GmbH");
IDS.put(10885, "HANK ELECTRONICS CO., LTD");
IDS.put(10892, "Sonnet Technologies, Inc.");
IDS.put(10893, "Keysight Technologies Inc.");
IDS.put(10895, "Manutronics Vietnam Joint Stock Company");
IDS.put(10900, "G2 Touch Co., Ltd.");
IDS.put(10902, "Micromax Informatics Ltd");
IDS.put(10910, "SEIKO SOLUTIONS Inc.");
IDS.put(10912, "Casco Products Corp.");
IDS.put(10922, "Virtium Technology, Inc.");
IDS.put(10923, "Field and Company LLC, dba Leef USA");
IDS.put(10928, "GM Global Technology Operations LLC");
IDS.put(10931, "Key Asic Inc.");
IDS.put(10943, "Revolabs, Inc.");
IDS.put(10945, "Lattice Semiconductor Corp");
IDS.put(10947, "Foshan Nanhai Saga Audio Equipment Co., Ltd.");
IDS.put(10957, "Silergy Corp.");
IDS.put(10963, "Shenzhen Hali-Power Industrial Co., Ltd.");
IDS.put(10971, "I-PEX (Dai-ichi Seiko)");
IDS.put(10973, "SEE-PLUS INDUSTRIAL LTD.");
IDS.put(10990, "Adapt-IP Company");
IDS.put(10997, "Libratone A/S");
IDS.put(10999, "Shenzhen Hazens Automotive Electronics (SZ) Co., Ltd.");
IDS.put(11000, "Jiangsu Toppower Automotive Electronics Co., Ltd.");
IDS.put(11001, "Drapho Electronics Technology Co., Ltd.");
IDS.put(1102, "Alps Electric Co., Ltd.");
IDS.put(11022, "Le Shi Zhi Xin Electronic Technology (Tian Jin) Limited");
IDS.put(11024, "Cardiac Insight, Inc.");
IDS.put(11028, "EverPro Technologies Company, Ltd.");
IDS.put(11029, "Rosenberger Hochfrequenztechnik");
IDS.put(11035, "Dongguan City Sanji Electronics Co., Ltd.");
IDS.put(11037, "Lintes Technology Co., Ltd.");
IDS.put(11039, "KinnexA, Inc.");
IDS.put(11042, "Metra Electronics Corp.");
IDS.put(11044, "KeepKey, LLC");
IDS.put(11047, "FluxData Incorporated");
IDS.put(1105, "Texas Instruments");
IDS.put(11061, "replacedem Technology Co., Ltd.");
IDS.put(11062, "Dongguan City Jianghan Electronics Co., Ltd.");
IDS.put(11063, "Huizhou Desay SV Automotive Co., Ltd.");
IDS.put(11064, "Ningbo Rixing Electronics Co., Ltd.");
IDS.put(11069, "GuangDong YuanFeng Automotive Electroics Co., Ltd.");
IDS.put(11080, "Sounding Audio Industrial Limited");
IDS.put(11082, "Yueqing Huaxin Electronic Co., Ltd.");
IDS.put(11098, "Universal Audio, Inc.");
IDS.put(11111, "Lifesize, Inc.");
IDS.put(11123, "Pioneer DJ Corporation");
IDS.put(11124, "Embedded Intelligence, Inc.");
IDS.put(11125, "New Matter");
IDS.put(11126, "Shanghai Wingtech Electronic Technology Co., Ltd.");
IDS.put(11127, "Epiphan Systems Inc.");
IDS.put(11130, "Spin Master Far East Ltd.");
IDS.put(11131, "Gigaset Digital Technology (Shenzhen) Co., Ltd.");
IDS.put(11132, "Noveltek Semiconductor Corp.");
IDS.put(11139, "Silicon Line GmbH");
IDS.put(11140, "Ever Win International Corp.");
IDS.put(11144, "Socionext Inc.");
IDS.put(11145, "Ugreen Group Limited");
IDS.put(11146, "Shanghai Pateo Electronic Equipment Mfg. Co., Ltd.");
IDS.put(1115, "Renesas Electronics Corp.");
IDS.put(11154, "i-BLADES, Inc.");
IDS.put(11155, "Altia Systems Inc.");
IDS.put(11156, "ShenZhen Baoyuanda Electronics Co., Ltd.");
IDS.put(11157, "iST - Integrated Service Technology Inc.");
IDS.put(11158, "HYUNDAI MOBIS Co., Ltd.");
IDS.put(11161, "360fly, Inc.");
IDS.put(11162, "HUIZHOU CHENG SHUO HARDWARE PLASTIC CO., LTD.");
IDS.put(11163, "Zhongshan Aute Electronics Technology Co., Ltd.");
IDS.put(11164, "Guangdong King Link Industrial Co., Ltd.");
IDS.put(11167, "Scietera Technologies, Inc.");
IDS.put(11168, "InVue Security Products");
IDS.put(11169, "I-Sheng Electric Wire & Cable Co., Ltd.");
IDS.put(11170, "China Daheng Group Inc Beijing Image Vision Tech Branch");
IDS.put(11171, "Shenzhen FeiTianXia Technology Ltd.");
IDS.put(11172, "Shenzhen HengJia New Energy Auto Part Co., Ltd.");
IDS.put(11175, "77 Elektronika Kft.");
IDS.put(11176, "YUDU EASON ELECTRONIC CO., LTD.");
IDS.put(1118, "Microsoft Corporation");
IDS.put(11181, "XIN JI (SHENZHEN) COMPUTER PARTS CO., LTD.");
IDS.put(11189, "Silk ID Systems");
IDS.put(11190, "3D Imaging & Simulations Corp. (3DISC)");
IDS.put(11191, "Dongguan ChengXiang Industrial Co., Ltd.");
IDS.put(11192, "OCC (Zhuhai) Electronic Co., Ltd.");
IDS.put(11194, "Sinseader Electronic Co., Ltd.");
IDS.put(11195, "DONGGUAN YELLOWKNIFE Industrial Co., Ltd.");
IDS.put(11197, "RF Creations Ltd.");
IDS.put(11198, "Chengyi Semiconductors (Shanghai) Co., Ltd.");
IDS.put(11199, "Shenzhen Shinning Electronic Co., Ltd.");
IDS.put(11200, "Shenzhen WFD Electronics Co., Ltd.");
IDS.put(11201, "Dongguan Sino Syncs Industrial Co., Ltd.");
IDS.put(11202, "JNTC Co., Ltd.");
IDS.put(11208, "DONGGUAN POLIXIN ELECTRIC CO., LTD.");
IDS.put(11209, "Tama Electric (Suzhou) Co., Ltd.");
IDS.put(1121, "Primax Electronics");
IDS.put(11210, "Exvision, Inc.");
IDS.put(11216, "mophie, LLC");
IDS.put(11219, "Dongguan ULT-unite electronic technology co., LTD");
IDS.put(11220, "JL Audio, Inc.");
IDS.put(11221, "Cable Matters Inc.");
IDS.put(11222, "CoroWare, Inc.");
IDS.put(11229, "Charm Sciences Inc.");
IDS.put(1123, "EATON");
IDS.put(11230, "Pickering Interfaces Limited");
IDS.put(11231, "Hangzhou Hikvision Digital Technology Co., Ltd.");
IDS.put(11232, "FULLINK ELECTRONICS TECHNOLOGY (SZ) LTD");
IDS.put(11233, "AutoChips Inc.");
IDS.put(11234, "Electric Connector Technology Co., Ltd.");
IDS.put(11237, "LELTEK");
IDS.put(11238, "Dongguan KaiWin Electronics Co., Ltd.");
IDS.put(11239, "BEFS Co., Ltd.");
IDS.put(11240, "Archisite, Inc.");
IDS.put(11241, "Magneti Marelli S.p.A Electr BL");
IDS.put(11246, "Ventev Mobile");
IDS.put(11247, "Quanta Storage Inc.");
IDS.put(11248, "Tech-Top Technology Limited");
IDS.put(11253, "Shenzhen YOOBAO Technology Co., Ltd.");
IDS.put(11254, "Shenzhen Sinotek Technology Co., Ltd.");
IDS.put(11255, "KEYW");
IDS.put(11256, "Visual Land Inc.");
IDS.put(11264, "MEEM SL Ltd");
IDS.put(11265, "Dongguan Arin Electronics Technology Co., Ltd.");
IDS.put(11266, "DongGuan City JianNuo Electronics Co., Ltd.");
IDS.put(11268, "Shenzhen XOX Electronics Co., Ltd.");
IDS.put(11269, "Protop International Inc.");
IDS.put(11270, "Microsemi Semiconductor (US) Inc.");
IDS.put(11271, "Webcloak LLC");
IDS.put(11272, "INVECAS INC.");
IDS.put(11274, "ATANS Technology Inc.");
IDS.put(11275, "Triple Win Precision Technology Co., Ltd.");
IDS.put(11276, "IC Realtech");
IDS.put(11277, "Embrava Pty Ltd");
IDS.put(1128, "Wieson Technologies Co., Ltd.");
IDS.put(11280, "Sinotronics Co., Ltd.");
IDS.put(11281, "ALLBEST ELECTRONICS TECHNOLOGY CO., LTD.");
IDS.put(11282, "Shenzhen Xin Kai Feng Electronics Factory");
IDS.put(11283, "MOST WELL Technology Corp.");
IDS.put(11284, "Buffalo Memory Co., Ltd.");
IDS.put(11285, "Xentris Wireless");
IDS.put(11286, "Priferential Accessories Ltd");
IDS.put(11289, "Sunlike Technology Co., Ltd.");
IDS.put(11290, "Young Fast Optoelectronics Co., Ltd.");
IDS.put(11291, "ISAW Camera Inc");
IDS.put(11298, "Qanba USA, LLC");
IDS.put(11299, "Super Micro Computer Inc.");
IDS.put(11302, "Micromax International Corporation");
IDS.put(11304, "Granite River Labs replacedan Ltd.");
IDS.put(11305, "Coagent Enterprise Limited");
IDS.put(11306, "LEIA Inc.");
IDS.put(11309, "Shenzhen Ebull Technology Limited");
IDS.put(1131, "American Megatrends");
IDS.put(11310, "Hualun Technology Co., Ltd.");
IDS.put(11311, "Sensel, Inc.");
IDS.put(11319, "Shenzhen Adition Audio Science & Technology Co., Ltd.");
IDS.put(11320, "Goldenconn Electronics Technology (Suzhou) Co., Ltd.");
IDS.put(11321, "JIB Electronics Technology Co., Ltd.");
IDS.put(11322, "Changzhou Shinco Automotive Electronics Co., Ltd.");
IDS.put(11323, "Shenzhen Hangsheng Electronics Corp., Ltd.");
IDS.put(11324, "Beartooth Radio, Inc.");
IDS.put(11325, "Audience, A Knowles Company");
IDS.put(11327, "Nextbit Systems, Inc.");
IDS.put(11328, "Leadtrend");
IDS.put(11329, "Adaptertek Technology Co., Ltd.");
IDS.put(1133, "Logitech Inc.");
IDS.put(11330, "Feature Integration Technology Inc.");
IDS.put(11331, "Avegant Corporation");
IDS.put(11335, "Chunghsin International Electronics Co., Ltd.");
IDS.put(11336, "Delphi Electrical Centers (Shanghai) Co., Ltd.");
IDS.put(11341, "VVETEK DOO");
IDS.put(11347, "Huizhou Foryou General Electronics Co., Ltd.");
IDS.put(11348, "LifeWatch Technologies Ltd.");
IDS.put(11349, "Magicleap");
IDS.put(11355, "Dongguan City Shenglan Electronics Co., LTD.");
IDS.put(11356, "Neusoft Corporation");
IDS.put(11357, "SIP Simya Electronics Technology Co., Ltd.");
IDS.put(11358, "GNSD Automotive Co., Ltd.");
IDS.put(11359, "YOODS Co., Ltd.");
IDS.put(11360, "Sirin Mobile Technologies AG");
IDS.put(11361, "Jadmam Corporation dba: Boytone");
IDS.put(11373, "Gibson Innovations");
IDS.put(11374, "Shen Zhen Xian Shuo Technology Co. LTD");
IDS.put(11375, "PST Eletronica LTDA");
IDS.put(11376, "PERI, Inc.");
IDS.put(11377, "Bozhou BoTong Information Technology Co., Ltd.");
IDS.put(11383, "Profindustry GmbH");
IDS.put(11384, "BRAGI GmbH");
IDS.put(11385, "WAWGD, Inc. (DBA: Foresight Sports)");
IDS.put(11390, "Dongguan Allpreplaced Electronic Co., Ltd.");
IDS.put(11391, "SHENZHEN D-VITEC INDUSTRIAL CO., LTD.");
IDS.put(11392, "motomobile AG");
IDS.put(11393, "Indie Semiconductor");
IDS.put(11397, "Audientes");
IDS.put(11403, "Huizhou Dehong Technology Co., Ltd.");
IDS.put(11404, "PowerCenter Technology Limited");
IDS.put(11405, "Mizco International, Inc.");
IDS.put(11408, "I. AM. PLUS, LLC");
IDS.put(11409, "Corigine, Inc.");
IDS.put(11410, "Ningbo Yinzhou Shengke Electronics Co., Ltd.");
IDS.put(11417, "Prusa Research s.r.o.");
IDS.put(11423, "e-Smart Systems Pvt. Ltd.");
IDS.put(11424, "Leagtech Jiangxi Electronic Co., Ltd.");
IDS.put(11425, "Dongguan Yujia Electronics Technology Co., Ltd.");
IDS.put(11426, "GuangZhou MingPing Electronics Technology");
IDS.put(11427, "DJI Technology Co., Ltd.");
IDS.put(11428, "Shenzhen Alex Technology Co., Ltd.");
IDS.put(11433, "JITS TECHNOLOGY CO., LIMITED");
IDS.put(11434, "LIVV Brand llc");
IDS.put(11444, "Ava Enterprises, Inc. dba: Boss Audio Systems");
IDS.put(11448, "Shenzhen Sydixon Electronic Technology Co., Ltd.");
IDS.put(11449, "On-Bright Electronics (Shanghai) Co., Ltd.");
IDS.put(11450, "Dongguan Puxu Industrial Co., Ltd.");
IDS.put(11451, "Shenzhen Soling Indusrtial Co., Ltd.");
IDS.put(11453, "EGGCYTE, INC.");
IDS.put(11455, "Donggguan Yuhua Electronic Co., Ltd.");
IDS.put(11456, "Hangzhou Zero Zero Technology Co., Ltd.");
IDS.put(11462, "Prodigy Technovations Pvt Ltd");
IDS.put(11463, "EmergiTech, Inc");
IDS.put(11464, "Hewlett Packard Enterprise");
IDS.put(11465, "Monolithic Power Systems Inc.");
IDS.put(11467, "USB Memory Direct");
IDS.put(11468, "Silicon Mitus Inc.");
IDS.put(11472, "Technics Global Electronics & JCE Co., Ltd.");
IDS.put(11478, "Immersive Media");
IDS.put(11479, "Cosemi Technologies Inc.");
IDS.put(11481, "Cambrionix Ltd");
IDS.put(11482, "CXUN Co. Ltd.");
IDS.put(11483, "China Tsp Inc");
IDS.put(11490, "Yanfeng Visteon (Chongqing) Automotive Electronics Co");
IDS.put(11491, "Alcorlink Corp.");
IDS.put(11492, "ISBC Ltd.");
IDS.put(11493, "InX8 Inc dba: AKiTiO");
IDS.put(11494, "SDAN Tecchnology Co., Ltd.");
IDS.put(11495, "Lemobile Information Technology (Beijing) Co., Ltd.");
IDS.put(11496, "GongGuan HWX Electronic Technology Co., Ltd.");
IDS.put(11497, "Suzhu Jingshi Electronic Technology Co., Ltd.");
IDS.put(11498, "Zhong Shan City Richsound Electronic Industrial Ltd.");
IDS.put(11499, "Dongguang Kangbang Electronics Co., Ltd.");
IDS.put(1151, "Plantronics, Inc.");
IDS.put(1154, "Kyocera Corporation");
IDS.put(1155, "STMicroelectronics");
IDS.put(1161, "Foxconn / Hon Hai");
IDS.put(1165, "ITE Tech Inc.");
IDS.put(1177, "Yamaha Corporation");
IDS.put(1188, "Hitachi, Ltd.");
IDS.put(1191, "Visioneer");
IDS.put(1193, "Canon Inc.");
IDS.put(1200, "Nikon Corporation");
IDS.put(1201, "Pan International");
IDS.put(1204, "Cypress Semiconductor");
IDS.put(1205, "ROHM Co., Ltd.");
IDS.put(1207, "Compal Electronics, Inc.");
IDS.put(1208, "Seiko Epson Corp.");
IDS.put(1211, "I-O Data Device, Inc.");
IDS.put(1221, "Fujitsu Ltd.");
IDS.put(1227, "FUJIFILM Corporation");
IDS.put(1238, "Mentor Graphics");
IDS.put(1240, "Microchip Technology Inc.");
IDS.put(1241, "Holtek Semiconductor, Inc.");
IDS.put(1242, "Panasonic Corporation");
IDS.put(1245, "Sharp Corporation");
IDS.put(1250, "Exar Corporation");
IDS.put(1254, "Identiv, Inc.");
IDS.put(1256, "Samsung Electronics Co., Ltd.");
IDS.put(1260, "Tokyo Electron Device Limited");
IDS.put(1266, "Chicony Electronics Co., Ltd.");
IDS.put(1271, "Newnex Technology Corp.");
IDS.put(1273, "Brother Industries, Ltd.");
IDS.put(1276, "SUNPLUS TECHNOLOGY CO., LTD.");
IDS.put(1278, "PFU Limited");
IDS.put(1281, "Fujikura/DDK");
IDS.put(1282, "Acer, Inc.");
IDS.put(1287, "Hosiden Corporation");
IDS.put(1293, "Belkin International, Inc.");
IDS.put(1300, "FCI Electronics");
IDS.put(1302, "Longwell Electronics/Longwell Company");
IDS.put(1305, "Star Micronics Co., LTD");
IDS.put(1309, "American Power Conversion");
IDS.put(1314, "ACON, Advanced-Connectek, Inc.");
IDS.put(1343, "Synopsys, Inc.");
IDS.put(1356, "Sony Corporation");
IDS.put(1360, "Fuji Xerox Co., Ltd.");
IDS.put(1367, "ATEN International Co. Ltd.");
IDS.put(1369, "Cadence Design Systems, Inc.");
IDS.put(1386, "WACOM Co., Ltd.");
IDS.put(1389, "EIZO Corporation");
IDS.put(1390, "Elecom Co., Ltd.");
IDS.put(1394, "Conexant Systems, Inc.");
IDS.put(1398, "BAFO/Quality Computer Accessories");
IDS.put(1403, "Y-E Data, Inc.");
IDS.put(1404, "AVM GmbH");
IDS.put(1410, "Roland Corporation");
IDS.put(1412, "RATOC Systems, Inc.");
IDS.put(1419, "Infineon Technologies");
IDS.put(1423, "Alcor Micro, Corp.");
IDS.put(1424, "OMRON Corporation");
IDS.put(1447, "Bose Corporation");
IDS.put(1449, "OmniVision Technologies, Inc.");
IDS.put(1452, "Apple");
IDS.put(1453, "Y.C. Cable U.S.A., Inc");
IDS.put(14627, "National Instruments");
IDS.put(1470, "Tyco Electronics Corp., a TE Connectivity Ltd. company");
IDS.put(1473, "MegaChips Corporation");
IDS.put(1478, "Qualcomm, Inc");
IDS.put(1480, "Foxlink/Cheng Uei Precision Industry Co., Ltd.");
IDS.put(1482, "Ricoh Company Ltd.");
IDS.put(1498, "Microtek International Inc.");
IDS.put(1504, "Symbol Technologies");
IDS.put(1507, "Genesys Logic, Inc.");
IDS.put(1509, "Fuji Electric Co., Ltd.");
IDS.put(1525, "Unixtar Technology Inc.");
IDS.put(1529, "Datalogic ADC");
IDS.put(1535, "LeCroy Corporation");
IDS.put(1539, "Novatek Microelectronics Corp.");
IDS.put(1545, "SMK Manufacturing Inc.");
IDS.put(1551, "Joinsoon Electronics Mfg. Co., Ltd.");
IDS.put(1555, "TransAct Technologies Incorporated");
IDS.put(1561, "Seiko Instruments Inc.");
IDS.put(1582, "JPC/MAIN SUPER Inc.");
IDS.put(1583, "Sin Sheng Terminal & Machine Inc.");
IDS.put(1593, "Chrontel, Inc.");
IDS.put(1611, "replacedog Devices, Inc. Development Tools");
IDS.put(1612, "Ji-Haw Industrial Co., Ltd");
IDS.put(1614, "Suyin Corporation");
IDS.put(1621, "Space Shuttle Hi-Tech Co.,Ltd.");
IDS.put(1622, "Glory Mark Electronic Ltd.");
IDS.put(1623, "Tekcon Electronics Corp.");
IDS.put(1624, "Sigma Designs, Inc.");
IDS.put(1631, "Good Way Technology Co., Ltd. & GWC technology Inc");
IDS.put(1632, "TSAY-E (BVI) International Inc.");
IDS.put(1633, "Hamamatsu Photonics K.K.");
IDS.put(1642, "Total Technologies, Ltd.");
IDS.put(1659, "Prolific Technology, Inc.");
IDS.put(16700, "Dell Inc.");
IDS.put(1680, "Golden Bridge Electech Inc.");
IDS.put(1689, "Tektronix, Inc.");
IDS.put(1690, "Askey Computer Corporation");
IDS.put(1709, "Greatland Electronics Taiwan Ltd.");
IDS.put(1710, "Eurofins Digital Testing Belgium");
IDS.put(1720, "Pixela Corporation");
IDS.put(1724, "Oki Data Corporation");
IDS.put(1727, "Leoco Corporation");
IDS.put(1732, "Bizlink Technology, Inc.");
IDS.put(1736, "SIIG, Inc.");
IDS.put(1747, "Mitsubishi Electric Corporation");
IDS.put(1758, "Heisei Technology Co., Ltd.");
IDS.put(1802, "Oki Electric Industry Co., Ltd.");
IDS.put(1805, "Comoss Electronic Co., Ltd.");
IDS.put(1809, "Magic Control Technology Corp.");
IDS.put(1816, "Imation Corp.");
IDS.put(1838, "Sunix Co., Ltd.");
IDS.put(1846, "Lorom Industrial Co., Ltd.");
IDS.put(1848, "Mad Catz, Inc.");
IDS.put(1899, "HID Global GmbH");
IDS.put(1901, "Denso Corporation");
IDS.put(1913, "Fairchild Semiconductor");
IDS.put(1921, "SanDisk Corporation");
IDS.put(1937, "Copartner Technology Corporation");
IDS.put(1954, "National Technical Systems");
IDS.put(1971, "Plustek, Inc.");
IDS.put(1972, "OLYMPUS CORPORATION");
IDS.put(1975, "TIME Interconnect Ltd.");
IDS.put(1994, "AVerMedia Technologies, Inc.");
IDS.put(1999, "Casio Computer Co., Ltd.");
IDS.put(2015, "David Electronics Company, Ltd.");
IDS.put(2039, "Century Corporation");
IDS.put(2058, "Evermuch Technology Co., Ltd.");
IDS.put(2101, "Action Star Enterprise Co., Ltd.");
IDS.put(2112, "Argosy Research Inc.");
IDS.put(2122, "Wipro Limited");
IDS.put(2159, "MEC IMEX INC/HPT");
IDS.put(2205, "Icron Technologies Corporation");
IDS.put(2247, "TAI TWUN ENTERPRISE CO., LTD.");
IDS.put(2276, "Pioneer Corporation");
IDS.put(2278, "Gemalto SA");
IDS.put(2310, "FARADAY Technology Corp.");
IDS.put(2313, "Audio-Technica Corp.");
IDS.put(2316, "Silicon Motion, Inc. - Taiwan");
IDS.put(2334, "Garmin International");
IDS.put(2352, "Toshiba Corporation");
IDS.put(2362, "Pixart Imaging, Inc.");
IDS.put(2363, "Plextor LLC");
IDS.put(2366, "J.S.T. Mfg. Co., Ltd.");
IDS.put(2385, "Kingston Technology Company");
IDS.put(2389, "NVIDIA");
IDS.put(2395, "Medialogic Corporation");
IDS.put(2397, "Polycom, Inc.");
IDS.put(2468, "Contech Research, Inc.");
IDS.put(2472, "Lin Shiung Enterprise Co., Ltd.");
IDS.put(2475, "replacedan Cash Machine Co., Ltd.");
IDS.put(2498, "NISCA Corporation");
IDS.put(2511, "Electronics Testing Center, Taiwan");
IDS.put(2522, "A-FOUR TECH CO., LTD.");
IDS.put(2555, "Altera");
IDS.put(2578, "Cambridge Silicon Radio Ltd.");
IDS.put(2583, "HOYA Corporation");
IDS.put(2631, "Hirose Electric Co., Ltd.");
IDS.put(2636, "COMPUTEX Co., Ltd.");
IDS.put(2640, "Mimaki Engineering Co., Ltd.");
IDS.put(2652, "Broadcom Corp.");
IDS.put(2667, "Green House Co., Ltd.");
IDS.put(2702, "replacedan Aviation Electronics Industry Ltd. (JAE)");
IDS.put(2727, "Wincor Nixdorf GmbH & Co KG");
IDS.put(2733, "Rohde & Schwarz GmbH & Co. KG");
IDS.put(2787, "Allion Labs, Inc.");
IDS.put(2821, "ASUSTek Computer Inc.");
IDS.put(2849, "Yokogawa Electric Corporation");
IDS.put(2851, "Pan-Asia Electronics Co., Ltd.");
IDS.put(2894, "Musical Electronics Ltd.");
IDS.put(2907, "Anritsu Corporation");
IDS.put(2922, "Maxim Integrated Products");
IDS.put(2965, "ASIX Electronics Corporation");
IDS.put(2967, "O2Micro, Inc.");
IDS.put(3010, "Seagate Technology LLC");
IDS.put(3034, "Realtek Semiconductor Corp.");
IDS.put(3035, "Ericsson AB");
IDS.put(3044, "Elka International Ltd.");
IDS.put(3056, "Pace Micro Technology PLC");
IDS.put(3108, "Taiyo Yuden Co., Ltd.");
IDS.put(3129, "Aeroflex");
IDS.put(3132, "Radius Co., Ltd.");
IDS.put(3141, "Sonix Technology Co., Ltd.");
IDS.put(3158, "Billion Bright (HK) Corporation Limited");
IDS.put(3161, "Dong Guan Shinko Wire Co., Ltd.");
IDS.put(3170, "Chant Sincere Co., Ltd");
IDS.put(3190, "Solid State System Co., Ltd.");
IDS.put(3209, "Honda Tsushin Kogyo Co., Ltd");
IDS.put(3245, "Motorola Solutions");
IDS.put(3255, "Singatron Enterprise Co. Ltd.");
IDS.put(3268, "emsys Embedded Systems GmbH");
IDS.put(32902, "Intel Corporation");
IDS.put(3294, "Z-Com INC.");
IDS.put(3313, "e-CONN ELECTRONIC CO., LTD.");
IDS.put(3314, "ENE Technology Inc.");
IDS.put(3351, "NALTEC, Inc.");
IDS.put(3402, "NF Corporation");
IDS.put(3403, "Grape Systems Inc.");
IDS.put(3409, "Volex (Asia) Pte Ltd");
IDS.put(3425, "MEILU ELECTRONICS (SHENZHEN) CO., LTD.");
IDS.put(3441, "Hirakawa Hewtech Corp.");
IDS.put(3452, "Taiwan Line Tek Electronic Co., Ltd.");
IDS.put(3463, "Dolby Laboratories Inc.");
IDS.put(3468, "C-MEDIA ELECTRONICS INC.");
IDS.put(3472, "Sure-Fire Electrical Corporation");
IDS.put(3495, "IOGEAR, Inc.");
IDS.put(3504, "Micro-Star International Co., Ltd.");
IDS.put(3537, "Contek Electronics Co., Ltd.");
IDS.put(3540, "Custom Engineering SPA");
IDS.put(3641, "Smart Modular Technologies, Inc.");
IDS.put(3658, "Shenzhen Bao Hing Electric Wire & Cable Mfr. Co.");
IDS.put(3673, "Bourns, Inc.");
IDS.put(3690, "Megawin Technology Co., Ltd.");
IDS.put(3698, "Hsi-Chin Electronics Co., Ltd.");
IDS.put(3714, "Ching Tai Electric Wire & Cable Co., Ltd.");
IDS.put(3724, "Well Force Electronic Co., Ltd");
IDS.put(3725, "MediaTek Inc.");
IDS.put(3728, "CRU");
IDS.put(3744, "Ours Technology Inc.");
IDS.put(3762, "Y-S ELECTRONIC CO., LTD.");
IDS.put(3778, "Sweetray Industrial Ltd.");
IDS.put(3779, "Axell Corporation");
IDS.put(3782, "InnoVISION Multimedia Limited");
IDS.put(3790, "TaiSol Electronics Co., Ltd.");
IDS.put(3812, "Sunrich Technology (H.K.) Ltd.");
IDS.put(3868, "Funai Electric Co., Ltd.");
IDS.put(3873, "IOI Technology Corporation");
IDS.put(3890, "YFC-BonEagle Electric Co., Ltd.");
IDS.put(3896, "Nien-Yi Industrial Corp.");
IDS.put(3916, "WORLDWIDE CABLE OPTO CORP.");
IDS.put(3923, "Taiyo Cable (Dongguan) Co. Ltd.");
IDS.put(3924, "Kawai Musical Instruments Mfg. Co., Ltd.");
IDS.put(3936, "GuangZhou Chief Tech Electronic Technology Co. Ltd.");
IDS.put(3944, "UQUEST, LTD.");
IDS.put(3991, "CviLux Corporation");
IDS.put(4003, "Chief Land Electronic Co., Ltd.");
IDS.put(4046, "Sony Mobile Communications");
IDS.put(4087, "CHI SHING COMPUTER ACCESSORIES CO., LTD.");
IDS.put(4096, "Speed Tech Corp.");
IDS.put(4100, "LG Electronics Inc.");
IDS.put(4101, "Apacer Technology Inc.");
IDS.put(4134, "Newly Corporation");
IDS.put(4168, "Targus Group International");
IDS.put(4172, "AMCO TEC International Inc.");
IDS.put(4183, "ON Semiconductor");
IDS.put(4184, "Western Digital Technologies, Inc.");
IDS.put(4227, "CANON ELECTRONICS INC.");
IDS.put(4235, "Grand-tek Technology Co., Ltd.");
IDS.put(4236, "Robert Bosch GmbH");
IDS.put(4238, "Lotes Co., Ltd.");
IDS.put(4266, "Cables To Go");
IDS.put(4267, "Universal Global Scientific Industrial Co., Ltd.");
IDS.put(4292, "Silicon Laboratories, Inc.");
IDS.put(4301, "Kycon Inc.");
IDS.put(4362, "Moxa Inc.");
IDS.put(4370, "Golden Bright (Sichuan) Electronic Technology Co Ltd");
IDS.put(4382, "VSO ELECTRONICS CO., LTD.");
IDS.put(4398, "Master Hill Electric Wire and Cable Co., Ltd.");
IDS.put(4477, "Santa Electronic Inc.");
IDS.put(4505, "Sierra Wireless Inc.");
IDS.put(4522, "GlobalMedia Group, LLC");
IDS.put(4528, "ATECH FLASH TECHNOLOGY");
IDS.put(4643, "SKYCABLE ENTERPRISE CO., LTD.");
IDS.put(4703, "ADATA Technology Co., Ltd.");
IDS.put(4716, "Aristocrat Technologies");
IDS.put(4717, "Bel Stewart");
IDS.put(4742, "MARVELL SEMICONDUCTOR, INC.");
IDS.put(4756, "RISO KAGAKU CORP.");
IDS.put(4792, "Zhejiang Xinya Electronic Technology Co., Ltd.");
IDS.put(4817, "Huawei Technologies Co., Ltd.");
IDS.put(4823, "Better Holdings (HK) Limited");
IDS.put(4907, "Konica Minolta, Inc.");
IDS.put(4925, "Jasco Products Company");
IDS.put(4989, "Pericom Semiconductor Corp.");
IDS.put(5008, "TomTom International B.V.");
IDS.put(5075, "AzureWave Technologies, Inc.");
IDS.put(5117, "Initio Corporation");
IDS.put(5118, "Phison Electronics Corp.");
IDS.put(5134, "Telechips, Inc.");
IDS.put(5145, "ABILITY ENTERPRISE CO., LTD.");
IDS.put(5148, "Leviton Manufacturing");
IDS.put(5271, "Panstrong Company Ltd.");
IDS.put(5293, "CTK Corporation");
IDS.put(5296, "StarTech.com Ltd.");
IDS.put(5376, "Ellisys");
IDS.put(5404, "VeriSilicon Holdings Co., Ltd.");
IDS.put(5421, "JMicron Technology Corp.");
IDS.put(5422, "HLDS (Hitachi-LG Data Storage, Inc.)");
IDS.put(5440, "Phihong Technology Co., Ltd.");
IDS.put(5451, "PNY Technologies Inc.");
IDS.put(5453, "Rapid Conn, Connect County Holdings Bhd");
IDS.put(5454, "D & M Holdings, Inc.");
IDS.put(5480, "Sunf Pu Technology Co., Ltd");
IDS.put(5488, "ALLTOP TECHNOLOGY CO., LTD.");
IDS.put(5510, "Palconn Technology Co., Ltd.");
IDS.put(5528, "Kunshan Guoji Electronics Co., Ltd.");
IDS.put(5546, "DongGuan Ya Lian Electronics Co., Ltd.");
IDS.put(5645, "Samtec");
IDS.put(5694, "HongLin Electronics Co., Ltd.");
IDS.put(5753, "Total Phase");
IDS.put(5766, "ZOOM Corporation");
IDS.put(5836, "silex technology, Inc.");
IDS.put(5946, "F. Hoffmann-La Roche AG");
IDS.put(5960, "MQP Electronics Ltd.");
IDS.put(5964, "ASMedia Technology Inc.");
IDS.put(5998, "UD electronic corp.");
IDS.put(6001, "Shenzhen Alex Connector Co., Ltd.");
IDS.put(6002, "System Level Solutions, Inc.");
IDS.put(6018, "Spreadtrum Hong Kong Limited");
IDS.put(6024, "ShenZhen Litkconn Technology Co., Ltd.");
IDS.put(6053, "Advanced Connection Technology Inc.");
IDS.put(6095, "Hip Hing Cable & Plug Mfy. Ltd.");
IDS.put(6121, "DisplayLink (UK) Ltd.");
IDS.put(6127, "Lenovo");
IDS.put(6133, "K.K. Rocky");
IDS.put(6160, "Wanshih Electronic Co., Ltd.");
IDS.put(6185, "Dongguan YuQiu Electronics Co., Ltd.");
IDS.put(6193, "Gwo Jinn Industries Co., Ltd.");
IDS.put(6297, "Linkiss Co., Ltd.");
IDS.put(6353, "Google Inc.");
IDS.put(6394, "Kuang Ying Computer Equipment Co., Ltd.");
IDS.put(6421, "Nordic Semiconductor ASA");
IDS.put(6448, "Shenzhen Xianhe Technology Co., Ltd.");
IDS.put(6449, "Ningbo Broad Telecommunication Co., Ltd.");
IDS.put(6470, "Irisguard UK Ltd");
IDS.put(6473, "Lab126");
IDS.put(6481, "Hyperstone GmbH");
IDS.put(6487, "BIOS Corporation");
IDS.put(6626, "Solomon Systech Limited");
IDS.put(6639, "Pak Heng Technology (Shenzhen) Co., Ltd.");
IDS.put(6655, "Best Buy China Ltd.");
IDS.put(6666, "USB-IF non-workshop");
IDS.put(6709, "Artesyn Technologies Inc.");
IDS.put(6720, "TERMINUS TECHNOLOGY INC.");
IDS.put(6766, "Global Unichip Corp.");
IDS.put(6786, "Proconn Technology Co., Ltd.");
IDS.put(6794, "Simula Technology Inc.");
IDS.put(6795, "SGS Taiwan Ltd.");
IDS.put(6830, "Johnson Component & Equipments Co., Ltd.");
IDS.put(6834, "Allied Vision Technologies GmbH");
IDS.put(6859, "Salcomp Plc");
IDS.put(6865, "Desan Wire Co., Ltd.");
IDS.put(6944, "MStar Semiconductor, Inc.");
IDS.put(6984, "Plastron Precision Co., Ltd.");
IDS.put(7013, "The Hong Kong Standards and Testing Centre Ltd.");
IDS.put(7048, "ShenMing Electron (Dong Guan) Co., Ltd.");
IDS.put(7086, "Vuzix Corporation");
IDS.put(7108, "Ford Motor Co.");
IDS.put(7118, "Contac Cable Industrial Limited");
IDS.put(7119, "Sunplus Innovation Technology Inc.");
IDS.put(7120, "Hangzhou Riyue Electronics Co., Ltd.");
IDS.put(7158, "Orient Semiconductor Electronics, Ltd.");
IDS.put(7207, "SHENZHEN DNS INDUSTRIES CO., LTD.");
IDS.put(7217, "LS Mtron Ltd.");
IDS.put(7229, "NONIN MEDICAL INC.");
IDS.put(7275, "Philips & Lite-ON Digital Solutions Corporation");
IDS.put(7310, "ASTRON INTERNATIONAL CORP.");
IDS.put(7320, "ALPINE ELECTRONICS, INC.");
IDS.put(7347, "Aces Electronics Co., Ltd.");
IDS.put(7348, "OPEX CORPORATION");
IDS.put(7390, "Telecommunications Technology replacedociation (TTA)");
IDS.put(7434, "Visteon Corporation");
IDS.put(7465, "Horng Tong Enterprise Co., Ltd.");
IDS.put(7501, "Pegatron Corporation");
IDS.put(7516, "Fresco Logic Inc.");
IDS.put(7529, "Walta Electronic Co., Ltd.");
IDS.put(7543, "Yueqing Changling Electronic Instrument Corp., Ltd.");
IDS.put(7584, "Parade Technologies, Inc.");
IDS.put(7647, "L&T Technology Services");
IDS.put(7649, "Actions Microelectronics Co., Ltd.");
IDS.put(7666, "China Telecommunication Technology Labs - Terminals");
IDS.put(7668, "SHEN ZHEN FORMAN PRECISION INDUSTRY CO., LTD.");
IDS.put(7682, "GLOBEMASTER TECHNOLOGIES CO., LTD.");
IDS.put(7696, "Point Grey Research Inc.");
IDS.put(7751, "HUNG TA H.T.ENTERPRISE CO., LTD.");
IDS.put(7758, "Etron Technology, Inc.");
IDS.put(7795, "COMLINK ELECTRONICS CO., LTD.");
IDS.put(7818, "HIBEST Electronic (DongGuan) Co., Ltd.");
IDS.put(7825, "Other World Computing");
IDS.put(7863, "WIN WIN PRECISION INDUSTRIAL CO., LTD.");
IDS.put(7879, "Gefen Inc.");
IDS.put(7881, "MOSER BAER INDIA LIMITED");
IDS.put(7898, "AIRTIES WIRELESS NETWORKS");
IDS.put(7956, "Astoria Networks GmbH");
IDS.put(7969, "Scosche Industries");
IDS.put(7976, "Cal-Comp Electronics & Communications");
IDS.put(7977, "replacedogix Semiconductor, Inc.");
IDS.put(7989, "Amphenol ShouhMin Industry (ShenZhen) Co., Ltd");
IDS.put(7996, "Chang Yang Electronics Company Ltd.");
IDS.put(8073, "Dongguan Goldconn Electronics Co., Ltd.");
IDS.put(8074, "Morning Star Industrial Co., Ltd.");
IDS.put(8117, "Unify Software and Solutions GmbH & Co. KG");
IDS.put(8137, "NXP Semiconductors");
IDS.put(8181, "Changzhou Wujin BEST Electronic Cables Co., Ltd.");
IDS.put(8205, "Belkin Electronic (Changzhou) Co., Ltd.");
IDS.put(8220, "Freeport Resources Enterprises Corp.");
IDS.put(8222, "Qingdao Haier Telecom Co., Ltd.");
IDS.put(8284, "Shenzhen Tronixin Electronics Co., Ltd.");
IDS.put(8294, "Unicorn Electronics Components Co., Ltd.");
IDS.put(8334, "Luxshare-ICT");
IDS.put(8341, "CE LINK LIMITED");
IDS.put(8342, "Microconn Electronic Co., Ltd.");
IDS.put(8367, "Shenzhen CARVE Electronics Co., Ltd.");
IDS.put(8382, "BURY GmbH & Co. KG");
IDS.put(8384, "FENGHUA KINGSUN CO., LTD.");
IDS.put(8386, "Sumitomo Electric Ind., Ltd., Optical Comm. R&D Lab");
IDS.put(8439, "XIMEA s.r.o.");
IDS.put(8457, "VIA Labs, Inc.");
IDS.put(8492, "Shenzhen Linoya Electronic Co., Ltd.");
IDS.put(8494, "Amphenol replacedembleTech (Xiamen) Co., Ltd.");
IDS.put(8524, "Y Soft Corporation");
IDS.put(8550, "JVC KENWOOD Corporation");
IDS.put(8564, "Transcend Information, Inc.");
IDS.put(8566, "TMC/Allion Test Labs");
IDS.put(8613, "Genesis Technology USA, Inc.");
IDS.put(8627, "Dongguan Teconn Electronics Technology Co., Ltd.");
IDS.put(8644, "Netcom Technology (HK) Limited");
IDS.put(8659, "Compupack Technology Co., Ltd.");
IDS.put(8667, "G-Max Technology Co., Ltd.");
IDS.put(8679, "Sagemcom Broadband SAS");
IDS.put(8695, "Wuerth-Elektronik eiSos GmbH & Co. KG");
IDS.put(8707, "Shin Shin Co., Ltd.");
IDS.put(8709, "3eYamaichi Electronics Co., Ltd.");
IDS.put(8710, "Wiretek International Investment Ltd.");
IDS.put(8711, "Fuzhou Rockchip Electronics Co., Ltd.");
IDS.put(8752, "Plugable Technologies");
IDS.put(8756, "T-CONN PRECISION CORPORATION");
IDS.put(8831, "Granite River Labs");
IDS.put(8842, "Hotron Precision Electronic Ind. Corp.");
IDS.put(8875, "Trigence Semiconductor, Inc.");
IDS.put(8888, "Motorola Mobility Inc.");
IDS.put(8904, "Karming Electronic (Shenzhen) Co., Ltd.");
IDS.put(8981, "Avery Design Systems, Inc.");
IDS.put(8993, "iKingdom Corp. (d.b.a. iConnectivity)");
IDS.put(9051, "KangXiang Electronic Co., Ltd.");
IDS.put(9068, "ZheJiang Chunsheng Electronics Co., Ltd.");
IDS.put(9130, "DOK (HK) Trading Limited");
IDS.put(9132, "Marunix Electron Limited");
IDS.put(9165, "Avconn Precise Connector Co., Ltd.");
IDS.put(9184, "BitifEye Digital Test Solutions GmbH");
IDS.put(9205, "Speed Conn Co., Ltd.");
IDS.put(9222, "INSIDE Secure");
IDS.put(9292, "Minebea Co., Ltd.");
IDS.put(9299, "BAANTO");
IDS.put(9338, "Suzhou Jutze Technologies Co., Ltd");
IDS.put(9355, "DONGGUAN SYNCONN PRECISION INDUSTRY CO. LTD.");
IDS.put(9382, "Shenzhen Pangngai Industrial Co., Ltd.");
IDS.put(9422, "Shenzhen Deren Electronic Co., Ltd.");
IDS.put(9424, "Smith Micro Software, Inc.");
IDS.put(9453, "ZEN FACTORY GROUP (ASIA) LTD.");
IDS.put(9481, "Chain-In Electronic Co., Ltd.");
IDS.put(9514, "SUZHOU KELI TECHNOLOGY DEVELOPMENT CO., LTD.");
IDS.put(9515, "TOP Exacreplacedude Industry (ShenZhen) Co., Ltd.");
IDS.put(9525, "ShenZhen Hogend Precision Technology Co., Ltd.");
IDS.put(9527, "Norel Systems Ltd.");
IDS.put(9556, "replacedA ABLOY AB");
IDS.put(9575, "DongGuan LongTao Electronic Co., Ltd.");
IDS.put(9577, "DongGuan City MingJi Electronics Co., Ltd.");
IDS.put(9589, "Weida Hi-Tech Co., Ltd.");
IDS.put(9593, "Dongguan Wisechamp Electronic Co., Ltd.");
IDS.put(9613, "Sequans Communications");
IDS.put(9636, "ALGOLTEK, INC.");
IDS.put(9651, "DongGuan Elinke Industrial Co., Ltd.");
IDS.put(9679, "Corning Optical Communications LLC");
IDS.put(9714, "Dongguan Jinyue Electronics Co., Ltd.");
IDS.put(9723, "RICOH IMAGING COMPANY, LTD.");
IDS.put(9742, "DongGuan HYX Industrial Co., Ltd.");
IDS.put(9753, "Advanced Silicon SA");
IDS.put(9756, "EISST Limited");
IDS.put(9771, "YTOP Electronics Technical (Kunshan) Co., Ltd.");
IDS.put(9841, "Innovative Logic");
IDS.put(9842, "GoPro");
IDS.put(9846, "Basler AG");
IDS.put(9851, "Palpilot International Corp.");
IDS.put(9896, "UNIREX CORPORATION");
IDS.put(9917, "Integral Memory Plc.");
IDS.put(9973, "Morning Star Digital Connector Co., Ltd.");
IDS.put(9984, "MITACHI CO., LTD.");
IDS.put(9999, "HGST, a Western Digital Company");
}
}
19
Source : USBMonitor.java
with Apache License 2.0
from z-jc
with Apache License 2.0
from z-jc
public final clreplaced USBMonitor {
// TODO set false on production
private static final boolean DEBUG = false;
private static final String TAG = "USBMonitor";
private static final String ACTION_USB_PERMISSION_BASE = "com.serenegiant.USB_PERMISSION.";
private final String ACTION_USB_PERMISSION = ACTION_USB_PERMISSION_BASE + hashCode();
public static final String ACTION_USB_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
/**
* openしているUsbControlBlock
*/
private final ConcurrentHashMap<UsbDevice, UsbControlBlock> mCtrlBlocks = new ConcurrentHashMap<UsbDevice, UsbControlBlock>();
private final SparseArray<WeakReference<UsbDevice>> mHasPermissions = new SparseArray<WeakReference<UsbDevice>>();
private final WeakReference<Context> mWeakContext;
private final UsbManager mUsbManager;
private final OnDeviceConnectListener mOnDeviceConnectListener;
private PendingIntent mPermissionIntent = null;
private List<DeviceFilter> mDeviceFilters = new ArrayList<DeviceFilter>();
/**
* コールバックをワーカースレッドで呼び出すためのハンドラー
*/
private final Handler mAsyncHandler;
private volatile boolean destroyed;
/**
* USB機器の状態変更時のコールバックリスナー
*/
public interface OnDeviceConnectListener {
/**
* called when device attached
* @param device
*/
public void onAttach(UsbDevice device);
/**
* called when device dettach(after onDisconnect)
* @param device
*/
public void onDettach(UsbDevice device);
/**
* called after device opend
* @param device
* @param ctrlBlock
* @param createNew
*/
public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew);
/**
* called when USB device removed or its power off (this callback is called after device closing)
* @param device
* @param ctrlBlock
*/
public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock);
/**
* called when canceled or could not get permission from user
* @param device
*/
public void onCancel(UsbDevice device);
}
public USBMonitor(final Context context, final OnDeviceConnectListener listener) {
if (DEBUG)
Log.v(TAG, "USBMonitor:Constructor");
if (listener == null)
throw new IllegalArgumentException("OnDeviceConnectListener should not null.");
mWeakContext = new WeakReference<Context>(context);
mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
mOnDeviceConnectListener = listener;
mAsyncHandler = HandlerThreadHandler.createHandler(TAG);
destroyed = false;
if (DEBUG)
Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
}
/**
* Release all related resources,
* never reuse again
*/
public void destroy() {
if (DEBUG)
Log.i(TAG, "destroy:");
unregister();
if (!destroyed) {
destroyed = true;
// モニターしているUSB機器を全てcloseする
final Set<UsbDevice> keys = mCtrlBlocks.keySet();
if (keys != null) {
UsbControlBlock ctrlBlock;
try {
for (final UsbDevice key : keys) {
ctrlBlock = mCtrlBlocks.remove(key);
if (ctrlBlock != null) {
ctrlBlock.close();
}
}
} catch (final Exception e) {
Log.e(TAG, "destroy:", e);
}
}
mCtrlBlocks.clear();
try {
mAsyncHandler.getLooper().quit();
} catch (final Exception e) {
Log.e(TAG, "destroy:", e);
}
}
}
/**
* register BroadcastReceiver to monitor USB events
* @throws IllegalStateException
*/
public synchronized void register() throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
if (mPermissionIntent == null) {
if (DEBUG)
Log.i(TAG, "register:");
final Context context = mWeakContext.get();
if (context != null) {
mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
final IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
// ACTION_USB_DEVICE_ATTACHED never comes on some devices so it should not be added here
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
context.registerReceiver(mUsbReceiver, filter);
}
// start connection check
mDeviceCounts = 0;
mAsyncHandler.postDelayed(mDeviceCheckRunnable, 1000);
}
}
/**
* unregister BroadcastReceiver
* @throws IllegalStateException
*/
public synchronized void unregister() throws IllegalStateException {
// 接続チェック用Runnableを削除
mDeviceCounts = 0;
if (!destroyed) {
mAsyncHandler.removeCallbacks(mDeviceCheckRunnable);
}
if (mPermissionIntent != null) {
// if (DEBUG) Log.i(TAG, "unregister:");
final Context context = mWeakContext.get();
try {
if (context != null) {
context.unregisterReceiver(mUsbReceiver);
}
} catch (final Exception e) {
Log.w(TAG, e);
}
mPermissionIntent = null;
}
}
public synchronized boolean isRegistered() {
return !destroyed && (mPermissionIntent != null);
}
/**
* set device filter
* @param filter
* @throws IllegalStateException
*/
public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
mDeviceFilters.clear();
mDeviceFilters.add(filter);
}
/**
* デバイスフィルターを追加
* @param filter
* @throws IllegalStateException
*/
public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
mDeviceFilters.add(filter);
}
/**
* デバイスフィルターを削除
* @param filter
* @throws IllegalStateException
*/
public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
mDeviceFilters.remove(filter);
}
/**
* set device filters
* @param filters
* @throws IllegalStateException
*/
public void setDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
mDeviceFilters.clear();
mDeviceFilters.addAll(filters);
}
/**
* add device filters
* @param filters
* @throws IllegalStateException
*/
public void addDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
mDeviceFilters.addAll(filters);
}
/**
* remove device filters
* @param filters
*/
public void removeDeviceFilter(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
mDeviceFilters.removeAll(filters);
}
/**
* return the number of connected USB devices that matched device filter
* @return
* @throws IllegalStateException
*/
public int getDeviceCount() throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
return getDeviceList().size();
}
/**
* return device list, return empty list if no device matched
* @return
* @throws IllegalStateException
*/
public List<UsbDevice> getDeviceList() throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
return getDeviceList(mDeviceFilters);
}
/**
* return device list, return empty list if no device matched
* @param filters
* @return
* @throws IllegalStateException
*/
public List<UsbDevice> getDeviceList(final List<DeviceFilter> filters) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
final List<UsbDevice> result = new ArrayList<UsbDevice>();
if (deviceList != null) {
if ((filters == null) || filters.isEmpty()) {
result.addAll(deviceList.values());
} else {
for (final UsbDevice device : deviceList.values()) {
for (final DeviceFilter filter : filters) {
if ((filter != null) && filter.matches(device)) {
// when filter matches
if (!filter.isExclude) {
result.add(device);
}
break;
}
}
}
}
}
return result;
}
/**
* return device list, return empty list if no device matched
* @param filter
* @return
* @throws IllegalStateException
*/
public List<UsbDevice> getDeviceList(final DeviceFilter filter) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
final List<UsbDevice> result = new ArrayList<UsbDevice>();
if (deviceList != null) {
for (final UsbDevice device : deviceList.values()) {
if ((filter == null) || (filter.matches(device) && !filter.isExclude)) {
result.add(device);
}
}
}
return result;
}
/**
* get USB device list, without filter
* @return
* @throws IllegalStateException
*/
public Iterator<UsbDevice> getDevices() throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
Iterator<UsbDevice> iterator = null;
final HashMap<String, UsbDevice> list = mUsbManager.getDeviceList();
if (list != null)
iterator = list.values().iterator();
return iterator;
}
/**
* output device list to LogCat
*/
public final void dumpDevices() {
final HashMap<String, UsbDevice> list = mUsbManager.getDeviceList();
if (list != null) {
final Set<String> keys = list.keySet();
if (keys != null && keys.size() > 0) {
final StringBuilder sb = new StringBuilder();
for (final String key : keys) {
final UsbDevice device = list.get(key);
final int num_interface = device != null ? device.getInterfaceCount() : 0;
sb.setLength(0);
for (int i = 0; i < num_interface; i++) {
sb.append(String.format(Locale.US, "interface%d:%s", i, device.getInterface(i).toString()));
}
Log.i(TAG, "key=" + key + ":" + device + ":" + sb.toString());
}
} else {
Log.i(TAG, "no device");
}
} else {
Log.i(TAG, "no device");
}
}
/**
* return whether the specific Usb device has permission
* @param device
* @return true: 指定したUsbDeviceにパーミッションがある
* @throws IllegalStateException
*/
public final boolean hasPermission(final UsbDevice device) throws IllegalStateException {
if (destroyed)
throw new IllegalStateException("already destroyed");
return updatePermission(device, device != null && mUsbManager.hasPermission(device));
}
/**
* 内部で保持しているパーミッション状態を更新
* @param device
* @param hasPermission
* @return hasPermission
*/
private boolean updatePermission(final UsbDevice device, final boolean hasPermission) {
final int deviceKey = getDeviceKey(device, true);
synchronized (mHasPermissions) {
if (hasPermission) {
if (mHasPermissions.get(deviceKey) == null) {
mHasPermissions.put(deviceKey, new WeakReference<UsbDevice>(device));
}
} else {
mHasPermissions.remove(deviceKey);
}
}
return hasPermission;
}
/**
* request permission to access to USB device
* @param device
* @return true if fail to request permission
*/
public synchronized boolean requestPermission(final UsbDevice device) {
// if (DEBUG) Log.v(TAG, "requestPermission:device=" + device);
boolean result = false;
if (isRegistered()) {
if (device != null) {
if (mUsbManager.hasPermission(device)) {
// call onConnect if app already has permission
processConnect(device);
} else {
try {
// パーミッションがなければ要求する
mUsbManager.requestPermission(device, mPermissionIntent);
} catch (final Exception e) {
// Android5.1.xのGALAXY系でandroid.permission.sec.MDM_APP_MGMTという意味不明の例外生成するみたい
Log.w(TAG, e);
processCancel(device);
result = true;
}
}
} else {
processCancel(device);
result = true;
}
} else {
processCancel(device);
result = true;
}
return result;
}
/**
* 指定したUsbDeviceをopenする
* @param device
* @return
* @throws SecurityException パーミッションがなければSecurityExceptionを投げる
*/
public UsbControlBlock openDevice(final UsbDevice device) throws SecurityException {
if (hasPermission(device)) {
UsbControlBlock result = mCtrlBlocks.get(device);
if (result == null) {
// この中でopenDeviceする
result = new UsbControlBlock(USBMonitor.this, device);
mCtrlBlocks.put(device, result);
}
return result;
} else {
throw new SecurityException("has no permission");
}
}
/**
* BroadcastReceiver for USB permission
*/
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
if (destroyed)
return;
final String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
// when received the result of requesting USB permission
synchronized (USBMonitor.this) {
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (device != null) {
// get permission, call onConnect
processConnect(device);
}
} else {
// failed to get permission
processCancel(device);
}
}
} else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
updatePermission(device, hasPermission(device));
processAttach(device);
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
// when device removed
final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null) {
UsbControlBlock ctrlBlock = mCtrlBlocks.remove(device);
if (ctrlBlock != null) {
// cleanup
ctrlBlock.close();
}
mDeviceCounts = 0;
processDettach(device);
}
}
}
};
/**
* number of connected & detected devices
*/
private volatile int mDeviceCounts = 0;
/**
* periodically check connected devices and if it changed, call onAttach
*/
private final Runnable mDeviceCheckRunnable = new Runnable() {
@Override
public void run() {
if (destroyed)
return;
final List<UsbDevice> devices = getDeviceList();
final int n = devices.size();
final int hasPermissionCounts;
final int m;
synchronized (mHasPermissions) {
hasPermissionCounts = mHasPermissions.size();
mHasPermissions.clear();
for (final UsbDevice device : devices) {
hasPermission(device);
}
m = mHasPermissions.size();
}
if ((n > mDeviceCounts) || (m > hasPermissionCounts)) {
mDeviceCounts = n;
if (mOnDeviceConnectListener != null) {
for (int i = 0; i < n; i++) {
final UsbDevice device = devices.get(i);
mAsyncHandler.post(new Runnable() {
@Override
public void run() {
mOnDeviceConnectListener.onAttach(device);
}
});
}
}
}
// confirm every 2 seconds
mAsyncHandler.postDelayed(this, 2000);
}
};
/**
* open specific USB device
* @param device
*/
private final void processConnect(final UsbDevice device) {
if (destroyed)
return;
updatePermission(device, true);
mAsyncHandler.post(new Runnable() {
@Override
public void run() {
if (DEBUG)
Log.v(TAG, "processConnect:device=" + device);
UsbControlBlock ctrlBlock;
final boolean createNew;
ctrlBlock = mCtrlBlocks.get(device);
if (ctrlBlock == null) {
ctrlBlock = new UsbControlBlock(USBMonitor.this, device);
mCtrlBlocks.put(device, ctrlBlock);
createNew = true;
} else {
createNew = false;
}
if (mOnDeviceConnectListener != null) {
mOnDeviceConnectListener.onConnect(device, ctrlBlock, createNew);
}
}
});
}
private final void processCancel(final UsbDevice device) {
if (destroyed)
return;
if (DEBUG)
Log.v(TAG, "processCancel:");
updatePermission(device, false);
if (mOnDeviceConnectListener != null) {
mAsyncHandler.post(new Runnable() {
@Override
public void run() {
mOnDeviceConnectListener.onCancel(device);
}
});
}
}
private final void processAttach(final UsbDevice device) {
if (destroyed)
return;
if (DEBUG)
Log.v(TAG, "processAttach:");
if (mOnDeviceConnectListener != null) {
mAsyncHandler.post(new Runnable() {
@Override
public void run() {
mOnDeviceConnectListener.onAttach(device);
}
});
}
}
private final void processDettach(final UsbDevice device) {
if (destroyed)
return;
if (DEBUG)
Log.v(TAG, "processDettach:");
if (mOnDeviceConnectListener != null) {
mAsyncHandler.post(new Runnable() {
@Override
public void run() {
mOnDeviceConnectListener.onDettach(device);
}
});
}
}
/**
* USB機器毎の設定保存用にデバイスキー名を生成する。
* ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
* 同種の製品だと同じキー名になるので注意
* @param device nullなら空文字列を返す
* @return
*/
public static final String getDeviceKeyName(final UsbDevice device) {
return getDeviceKeyName(device, null, false);
}
/**
* USB機器毎の設定保存用にデバイスキー名を生成する。
* useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
* @param device
* @param useNewAPI
* @return
*/
public static final String getDeviceKeyName(final UsbDevice device, final boolean useNewAPI) {
return getDeviceKeyName(device, null, useNewAPI);
}
/**
* USB機器毎の設定保存用にデバイスキー名を生成する。この機器名をHashMapのキーにする
* UsbDeviceがopenしている時のみ有効
* ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
* serialがnullや空文字でなければserialを含めたデバイスキー名を生成する
* useNewAPI=trueでAPIレベルを満たしていればマニュファクチャ名, バージョン, コンフィギュレーションカウントも使う
* @param device nullなら空文字列を返す
* @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
* @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
* @return
*/
@SuppressLint("NewApi")
public static final String getDeviceKeyName(final UsbDevice device, final String serial, final boolean useNewAPI) {
if (device == null)
return "";
final StringBuilder sb = new StringBuilder();
// API >= 12
sb.append(device.getVendorId());
// API >= 12
sb.append("#");
// API >= 12
sb.append(device.getProductId());
// API >= 12
sb.append("#");
// API >= 12
sb.append(device.getDeviceClreplaced());
// API >= 12
sb.append("#");
// API >= 12
sb.append(device.getDeviceSubclreplaced());
// API >= 12
sb.append("#");
// API >= 12
sb.append(device.getDeviceProtocol());
if (!TextUtils.isEmpty(serial)) {
sb.append("#");
sb.append(serial);
}
if (useNewAPI && BuildCheck.isAndroid5()) {
sb.append("#");
if (TextUtils.isEmpty(serial)) {
// API >= 21
sb.append(device.getSerialNumber());
// API >= 21
sb.append("#");
}
// API >= 21
sb.append(device.getManufacturerName());
// API >= 21
sb.append("#");
// API >= 21
sb.append(device.getConfigurationCount());
// API >= 21
sb.append("#");
if (BuildCheck.isMarshmallow()) {
// API >= 23
sb.append(device.getVersion());
// API >= 23
sb.append("#");
}
}
// if (DEBUG) Log.v(TAG, "getDeviceKeyName:" + sb.toString());
return sb.toString();
}
/**
* デバイスキーを整数として取得
* getDeviceKeyNameで得られる文字列のhasCodeを取得
* ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
* 同種の製品だと同じデバイスキーになるので注意
* @param device nullなら0を返す
* @return
*/
public static final int getDeviceKey(final UsbDevice device) {
return device != null ? getDeviceKeyName(device, null, false).hashCode() : 0;
}
/**
* デバイスキーを整数として取得
* getDeviceKeyNameで得られる文字列のhasCodeを取得
* useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
* @param device
* @param useNewAPI
* @return
*/
public static final int getDeviceKey(final UsbDevice device, final boolean useNewAPI) {
return device != null ? getDeviceKeyName(device, null, useNewAPI).hashCode() : 0;
}
/**
* デバイスキーを整数として取得
* getDeviceKeyNameで得られる文字列のhasCodeを取得
* serialがnullでuseNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
* @param device nullなら0を返す
* @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
* @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
* @return
*/
public static final int getDeviceKey(final UsbDevice device, final String serial, final boolean useNewAPI) {
return device != null ? getDeviceKeyName(device, serial, useNewAPI).hashCode() : 0;
}
public static clreplaced UsbDeviceInfo {
public String usb_version;
public String manufacturer;
public String product;
public String version;
public String serial;
private void clear() {
usb_version = manufacturer = product = version = serial = null;
}
@Override
public String toString() {
return String.format("UsbDevice:usb_version=%s,manufacturer=%s,product=%s,version=%s,serial=%s", usb_version != null ? usb_version : "", manufacturer != null ? manufacturer : "", product != null ? product : "", version != null ? version : "", serial != null ? serial : "");
}
}
private static final int USB_DIR_OUT = 0;
private static final int USB_DIR_IN = 0x80;
private static final int USB_TYPE_MASK = (0x03 << 5);
private static final int USB_TYPE_STANDARD = (0x00 << 5);
private static final int USB_TYPE_CLreplaced = (0x01 << 5);
private static final int USB_TYPE_VENDOR = (0x02 << 5);
private static final int USB_TYPE_RESERVED = (0x03 << 5);
private static final int USB_RECIP_MASK = 0x1f;
private static final int USB_RECIP_DEVICE = 0x00;
private static final int USB_RECIP_INTERFACE = 0x01;
private static final int USB_RECIP_ENDPOINT = 0x02;
private static final int USB_RECIP_OTHER = 0x03;
private static final int USB_RECIP_PORT = 0x04;
private static final int USB_RECIP_RPIPE = 0x05;
private static final int USB_REQ_GET_STATUS = 0x00;
private static final int USB_REQ_CLEAR_FEATURE = 0x01;
private static final int USB_REQ_SET_FEATURE = 0x03;
private static final int USB_REQ_SET_ADDRESS = 0x05;
private static final int USB_REQ_GET_DESCRIPTOR = 0x06;
private static final int USB_REQ_SET_DESCRIPTOR = 0x07;
private static final int USB_REQ_GET_CONFIGURATION = 0x08;
private static final int USB_REQ_SET_CONFIGURATION = 0x09;
private static final int USB_REQ_GET_INTERFACE = 0x0A;
private static final int USB_REQ_SET_INTERFACE = 0x0B;
private static final int USB_REQ_SYNCH_FRAME = 0x0C;
private static final int USB_REQ_SET_SEL = 0x30;
private static final int USB_REQ_SET_ISOCH_DELAY = 0x31;
private static final int USB_REQ_SET_ENCRYPTION = 0x0D;
private static final int USB_REQ_GET_ENCRYPTION = 0x0E;
private static final int USB_REQ_RPIPE_ABORT = 0x0E;
private static final int USB_REQ_SET_HANDSHAKE = 0x0F;
private static final int USB_REQ_RPIPE_RESET = 0x0F;
private static final int USB_REQ_GET_HANDSHAKE = 0x10;
private static final int USB_REQ_SET_CONNECTION = 0x11;
private static final int USB_REQ_SET_SECURITY_DATA = 0x12;
private static final int USB_REQ_GET_SECURITY_DATA = 0x13;
private static final int USB_REQ_SET_WUSB_DATA = 0x14;
private static final int USB_REQ_LOOPBACK_DATA_WRITE = 0x15;
private static final int USB_REQ_LOOPBACK_DATA_READ = 0x16;
private static final int USB_REQ_SET_INTERFACE_DS = 0x17;
// 0x10
private static final int USB_REQ_STANDARD_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE);
// 0x90
private static final int USB_REQ_STANDARD_DEVICE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE);
// 0x11
private static final int USB_REQ_STANDARD_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE);
// 0x91
private static final int USB_REQ_STANDARD_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE);
// 0x12
private static final int USB_REQ_STANDARD_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT);
// 0x92
private static final int USB_REQ_STANDARD_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT);
// 0x20
private static final int USB_REQ_CS_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLreplaced | USB_RECIP_DEVICE);
// 0xa0
private static final int USB_REQ_CS_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLreplaced | USB_RECIP_DEVICE);
// 0x21
private static final int USB_REQ_CS_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLreplaced | USB_RECIP_INTERFACE);
// 0xa1
private static final int USB_REQ_CS_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLreplaced | USB_RECIP_INTERFACE);
// 0x22
private static final int USB_REQ_CS_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLreplaced | USB_RECIP_ENDPOINT);
// 0xa2
private static final int USB_REQ_CS_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLreplaced | USB_RECIP_ENDPOINT);
// 0x40
private static final int USB_REQ_VENDER_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLreplaced | USB_RECIP_DEVICE);
// 0xc0
private static final int USB_REQ_VENDER_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLreplaced | USB_RECIP_DEVICE);
// 0x41
private static final int USB_REQ_VENDER_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLreplaced | USB_RECIP_INTERFACE);
// 0xc1
private static final int USB_REQ_VENDER_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLreplaced | USB_RECIP_INTERFACE);
// 0x42
private static final int USB_REQ_VENDER_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLreplaced | USB_RECIP_ENDPOINT);
// 0xc2
private static final int USB_REQ_VENDER_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLreplaced | USB_RECIP_ENDPOINT);
private static final int USB_DT_DEVICE = 0x01;
private static final int USB_DT_CONFIG = 0x02;
private static final int USB_DT_STRING = 0x03;
private static final int USB_DT_INTERFACE = 0x04;
private static final int USB_DT_ENDPOINT = 0x05;
private static final int USB_DT_DEVICE_QUALIFIER = 0x06;
private static final int USB_DT_OTHER_SPEED_CONFIG = 0x07;
private static final int USB_DT_INTERFACE_POWER = 0x08;
private static final int USB_DT_OTG = 0x09;
private static final int USB_DT_DEBUG = 0x0a;
private static final int USB_DT_INTERFACE_replacedOCIATION = 0x0b;
private static final int USB_DT_SECURITY = 0x0c;
private static final int USB_DT_KEY = 0x0d;
private static final int USB_DT_ENCRYPTION_TYPE = 0x0e;
private static final int USB_DT_BOS = 0x0f;
private static final int USB_DT_DEVICE_CAPABILITY = 0x10;
private static final int USB_DT_WIRELESS_ENDPOINT_COMP = 0x11;
private static final int USB_DT_WIRE_ADAPTER = 0x21;
private static final int USB_DT_RPIPE = 0x22;
private static final int USB_DT_CS_RADIO_CONTROL = 0x23;
private static final int USB_DT_PIPE_USAGE = 0x24;
private static final int USB_DT_SS_ENDPOINT_COMP = 0x30;
private static final int USB_DT_CS_DEVICE = (USB_TYPE_CLreplaced | USB_DT_DEVICE);
private static final int USB_DT_CS_CONFIG = (USB_TYPE_CLreplaced | USB_DT_CONFIG);
private static final int USB_DT_CS_STRING = (USB_TYPE_CLreplaced | USB_DT_STRING);
private static final int USB_DT_CS_INTERFACE = (USB_TYPE_CLreplaced | USB_DT_INTERFACE);
private static final int USB_DT_CS_ENDPOINT = (USB_TYPE_CLreplaced | USB_DT_ENDPOINT);
private static final int USB_DT_DEVICE_SIZE = 18;
/**
* 指定したIDのStringディスクリプタから文字列を取得する。取得できなければnull
* @param connection
* @param id
* @param languageCount
* @param languages
* @return
*/
private static String getString(final UsbDeviceConnection connection, final int id, final int languageCount, final byte[] languages) {
final byte[] work = new byte[256];
String result = null;
for (int i = 1; i <= languageCount; i++) {
int ret = connection.controlTransfer(// USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
USB_REQ_STANDARD_DEVICE_GET, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | id, languages[i], work, 256, 0);
if ((ret > 2) && (work[0] == ret) && (work[1] == USB_DT_STRING)) {
// skip first two bytes(bLength & bDescriptorType), and copy the rest to the string
try {
result = new String(work, 2, ret - 2, "UTF-16LE");
if (!"Љ".equals(result)) {
// 変なゴミが返ってくる時がある
break;
} else {
result = null;
}
} catch (final UnsupportedEncodingException e) {
// ignore
}
}
}
return result;
}
/**
* ベンダー名・製品名・バージョン・シリアルを取得する
* @param device
* @return
*/
public UsbDeviceInfo getDeviceInfo(final UsbDevice device) {
return updateDeviceInfo(mUsbManager, device, null);
}
/**
* ベンダー名・製品名・バージョン・シリアルを取得する
* #updateDeviceInfo(final UsbManager, final UsbDevice, final UsbDeviceInfo)のヘルパーメソッド
* @param context
* @param device
* @return
*/
public static UsbDeviceInfo getDeviceInfo(final Context context, final UsbDevice device) {
return updateDeviceInfo((UsbManager) context.getSystemService(Context.USB_SERVICE), device, new UsbDeviceInfo());
}
/**
* ベンダー名・製品名・バージョン・シリアルを取得する
* @param manager
* @param device
* @param _info
* @return
*/
@TargetApi(Build.VERSION_CODES.M)
public static UsbDeviceInfo updateDeviceInfo(final UsbManager manager, final UsbDevice device, final UsbDeviceInfo _info) {
final UsbDeviceInfo info = _info != null ? _info : new UsbDeviceInfo();
info.clear();
if (device != null) {
if (BuildCheck.isLollipop()) {
info.manufacturer = device.getManufacturerName();
info.product = device.getProductName();
info.serial = device.getSerialNumber();
}
if (BuildCheck.isMarshmallow()) {
info.usb_version = device.getVersion();
}
if ((manager != null) && manager.hasPermission(device)) {
final UsbDeviceConnection connection = manager.openDevice(device);
if (connection == null) {
return null;
}
final byte[] desc = connection.getRawDescriptors();
if (TextUtils.isEmpty(info.usb_version)) {
info.usb_version = String.format("%x.%02x", ((int) desc[3] & 0xff), ((int) desc[2] & 0xff));
}
if (TextUtils.isEmpty(info.version)) {
info.version = String.format("%x.%02x", ((int) desc[13] & 0xff), ((int) desc[12] & 0xff));
}
if (TextUtils.isEmpty(info.serial)) {
info.serial = connection.getSerial();
}
final byte[] languages = new byte[256];
int languageCount = 0;
// controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
try {
int result = connection.controlTransfer(// USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
USB_REQ_STANDARD_DEVICE_GET, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0, 0, languages, 256, 0);
if (result > 0) {
languageCount = (result - 2) / 2;
}
if (languageCount > 0) {
if (TextUtils.isEmpty(info.manufacturer)) {
info.manufacturer = getString(connection, desc[14], languageCount, languages);
}
if (TextUtils.isEmpty(info.product)) {
info.product = getString(connection, desc[15], languageCount, languages);
}
if (TextUtils.isEmpty(info.serial)) {
info.serial = getString(connection, desc[16], languageCount, languages);
}
}
} finally {
connection.close();
}
}
if (TextUtils.isEmpty(info.manufacturer)) {
info.manufacturer = USBVendorId.vendorName(device.getVendorId());
}
if (TextUtils.isEmpty(info.manufacturer)) {
info.manufacturer = String.format("%04x", device.getVendorId());
}
if (TextUtils.isEmpty(info.product)) {
info.product = String.format("%04x", device.getProductId());
}
}
return info;
}
/**
* control clreplaced
* never reuse the instance when it closed
*/
public static final clreplaced UsbControlBlock implements Cloneable {
private final WeakReference<USBMonitor> mWeakMonitor;
private final WeakReference<UsbDevice> mWeakDevice;
protected UsbDeviceConnection mConnection;
protected final UsbDeviceInfo mInfo;
private final int mBusNum;
private final int mDevNum;
private final SparseArray<SparseArray<UsbInterface>> mInterfaces = new SparseArray<SparseArray<UsbInterface>>();
/**
* this clreplaced needs permission to access USB device before constructing
* @param monitor
* @param device
*/
private UsbControlBlock(final USBMonitor monitor, final UsbDevice device) {
if (DEBUG)
Log.i(TAG, "UsbControlBlock:constructor");
mWeakMonitor = new WeakReference<USBMonitor>(monitor);
mWeakDevice = new WeakReference<UsbDevice>(device);
mConnection = monitor.mUsbManager.openDevice(device);
mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
final String name = device.getDeviceName();
final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null;
int busnum = 0;
int devnum = 0;
if (v != null) {
busnum = Integer.parseInt(v[v.length - 2]);
devnum = Integer.parseInt(v[v.length - 1]);
}
mBusNum = busnum;
mDevNum = devnum;
// if (DEBUG) {
if (mConnection != null) {
final int desc = mConnection.getFileDescriptor();
final byte[] rawDesc = mConnection.getRawDescriptors();
Log.i(TAG, String.format(Locale.US, "name=%s,desc=%d,busnum=%d,devnum=%d,rawDesc=", name, desc, busnum, devnum) + rawDesc);
} else {
Log.e(TAG, "could not connect to device " + name);
}
// }
}
/**
* copy constructor
* @param src
* @throws IllegalStateException
*/
private UsbControlBlock(final UsbControlBlock src) throws IllegalStateException {
final USBMonitor monitor = src.getUSBMonitor();
final UsbDevice device = src.getDevice();
if (device == null) {
throw new IllegalStateException("device may already be removed");
}
mConnection = monitor.mUsbManager.openDevice(device);
if (mConnection == null) {
throw new IllegalStateException("device may already be removed or have no permission");
}
mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
mWeakMonitor = new WeakReference<USBMonitor>(monitor);
mWeakDevice = new WeakReference<UsbDevice>(device);
mBusNum = src.mBusNum;
mDevNum = src.mDevNum;
// FIXME USBMonitor.mCtrlBlocksに追加する(今はHashMapなので追加すると置き換わってしまうのでだめ, ListかHashMapにListをぶら下げる?)
}
/**
* duplicate by clone
* need permission
* USBMonitor never handle cloned UsbControlBlock, you should release it after using it.
* @return
* @throws CloneNotSupportedException
*/
@Override
public UsbControlBlock clone() throws CloneNotSupportedException {
final UsbControlBlock ctrlblock;
try {
ctrlblock = new UsbControlBlock(this);
} catch (final IllegalStateException e) {
throw new CloneNotSupportedException(e.getMessage());
}
return ctrlblock;
}
public USBMonitor getUSBMonitor() {
return mWeakMonitor.get();
}
public final UsbDevice getDevice() {
return mWeakDevice.get();
}
/**
* get device name
* @return
*/
public String getDeviceName() {
final UsbDevice device = mWeakDevice.get();
return device != null ? device.getDeviceName() : "";
}
/**
* get device id
* @return
*/
public int getDeviceId() {
final UsbDevice device = mWeakDevice.get();
return device != null ? device.getDeviceId() : 0;
}
/**
* get device key string
* @return same value if the devices has same vendor id, product id, device clreplaced, device subclreplaced and device protocol
*/
public String getDeviceKeyName() {
return USBMonitor.getDeviceKeyName(mWeakDevice.get());
}
/**
* get device key string
* @param useNewAPI if true, try to use serial number
* @return
* @throws IllegalStateException
*/
public String getDeviceKeyName(final boolean useNewAPI) throws IllegalStateException {
if (useNewAPI)
checkConnection();
return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, useNewAPI);
}
/**
* get device key
* @return
* @throws IllegalStateException
*/
public int getDeviceKey() throws IllegalStateException {
checkConnection();
return USBMonitor.getDeviceKey(mWeakDevice.get());
}
/**
* get device key
* @param useNewAPI if true, try to use serial number
* @return
* @throws IllegalStateException
*/
public int getDeviceKey(final boolean useNewAPI) throws IllegalStateException {
if (useNewAPI)
checkConnection();
return USBMonitor.getDeviceKey(mWeakDevice.get(), mInfo.serial, useNewAPI);
}
/**
* get device key string
* if device has serial number, use it
* @return
*/
public String getDeviceKeyNameWithSerial() {
return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, false);
}
/**
* get device key
* if device has serial number, use it
* @return
*/
public int getDeviceKeyWithSerial() {
return getDeviceKeyNameWithSerial().hashCode();
}
/**
* get UsbDeviceConnection
* @return
*/
public synchronized UsbDeviceConnection getConnection() {
return mConnection;
}
/**
* get file descriptor to access USB device
* @return
* @throws IllegalStateException
*/
public synchronized int getFileDescriptor() throws IllegalStateException {
checkConnection();
return mConnection.getFileDescriptor();
}
/**
* get raw descriptor for the USB device
* @return
* @throws IllegalStateException
*/
public synchronized byte[] getRawDescriptors() throws IllegalStateException {
checkConnection();
return mConnection.getRawDescriptors();
}
/**
* get vendor id
* @return
*/
public int getVenderId() {
final UsbDevice device = mWeakDevice.get();
return device != null ? device.getVendorId() : 0;
}
/**
* get product id
* @return
*/
public int getProductId() {
final UsbDevice device = mWeakDevice.get();
return device != null ? device.getProductId() : 0;
}
/**
* get version string of USB
* @return
*/
public String getUsbVersion() {
return mInfo.usb_version;
}
/**
* get manufacture
* @return
*/
public String getManufacture() {
return mInfo.manufacturer;
}
/**
* get product name
* @return
*/
public String getProductName() {
return mInfo.product;
}
/**
* get version
* @return
*/
public String getVersion() {
return mInfo.version;
}
/**
* get serial number
* @return
*/
public String getSerial() {
return mInfo.serial;
}
public int getBusNum() {
return mBusNum;
}
public int getDevNum() {
return mDevNum;
}
/**
* get interface
* @param interface_id
* @throws IllegalStateException
*/
public synchronized UsbInterface getInterface(final int interface_id) throws IllegalStateException {
return getInterface(interface_id, 0);
}
/**
* get interface
* @param interface_id
* @param altsetting
* @return
* @throws IllegalStateException
*/
public synchronized UsbInterface getInterface(final int interface_id, final int altsetting) throws IllegalStateException {
checkConnection();
SparseArray<UsbInterface> intfs = mInterfaces.get(interface_id);
if (intfs == null) {
intfs = new SparseArray<UsbInterface>();
mInterfaces.put(interface_id, intfs);
}
UsbInterface intf = intfs.get(altsetting);
if (intf == null) {
final UsbDevice device = mWeakDevice.get();
final int n = device.getInterfaceCount();
for (int i = 0; i < n; i++) {
final UsbInterface temp = device.getInterface(i);
if ((temp.getId() == interface_id) && (temp.getAlternateSetting() == altsetting)) {
intf = temp;
break;
}
}
if (intf != null) {
intfs.append(altsetting, intf);
}
}
return intf;
}
/**
* open specific interface
* @param intf
*/
public synchronized void claimInterface(final UsbInterface intf) {
claimInterface(intf, true);
}
public synchronized void claimInterface(final UsbInterface intf, final boolean force) {
checkConnection();
mConnection.claimInterface(intf, force);
}
/**
* close interface
* @param intf
* @throws IllegalStateException
*/
public synchronized void releaseInterface(final UsbInterface intf) throws IllegalStateException {
checkConnection();
final SparseArray<UsbInterface> intfs = mInterfaces.get(intf.getId());
if (intfs != null) {
final int index = intfs.indexOfValue(intf);
intfs.removeAt(index);
if (intfs.size() == 0) {
mInterfaces.remove(intf.getId());
}
}
mConnection.releaseInterface(intf);
}
/**
* Close device
* This also close interfaces if they are opened in Java side
*/
public synchronized void close() {
if (DEBUG)
Log.i(TAG, "UsbControlBlock#close:");
if (mConnection != null) {
final int n = mInterfaces.size();
for (int i = 0; i < n; i++) {
final SparseArray<UsbInterface> intfs = mInterfaces.valueAt(i);
if (intfs != null) {
final int m = intfs.size();
for (int j = 0; j < m; j++) {
final UsbInterface intf = intfs.valueAt(j);
mConnection.releaseInterface(intf);
}
intfs.clear();
}
}
mInterfaces.clear();
mConnection.close();
mConnection = null;
final USBMonitor monitor = mWeakMonitor.get();
if (monitor != null) {
if (monitor.mOnDeviceConnectListener != null) {
monitor.mOnDeviceConnectListener.onDisconnect(mWeakDevice.get(), UsbControlBlock.this);
}
monitor.mCtrlBlocks.remove(getDevice());
}
}
}
@Override
public boolean equals(final Object o) {
if (o == null)
return false;
if (o instanceof UsbControlBlock) {
final UsbDevice device = ((UsbControlBlock) o).getDevice();
return device == null ? mWeakDevice.get() == null : device.equals(mWeakDevice.get());
} else if (o instanceof UsbDevice) {
return o.equals(mWeakDevice.get());
}
return super.equals(o);
}
// @Override
// protected void finalize() throws Throwable {
// / close();
// super.finalize();
// }
private synchronized void checkConnection() throws IllegalStateException {
if (mConnection == null) {
throw new IllegalStateException("already closed");
}
}
}
}
19
Source : StringAdapter.java
with GNU General Public License v3.0
from z-chu
with GNU General Public License v3.0
from z-chu
private SparseArray<ArrayList<String>> getSectionData(int section, PageProperty property) {
SparseArray<ArrayList<String>> pages = null;
if (map == null) {
map = new LruCache<>(3);
pages = loadPages(getPageSource(section), property.textPaint, property.visibleHeight, property.visibleWidth, property.intervalSize, property.paragraphSize);
map.put(section, pages);
mPageProperty = property;
} else {
if (mPageProperty != null && mPageProperty.equals(property)) {
pages = map.get(section);
}
if (pages == null) {
pages = loadPages(getPageSource(section), property.textPaint, property.visibleHeight, property.visibleWidth, property.intervalSize, property.paragraphSize);
map.put(section, pages);
mPageProperty = property;
}
}
return pages;
}
19
Source : StringAdapter.java
with GNU General Public License v3.0
from z-chu
with GNU General Public License v3.0
from z-chu
public static SparseArray<ArrayList<String>> loadPages(String source, Paint textPaint, int visibleHeight, int visibleWidth, int intervalSize, int paragraphSize) {
SparseArray<ArrayList<String>> pageArray = new SparseArray<>();
List<String> lines = new ArrayList<>();
if (source != null && source.length() > 0) {
String[] split = source.split("\n");
// 剩余高度
int rHeight = visibleHeight + intervalSize + paragraphSize;
for (String paragraph : split) {
boolean hasContent = false;
// 如果只有换行符,那么就不执行
if (StringUtils.isBlank(paragraph))
continue;
// 重置段落
paragraph = StringUtils.halfToFull(" " + paragraph + "\n");
paragraph = StringUtils.trimBeforeReplace(paragraph, " ");
while (paragraph.length() > 0) {
// 测量一行占用的字节数
int count = textPaint.breakText(paragraph, true, visibleWidth, null);
String subStr = paragraph.substring(0, count);
String trim = subStr.trim();
if (trim.length() > 0 && !trim.equals("\n") && !trim.equals("\r\n") && !StringUtils.isBlank(trim)) {
// 重置剩余距离
rHeight -= (textPaint.getTextSize() + intervalSize);
// 达到行数要求,创建Page
if (rHeight < 0) {
// 创建Page
pageArray.put(pageArray.size(), new ArrayList<>(lines));
// 重置Lines
lines.clear();
rHeight = visibleHeight;
continue;
}
// 将一行字节,存储到lines中
lines.add(subStr);
hasContent = true;
}
// 裁剪
paragraph = paragraph.substring(count);
}
if (lines.size() > 0 && hasContent) {
rHeight -= paragraphSize;
}
}
if (lines.size() != 0) {
pageArray.put(pageArray.size(), new ArrayList<>(lines));
// 重置Lines
lines.clear();
}
}
return pageArray;
}
19
Source : ReadAdapter.java
with GNU General Public License v3.0
from z-chu
with GNU General Public License v3.0
from z-chu
/**
* Created by Chu on 2017/8/15.
*/
public clreplaced ReadAdapter extends StringAdapter {
private SparseArray<BookSectionContent> bookArray;
private List<BookSectionItem> mBookSectionItems;
public ReadAdapter() {
bookArray = new SparseArray<>();
}
public ReadAdapter(List<BookSectionItem> bookSectionItems) {
bookArray = new SparseArray<>();
mBookSectionItems = bookSectionItems;
}
@Override
protected String getPageSource(int section) {
BookSectionContent sectionContent = bookArray.get(section);
return sectionContent != null ? bookArray.get(section).getContent() : null;
}
@Override
public int getSectionCount() {
return bookArray.size();
}
@Override
public String getSectionName(int section) {
BookSectionContent sectionContent = bookArray.get(section);
return sectionContent != null ? bookArray.get(section).getSectionName() : null;
}
@Override
public boolean hasNextSection(int currentSection) {
return bookArray.get(currentSection + 1) != null;
}
@Override
public boolean hasPreviousSection(int currentSection) {
return bookArray.get(currentSection - 1) != null;
}
public void addData(int section, BookSectionContent content) {
bookArray.put(section, content);
}
public boolean hreplacedection(int section) {
return bookArray.get(section) != null;
}
}
19
Source : DisplayUtil.java
with GNU General Public License v3.0
from ywwynm
with GNU General Public License v3.0
from ywwynm
/**
* Created by ywwynm on 2015/6/28.
* A helper clreplaced to get necessary screen information and update UI with color.
*/
public clreplaced DisplayUtil {
public static final String TAG = "DisplayUtil";
private DisplayUtil() {
}
public static float getScreenDensity(Context context) {
return context.getResources().getDisplayMetrics().density;
}
@SuppressLint("NewApi")
public static Point getDisplaySize(Context context) {
Point screen = new Point();
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (!DeviceUtil.hasLollipopApi()) {
display.getSize(screen);
} else {
// Content can overlay Navigation Bar above Lollipop.
display.getRealSize(screen);
}
return screen;
}
// Get physical screen size of phone/tablet.
public static Point getScreenSize(Context context) {
Point realScreen = new Point();
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
if (DeviceUtil.hasJellyBeanMR1Api()) {
display.getRealSize(realScreen);
} else {
try {
Method mGetRawH = Display.clreplaced.getMethod("getRawHeight");
Method mGetRawW = Display.clreplaced.getMethod("getRawWidth");
realScreen.x = (Integer) mGetRawW.invoke(display);
realScreen.y = (Integer) mGetRawH.invoke(display);
} catch (Exception e) {
display.getSize(realScreen);
Log.e(TAG, "Cannot use reflection to get real screen size. " + "Returned size may be wrong.");
}
}
return realScreen;
}
public static boolean isTablet(Context context) {
// improved on 2016/5/11~
return context.getResources().getBoolean(R.bool.isTablet);
}
public static int getStatusbarHeight(Context context) {
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
} else
return 0;
}
public static boolean hasNavigationBar(Context context) {
// improved on 2016/11/21
boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
boolean con1 = !hasMenuKey && !hasBackKey;
Resources resources = context.getResources();
int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
boolean con2 = id > 0 && resources.getBoolean(id);
boolean con3;
Point displaySize = new Point();
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
display.getSize(displaySize);
Point screenSize = getScreenSize(context);
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
con3 = displaySize.y != screenSize.y;
} else {
con3 = displaySize.x != screenSize.x;
}
return con1 || con2 || con3;
}
public static int getNavigationBarHeight(Context context) {
// improved on 2016/11/21
int res1 = 0;
Resources resources = context.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
res1 = resources.getDimensionPixelSize(resourceId);
}
int res2;
Point displaySize = new Point();
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
display.getSize(displaySize);
Point screenSize = getScreenSize(context);
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
res2 = screenSize.y - displaySize.y;
} else {
res2 = screenSize.x - displaySize.x;
}
return Math.max(res1, res2);
}
// This method has a sexy history~
// Someday if you see this code again, wish that your dream had come true
public static int getRandomColor(Context context) {
int[] colors = context.getResources().getIntArray(R.array.thing);
return colors[new Random().nextInt(colors.length)];
}
public static int getColorIndex(int color, Context context) {
int[] colors = context.getResources().getIntArray(R.array.thing);
for (int i = 0; i < colors.length; i++) {
if (colors[i] == color) {
return i;
}
}
return -1;
}
public static int getDarkColor(int color, Context context) {
int[] colorsDark = context.getResources().getIntArray(R.array.thing_dark);
int index = getColorIndex(color, context);
if (index != -1) {
return colorsDark[index];
} else
return 0;
}
public static int getLightColor(int color, Context context) {
int[] colorsLight = context.getResources().getIntArray(R.array.thing_light);
int index = getColorIndex(color, context);
if (index != -1) {
return colorsLight[index];
} else
return 0;
}
public static int getTransparentColor(int color, int alpha) {
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(alpha, red, green, blue);
}
/**
* Play drawer toggle animation(from drawer to arrow and vice versa).
* @param d the {@link DrawerArrowDrawable} object to play toggle animation.
*/
public static void playDrawerToggleAnim(final DrawerArrowDrawable d) {
float start = d.getProgress();
float end = Math.abs(start - 1);
ValueAnimator offsetAnimator = ValueAnimator.ofFloat(start, end);
offsetAnimator.setDuration(300);
offsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
offsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float progress = (Float) animation.getAnimatedValue();
d.setProgress(progress);
}
});
offsetAnimator.start();
}
/**
* Set backgroundTint to {@link View} across all targeting platform level.
* @param view the {@link View} to tint.
* @param color color used to tint.
*/
public static void tintView(View view, int color) {
final Drawable d = view.getBackground();
Drawable.ConstantState constantState = d.getConstantState();
if (constantState == null)
return;
final Drawable nd = constantState.newDrawable();
nd.setColorFilter(AppCompatDrawableManager.getPorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
view.setBackground(nd);
// Drawable wrappedDrawable = DrawableCompat.wrap(view.getBackground().mutate());
// DrawableCompat.setTint(wrappedDrawable, color);
// view.setBackground(wrappedDrawable);
// ViewCompat.setBackgroundTintList(view, ColorStateList.valueOf(color));
}
public static void expandLayoutToStatusBarAboveLollipop(Activity activity) {
if (DeviceUtil.hasLollipopApi()) {
View decor = activity.getWindow().getDecorView();
decor.setSystemUiVisibility(decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
}
public static void expandLayoutToFullscreenAboveLollipop(Activity activity) {
if (DeviceUtil.hasLollipopApi()) {
View decor = activity.getWindow().getDecorView();
decor.setSystemUiVisibility(decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
}
public static void expandStatusBarViewAboveKitkat(View statusBar) {
if (DeviceUtil.hasKitKatApi()) {
final int height = getStatusbarHeight(statusBar.getContext());
ViewGroup.LayoutParams vlp = statusBar.getLayoutParams();
vlp.height = height;
statusBar.requestLayout();
}
}
public static void darkStatusBar(Activity activity) {
Window window = activity.getWindow();
if (DeviceUtil.hasMarshmallowApi()) {
View decor = window.getDecorView();
decor.setSystemUiVisibility(decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
darkStatusBarForMIUI(window);
darkStatusBarForFlyme(window);
}
public static void cancelDarkStatusBar(Activity activity) {
Window window = activity.getWindow();
if (DeviceUtil.hasMarshmallowApi()) {
View decor = window.getDecorView();
decor.setSystemUiVisibility(decor.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
private static void darkStatusBarForMIUI(Window window) {
Clreplaced<? extends Window> clazz = window.getClreplaced();
try {
Clreplaced<?> layoutParams = Clreplaced.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.clreplaced, int.clreplaced);
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
} catch (Exception ignored) {
}
}
private static void darkStatusBarForFlyme(Window window) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.clreplaced.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.clreplaced.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
value |= bit;
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
} catch (Exception ignored) {
}
}
public static boolean isInMultiWindow(Activity activity) {
if (DeviceUtil.hasNougatApi()) {
return activity.isInMultiWindowMode();
}
return false;
}
/**
* Set color of handlers appearing when user is selecting content of {@link EditText}.
* @param editText handlers of which {@link EditText} should be set to {@param color}.
* @param color color to set for handlers.
*/
public static void setSelectionHandlersColor(EditText editText, int color) {
try {
final Clreplaced<?> cTextView = TextView.clreplaced;
final Field fhlRes = cTextView.getDeclaredField("mTextSelectHandleLeftRes");
final Field fhrRes = cTextView.getDeclaredField("mTextSelectHandleRightRes");
final Field fhcRes = cTextView.getDeclaredField("mTextSelectHandleRes");
fhlRes.setAccessible(true);
fhrRes.setAccessible(true);
fhcRes.setAccessible(true);
int hlRes = fhlRes.getInt(editText);
int hrRes = fhrRes.getInt(editText);
int hcRes = fhcRes.getInt(editText);
final Field fEditor = TextView.clreplaced.getDeclaredField("mEditor");
fEditor.setAccessible(true);
final Object editor = fEditor.get(editText);
final Clreplaced<?> cEditor = editor.getClreplaced();
final Field fSelectHandleL = cEditor.getDeclaredField("mSelectHandleLeft");
final Field fSelectHandleR = cEditor.getDeclaredField("mSelectHandleRight");
final Field fSelectHandleC = cEditor.getDeclaredField("mSelectHandleCenter");
fSelectHandleL.setAccessible(true);
fSelectHandleR.setAccessible(true);
fSelectHandleC.setAccessible(true);
Drawable selectHandleL = ContextCompat.getDrawable(editText.getContext(), hlRes);
Drawable selectHandleR = ContextCompat.getDrawable(editText.getContext(), hrRes);
Drawable selectHandleC = ContextCompat.getDrawable(editText.getContext(), hcRes);
selectHandleL.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
selectHandleR.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
selectHandleC.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
fSelectHandleL.set(editor, selectHandleL);
fSelectHandleR.set(editor, selectHandleR);
fSelectHandleC.set(editor, selectHandleC);
} catch (Exception ignored) {
}
}
public static int getThingCardWidth(Context context) {
int span = 2;
Resources res = context.getResources();
float density = res.getDisplayMetrics().density;
if (res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
span++;
}
if (isTablet(context)) {
span++;
}
int basePadding;
if (!DeviceUtil.hasLollipopApi()) {
basePadding = (int) (density * 4);
} else {
basePadding = (int) (density * 6);
}
return (res.getDisplayMetrics().widthPixels - basePadding * 2 * (span + 1)) / span;
}
private static SparseArray<StateListDrawable> sSldMap;
// private static HashMap<Integer, StateListDrawable> sSldMap;
public static void setRippleColorForCardView(CardView cardView, int color) {
if (DeviceUtil.hasLollipopApi()) {
RippleDrawable rp = (RippleDrawable) cardView.getForeground();
rp.setColor(ColorStateList.valueOf(color));
} else {
if (sSldMap == null) {
sSldMap = new SparseArray<>();
}
StateListDrawable sld = sSldMap.get(color);
if (sld == null) {
sld = new StateListDrawable();
sld.addState(new int[] { android.R.attr.state_pressed }, new ColorDrawable(color));
sld.addState(new int[] { -android.R.attr.state_pressed }, new ColorDrawable(Color.TRANSPARENT));
sSldMap.put(color, sld);
}
cardView.setForeground(sld);
}
}
public static void setSeekBarColor(SeekBar seekBar, int color) {
if (DeviceUtil.hasLollipopApi()) {
seekBar.setProgressTintList(ColorStateList.valueOf(color));
} else {
seekBar.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
seekBar.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
public static void setButtonColor(Button button, int color) {
button.getBackground().setColorFilter(color, PorterDuff.Mode.MULTIPLY);
}
public static void setCheckBoxColor(AppCompatCheckBox checkBox, int accentColor) {
setCheckBoxColor(checkBox, ContextCompat.getColor(checkBox.getContext(), R.color.black_54), accentColor);
}
public static void setCheckBoxColor(AppCompatCheckBox checkBox, int uncheckedColor, int checkedColor) {
ColorStateList colorStateList = new ColorStateList(new int[][] { // unchecked
new int[] { -android.R.attr.state_checked }, // checked
new int[] { android.R.attr.state_checked } }, new int[] { uncheckedColor, checkedColor });
checkBox.setSupportButtonTintList(colorStateList);
}
public static int getCursorY(EditText et) {
int pos = et.getSelectionStart();
Layout layout = et.getLayout();
int line = layout.getLineForOffset(pos);
int baseline = layout.getLineBaseline(line);
int ascent = layout.getLineAscent(line);
return baseline + ascent;
}
}
19
Source : EverythingDoneBaseActivity.java
with GNU General Public License v3.0
from ywwynm
with GNU General Public License v3.0
from ywwynm
/**
* Created by ywwynm on 2015/6/4.
* A base Activity clreplaced to reduce same codes in different subclreplacedes.
*/
public abstract clreplaced EverythingDoneBaseActivity extends AppCompatActivity {
public static final String TAG = "EverythingDoneBaseActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Call this before setContentView so that NavigationView can update its language correctly.
LocaleUtil.changeLanguage();
beforeSetContentView();
setContentView(getLayoutResource());
beforeInit();
init();
}
@SuppressWarnings("unchecked")
protected final <T extends View> T f(@IdRes int id) {
return (T) findViewById(id);
}
@SuppressWarnings("unchecked")
protected final <T extends View> T f(View v, @IdRes int id) {
return (T) v.findViewById(id);
}
protected void beforeSetContentView() {
}
@LayoutRes
protected abstract int getLayoutResource();
protected void beforeInit() {
}
protected void init() {
initMembers();
findViews();
initUI();
setActionbar();
setEvents();
}
protected abstract void initMembers();
protected abstract void findViews();
protected abstract void initUI();
protected abstract void setActionbar();
protected abstract void setEvents();
private SparseArray<PermissionCallback> mCallbacks;
public void doWithPermissionChecked(@NonNull PermissionCallback permissionCallback, int requestCode, String... permissions) {
if (DeviceUtil.hasMarshmallowApi()) {
if (mCallbacks == null) {
mCallbacks = new SparseArray<>();
}
for (String permission : permissions) {
int pg = ContextCompat.checkSelfPermission(this, permission);
if (pg != PackageManager.PERMISSION_GRANTED) {
mCallbacks.put(requestCode, permissionCallback);
ActivityCompat.requestPermissions(this, permissions, requestCode);
return;
}
}
}
permissionCallback.onGranted();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionCallback callback = mCallbacks.get(requestCode);
if (callback != null) {
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
callback.onDenied();
return;
}
}
callback.onGranted();
}
}
}
19
Source : LoadingRendererFactory.java
with Apache License 2.0
from Yuphee
with Apache License 2.0
from Yuphee
public final clreplaced LoadingRendererFactory {
private static final SparseArray<Clreplaced<? extends LoadingRenderer>> LOADING_RENDERERS = new SparseArray<>();
static {
// circle rotate
// LOADING_RENDERERS.put(0, MaterialLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(1, LevelLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(2, WhorlLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(3, GearLoadingRenderer.clreplaced);
// //circle jump
// LOADING_RENDERERS.put(4, SwapLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(5, GuardLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(6, DanceLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(7, CollisionLoadingRenderer.clreplaced);
// //scenery
// LOADING_RENDERERS.put(8, DayNightLoadingRenderer.clreplaced);
LOADING_RENDERERS.put(9, ElectricFanLoadingRenderer.clreplaced);
// animal
// LOADING_RENDERERS.put(10, FishLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(11, GhostsEyeLoadingRenderer.clreplaced);
// //goods
// LOADING_RENDERERS.put(12, BalloonLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(13, WaterBottleLoadingRenderer.clreplaced);
// //shape change
// LOADING_RENDERERS.put(14, CircleBroodLoadingRenderer.clreplaced);
// LOADING_RENDERERS.put(15, CoolWaitLoadingRenderer.clreplaced);
}
private LoadingRendererFactory() {
}
public static LoadingRenderer createLoadingRenderer(Context context, int loadingRendererId) throws Exception {
Clreplaced<?> loadingRendererClazz = LOADING_RENDERERS.get(loadingRendererId);
Constructor<?>[] constructors = loadingRendererClazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
Clreplaced<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes != null && parameterTypes.length == 1 && parameterTypes[0].equals(Context.clreplaced)) {
constructor.setAccessible(true);
return (LoadingRenderer) constructor.newInstance(context);
}
}
throw new InstantiationException();
}
}
19
Source : ExternalViewerButton.java
with Apache License 2.0
from Yuloran
with Apache License 2.0
from Yuloran
/**
* This is a custom image button that launches an external viewer. It changes its
* image resource based on the current viewer type (photosphere, refocus, etc).
* Also, it tracks whether it is shown by tracking the visibility change of all
* its ancestors, and keep the visibility of the clings that are registered to the
* button in sync.
*/
public clreplaced ExternalViewerButton extends ImageButton {
private static final Log.Tag TAG = new Log.Tag("ExtViewerButton");
private int mState = CameraAppUI.BottomPanel.VIEWER_NONE;
private final SparseArray<Cling> mClingMap;
public ExternalViewerButton(Context context, AttributeSet attrs) {
super(context, attrs);
mClingMap = new SparseArray<Cling>();
updateClingVisibility();
}
@Override
protected void onVisibilityChanged(View v, int visibility) {
super.onVisibilityChanged(v, visibility);
if (mClingMap == null) {
return;
}
updateClingVisibility();
}
/**
* Sets cling of the given viewer type for external viewer button.
*/
public void setClingForViewer(int viewerType, Cling cling) {
if (cling == null) {
Log.w(TAG, "Cannot set a null cling for viewer");
return;
}
mClingMap.put(viewerType, cling);
cling.setReferenceView(this);
}
/**
* Clears cling of the given viewer type for external viewer button.
*/
public void clearClingForViewer(int viewerType) {
Cling cling = mClingMap.get(viewerType);
if (cling == null) {
Log.w(TAG, "Cling does not exist for the given viewer type: " + viewerType);
}
cling.setReferenceView(null);
mClingMap.remove(viewerType);
}
/**
* Returns a cling for the specified viewer type.
*/
public Cling getClingForViewer(int viewerType) {
return mClingMap.get(viewerType);
}
/**
* Sets the current state of the button, which affects the visibility and image
* resource of the button.
*/
public void setState(int state) {
mState = state;
int newVisibility;
if (state == CameraAppUI.BottomPanel.VIEWER_NONE) {
newVisibility = View.GONE;
} else {
setImageResource(getViewButtonResource(state));
newVisibility = View.VISIBLE;
}
if (newVisibility != getVisibility()) {
setVisibility(newVisibility);
} else if (newVisibility == View.VISIBLE) {
// If visibility has changed, cling visibility was updated already,
// so only need to update it when visibility has not changed.
updateClingVisibility();
}
}
/**
* Sets all the clings to be invisible.
*/
public void hideClings() {
for (int i = 0; i < mClingMap.size(); i++) {
mClingMap.valueAt(i).setVisibility(View.INVISIBLE);
}
}
/**
* Gets the image resource for a specific state.
*/
private int getViewButtonResource(int state) {
switch(state) {
case CameraAppUI.BottomPanel.VIEWER_REFOCUS:
return R.drawable.ic_refocus_normal;
case CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE:
return R.drawable.ic_view_photosphere;
default:
return R.drawable.ic_control_play;
}
}
/**
* Updates the visibility of clings based on whether the button is currently
* shown.
*/
public void updateClingVisibility() {
hideClings();
if (isShown()) {
Cling cling = mClingMap.get(mState);
if (cling != null) {
cling.adjustPosition();
cling.setVisibility(View.VISIBLE);
}
}
}
}
19
Source : SettingsUtil.java
with Apache License 2.0
from Yuloran
with Apache License 2.0
from Yuloran
/**
* Utility functions around camera settings.
*/
public clreplaced SettingsUtil {
/**
* Returns the maximum video recording duration (in milliseconds).
*/
public static int getMaxVideoDuration(Context context) {
// in milliseconds, 0 means unlimited.
int duration = 0;
try {
duration = context.getResources().getInteger(R.integer.max_video_recording_length);
} catch (Resources.NotFoundException ex) {
}
return duration;
}
/**
* The selected Camera sizes.
*/
public static clreplaced SelectedPictureSizes {
public Size large;
public Size medium;
public Size small;
/**
* This takes a string preference describing the desired resolution and
* returns the camera size it represents. <br/>
* It supports historical values of SIZE_LARGE, SIZE_MEDIUM, and
* SIZE_SMALL as well as resolutions separated by an x i.e. "1024x576" <br/>
* If it fails to parse the string, it will return the old SIZE_LARGE
* value.
*
* @param sizeSetting the preference string to convert to a size
* @param supportedSizes all possible camera sizes that are supported
* @return the size that this setting represents
*/
public Size getFromSetting(String sizeSetting, List<Size> supportedSizes) {
if (SIZE_LARGE.equals(sizeSetting)) {
return large;
} else if (SIZE_MEDIUM.equals(sizeSetting)) {
return medium;
} else if (SIZE_SMALL.equals(sizeSetting)) {
return small;
} else if (sizeSetting != null && sizeSetting.split("x").length == 2) {
Size desiredSize = sizeFromSettingString(sizeSetting);
if (supportedSizes.contains(desiredSize)) {
return desiredSize;
}
}
return large;
}
@Override
public String toString() {
return "SelectedPictureSizes: " + large + ", " + medium + ", " + small;
}
}
/**
* The selected {@link CamcorderProfile} qualities.
*/
public static clreplaced SelectedVideoQualities {
public int large = -1;
public int medium = -1;
public int small = -1;
public int getFromSetting(String sizeSetting) {
// Sanitize the value to be either small, medium or large. Default
// to the latter.
if (!SIZE_SMALL.equals(sizeSetting) && !SIZE_MEDIUM.equals(sizeSetting)) {
sizeSetting = SIZE_LARGE;
}
if (SIZE_LARGE.equals(sizeSetting)) {
return large;
} else if (SIZE_MEDIUM.equals(sizeSetting)) {
return medium;
} else {
return small;
}
}
}
private static final Log.Tag TAG = new Log.Tag("SettingsUtil");
/**
* Enable debug output.
*/
private static final boolean DEBUG = false;
private static final String SIZE_LARGE = "large";
private static final String SIZE_MEDIUM = "medium";
private static final String SIZE_SMALL = "small";
/**
* The ideal "medium" picture size is 50% of "large".
*/
private static final float MEDIUM_RELATIVE_PICTURE_SIZE = 0.5f;
/**
* The ideal "small" picture size is 25% of "large".
*/
private static final float SMALL_RELATIVE_PICTURE_SIZE = 0.25f;
/**
* Video qualities sorted by size.
*/
public static int[] sVideoQualities = new int[] { CamcorderProfile.QUALITY_2160P, CamcorderProfile.QUALITY_1080P, CamcorderProfile.QUALITY_720P, CamcorderProfile.QUALITY_480P, CamcorderProfile.QUALITY_CIF, CamcorderProfile.QUALITY_QVGA, CamcorderProfile.QUALITY_QCIF };
public static SparseArray<SelectedPictureSizes> sCachedSelectedPictureSizes = new SparseArray<SelectedPictureSizes>(2);
public static SparseArray<SelectedVideoQualities> sCachedSelectedVideoQualities = new SparseArray<SelectedVideoQualities>(2);
/**
* Based on the selected size, this method returns the matching concrete
* resolution.
*
* @param sizeSetting The setting selected by the user. One of "large",
* "medium, "small".
* @param supported The list of supported resolutions.
* @param cameraId This is used for caching the results for finding the
* different sizes.
*/
public static Size getPhotoSize(String sizeSetting, List<Size> supported, int cameraId) {
if (ResolutionUtil.NEXUS_5_LARGE_16_BY_9.equals(sizeSetting)) {
return ResolutionUtil.NEXUS_5_LARGE_16_BY_9_SIZE;
}
Size selectedSize = getCameraPictureSize(sizeSetting, supported, cameraId);
return selectedSize;
}
/**
* Based on the selected size (large, medium or small), and the list of
* supported resolutions, this method selects and returns the best matching
* picture size.
*
* @param sizeSetting The setting selected by the user. One of "large",
* "medium, "small".
* @param supported The list of supported resolutions.
* @param cameraId This is used for caching the results for finding the
* different sizes.
* @return The selected size.
*/
private static Size getCameraPictureSize(String sizeSetting, List<Size> supported, int cameraId) {
return getSelectedCameraPictureSizes(supported, cameraId).getFromSetting(sizeSetting, supported);
}
/**
* Based on the list of supported resolutions, this method selects the ones
* that shall be selected for being 'large', 'medium' and 'small'.
*
* @return It's guaranteed that all three sizes are filled. If less than
* three sizes are supported, the selected sizes might contain
* duplicates.
*/
static SelectedPictureSizes getSelectedCameraPictureSizes(List<Size> supported, int cameraId) {
List<Size> supportedCopy = new LinkedList<Size>(supported);
if (sCachedSelectedPictureSizes.get(cameraId) != null) {
return sCachedSelectedPictureSizes.get(cameraId);
}
if (supportedCopy == null) {
return null;
}
SelectedPictureSizes selectedSizes = new SelectedPictureSizes();
// Sort supported sizes by total pixel count, descending.
Collections.sort(supportedCopy, new Comparator<Size>() {
@Override
public int compare(Size lhs, Size rhs) {
int leftArea = lhs.width() * lhs.height();
int rightArea = rhs.width() * rhs.height();
return rightArea - leftArea;
}
});
if (DEBUG) {
Log.d(TAG, "Supported Sizes:");
for (Size size : supportedCopy) {
Log.d(TAG, " --> " + size.width() + "x" + size.height() + " " + ((size.width() * size.height()) / 1000000f) + " - " + (size.width() / (float) size.height()));
}
}
// Large size is always the size with the most pixels reported.
selectedSizes.large = supportedCopy.remove(0);
// If possible we want to find medium and small sizes with the same
// aspect ratio as 'large'.
final float targetAspectRatio = selectedSizes.large.width() / (float) selectedSizes.large.height();
// Create a list of sizes with the same aspect ratio as "large" which we
// will search in primarily.
ArrayList<Size> aspectRatioMatches = new ArrayList<Size>();
for (Size size : supportedCopy) {
float aspectRatio = size.width() / (float) size.height();
// Allow for small rounding errors in aspect ratio.
if (Math.abs(aspectRatio - targetAspectRatio) < 0.01) {
aspectRatioMatches.add(size);
}
}
// If we have at least two more resolutions that match the 'large'
// aspect ratio, use that list to find small and medium sizes. If not,
// use the full list with any aspect ratio.
final List<Size> searchList = (aspectRatioMatches.size() >= 2) ? aspectRatioMatches : supportedCopy;
// Edge cases: If there are no further supported resolutions, use the
// only one we have.
// If there is only one remaining, use it for small and medium. If there
// are two, use the two for small and medium.
// These edge cases should never happen on a real device, but might
// happen on test devices and emulators.
if (searchList.isEmpty()) {
Log.w(TAG, "Only one supported resolution.");
selectedSizes.medium = selectedSizes.large;
selectedSizes.small = selectedSizes.large;
} else if (searchList.size() == 1) {
Log.w(TAG, "Only two supported resolutions.");
selectedSizes.medium = searchList.get(0);
selectedSizes.small = searchList.get(0);
} else if (searchList.size() == 2) {
Log.w(TAG, "Exactly three supported resolutions.");
selectedSizes.medium = searchList.get(0);
selectedSizes.small = searchList.get(1);
} else {
// Based on the large pixel count, determine the target pixel count
// for medium and small.
final int largePixelCount = selectedSizes.large.width() * selectedSizes.large.height();
final int mediumTargetPixelCount = (int) (largePixelCount * MEDIUM_RELATIVE_PICTURE_SIZE);
final int smallTargetPixelCount = (int) (largePixelCount * SMALL_RELATIVE_PICTURE_SIZE);
int mediumSizeIndex = findClosestSize(searchList, mediumTargetPixelCount);
int smallSizeIndex = findClosestSize(searchList, smallTargetPixelCount);
// If the selected sizes are the same, move the small size one down
// or
// the medium size one up.
if (searchList.get(mediumSizeIndex).equals(searchList.get(smallSizeIndex))) {
if (smallSizeIndex < (searchList.size() - 1)) {
smallSizeIndex += 1;
} else {
mediumSizeIndex -= 1;
}
}
selectedSizes.medium = searchList.get(mediumSizeIndex);
selectedSizes.small = searchList.get(smallSizeIndex);
}
sCachedSelectedPictureSizes.put(cameraId, selectedSizes);
return selectedSizes;
}
/**
* Determines the video quality for large/medium/small for the given camera.
* Returns the one matching the given setting. Defaults to 'large' of the
* qualitySetting does not match either large. medium or small.
*
* @param qualitySetting One of 'large', 'medium', 'small'.
* @param cameraId The ID of the camera for which to get the quality
* setting.
* @return The CamcorderProfile quality setting.
*/
public static int getVideoQuality(String qualitySetting, int cameraId) {
return getSelectedVideoQualities(cameraId).getFromSetting(qualitySetting);
}
static SelectedVideoQualities getSelectedVideoQualities(int cameraId) {
if (sCachedSelectedVideoQualities.get(cameraId) != null) {
return sCachedSelectedVideoQualities.get(cameraId);
}
// Go through the sizes in descending order, see if they are supported,
// and set large/medium/small accordingly.
// If no quality is supported at all, the first call to
// getNextSupportedQuality will throw an exception.
// If only one quality is supported, then all three selected qualities
// will be the same.
int largeIndex = getNextSupportedVideoQualityIndex(cameraId, -1);
int mediumIndex = getNextSupportedVideoQualityIndex(cameraId, largeIndex);
int smallIndex = getNextSupportedVideoQualityIndex(cameraId, mediumIndex);
SelectedVideoQualities selectedQualities = new SelectedVideoQualities();
selectedQualities.large = sVideoQualities[largeIndex];
selectedQualities.medium = sVideoQualities[mediumIndex];
selectedQualities.small = sVideoQualities[smallIndex];
sCachedSelectedVideoQualities.put(cameraId, selectedQualities);
return selectedQualities;
}
/**
* Starting from 'start' this method returns the next supported video
* quality.
*/
private static int getNextSupportedVideoQualityIndex(int cameraId, int start) {
for (int i = start + 1; i < sVideoQualities.length; ++i) {
if (isVideoQualitySupported(sVideoQualities[i]) && CamcorderProfile.hasProfile(cameraId, sVideoQualities[i])) {
// We found a new supported quality.
return i;
}
}
// Failed to find another supported quality.
if (start < 0 || start >= sVideoQualities.length) {
// This means we couldn't find any supported quality.
throw new IllegalArgumentException("Could not find supported video qualities.");
}
// We previously found a larger supported size. In this edge case, just
// return the same index as the previous size.
return start;
}
/**
* @return Whether the given {@link CamcorderProfile} is supported on the
* current device/OS version.
*/
private static boolean isVideoQualitySupported(int videoQuality) {
// 4k is only supported on L or higher but some devices falsely report
// to have support for it on K, see b/18172081.
if (!ApiHelper.isLOrHigher() && videoQuality == CamcorderProfile.QUALITY_2160P) {
return false;
}
return true;
}
/**
* Returns the index of the size within the given list that is closest to
* the given target pixel count.
*/
private static int findClosestSize(List<Size> sortedSizes, int targetPixelCount) {
int closestMatchIndex = 0;
int closestMatchPixelCountDiff = Integer.MAX_VALUE;
for (int i = 0; i < sortedSizes.size(); ++i) {
Size size = sortedSizes.get(i);
int pixelCountDiff = Math.abs((size.width() * size.height()) - targetPixelCount);
if (pixelCountDiff < closestMatchPixelCountDiff) {
closestMatchIndex = i;
closestMatchPixelCountDiff = pixelCountDiff;
}
}
return closestMatchIndex;
}
private static final String SIZE_SETTING_STRING_DIMENSION_DELIMITER = "x";
/**
* This is used to serialize a size to a string for storage in settings
*
* @param size The size to serialize.
* @return the string to be saved in preferences
*/
public static String sizeToSettingString(Size size) {
return size.width() + SIZE_SETTING_STRING_DIMENSION_DELIMITER + size.height();
}
/**
* This parses a setting string and returns the representative size.
*
* @param sizeSettingString The string that stored in settings to represent a size.
* @return the represented Size.
*/
public static Size sizeFromSettingString(String sizeSettingString) {
if (sizeSettingString == null) {
return null;
}
String[] parts = sizeSettingString.split(SIZE_SETTING_STRING_DIMENSION_DELIMITER);
if (parts.length != 2) {
return null;
}
try {
int width = Integer.parseInt(parts[0]);
int height = Integer.parseInt(parts[1]);
return new Size(width, height);
} catch (NumberFormatException ex) {
return null;
}
}
/**
* Updates an AlertDialog.Builder to explain what it means to enable
* location on captures.
*/
public static AlertDialog.Builder getFirstTimeLocationAlertBuilder(AlertDialog.Builder builder, Callback<Boolean> callback) {
if (callback == null) {
return null;
}
getLocationAlertBuilder(builder, callback).setMessage(R.string.remember_location_prompt);
return builder;
}
/**
* Updates an AlertDialog.Builder for choosing whether to include location
* on captures.
*/
public static AlertDialog.Builder getLocationAlertBuilder(AlertDialog.Builder builder, final Callback<Boolean> callback) {
if (callback == null) {
return null;
}
builder.setreplacedle(R.string.remember_location_replacedle).setPositiveButton(R.string.remember_location_yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
callback.onCallback(true);
}
}).setNegativeButton(R.string.remember_location_no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
callback.onCallback(false);
}
});
return builder;
}
/**
* Gets the first (lowest-indexed) camera matching the given criterion.
*
* @param facing Either {@link CAMERA_FACING_BACK}, {@link CAMERA_FACING_FRONT}, or some other
* implementation of {@link CameraDeviceSelector}.
* @return The ID of the first camera matching the supplied criterion, or
* -1, if no camera meeting the specification was found.
*/
public static int getCameraId(CameraDeviceInfo info, CameraDeviceSelector chooser) {
if (info == null) {
return -1;
}
int numCameras = info.getNumberOfCameras();
for (int i = 0; i < numCameras; ++i) {
CameraDeviceInfo.Characteristics props = info.getCharacteristics(i);
if (props == null) {
// Skip this device entry
continue;
}
if (chooser.useCamera(props)) {
return i;
}
}
return -1;
}
public static interface CameraDeviceSelector {
/**
* Given the static characteristics of a specific camera device, decide whether it is the
* one we will use.
*
* @param info The static characteristics of a device.
* @return Whether we're electing to use this particular device.
*/
public boolean useCamera(CameraDeviceInfo.Characteristics info);
}
public static final CameraDeviceSelector CAMERA_FACING_BACK = new CameraDeviceSelector() {
@Override
public boolean useCamera(CameraDeviceInfo.Characteristics info) {
return info.isFacingBack();
}
};
public static final CameraDeviceSelector CAMERA_FACING_FRONT = new CameraDeviceSelector() {
@Override
public boolean useCamera(CameraDeviceInfo.Characteristics info) {
return info.isFacingFront();
}
};
}
19
Source : ModuleManagerImpl.java
with Apache License 2.0
from Yuloran
with Apache License 2.0
from Yuloran
/**
* A clreplaced which implements {@link com.android.camera.app.ModuleManager}.
*/
public clreplaced ModuleManagerImpl implements ModuleManager {
private static final Log.Tag TAG = new Log.Tag("ModuleManagerImpl");
private final SparseArray<ModuleAgent> mRegisteredModuleAgents = new SparseArray<ModuleAgent>(2);
private int mDefaultModuleId = MODULE_INDEX_NONE;
public ModuleManagerImpl() {
}
@Override
public void registerModule(ModuleAgent agent) {
if (agent == null) {
throw new NullPointerException("Registering a null ModuleAgent.");
}
final int moduleId = agent.getModuleId();
if (moduleId == MODULE_INDEX_NONE) {
throw new IllegalArgumentException("ModuleManager: The module ID can not be " + "MODULE_INDEX_NONE");
}
if (mRegisteredModuleAgents.get(moduleId) != null) {
throw new IllegalArgumentException("Module ID is registered already:" + moduleId);
}
mRegisteredModuleAgents.put(moduleId, agent);
}
@Override
public boolean unregisterModule(int moduleId) {
if (mRegisteredModuleAgents.get(moduleId) == null) {
return false;
}
mRegisteredModuleAgents.delete(moduleId);
if (moduleId == mDefaultModuleId) {
mDefaultModuleId = -1;
}
return true;
}
@Override
public List<ModuleAgent> getRegisteredModuleAgents() {
List<ModuleAgent> agents = new ArrayList<ModuleAgent>();
for (int i = 0; i < mRegisteredModuleAgents.size(); i++) {
agents.add(mRegisteredModuleAgents.valueAt(i));
}
return agents;
}
@Override
public List<Integer> getSupportedModeIndexList() {
List<Integer> modeIndexList = new ArrayList<Integer>();
for (int i = 0; i < mRegisteredModuleAgents.size(); i++) {
modeIndexList.add(mRegisteredModuleAgents.keyAt(i));
}
return modeIndexList;
}
@Override
public boolean setDefaultModuleIndex(int moduleId) {
if (mRegisteredModuleAgents.get(moduleId) != null) {
mDefaultModuleId = moduleId;
return true;
}
return false;
}
@Override
public int getDefaultModuleIndex() {
return mDefaultModuleId;
}
@Override
public ModuleAgent getModuleAgent(int moduleId) {
ModuleAgent agent = mRegisteredModuleAgents.get(moduleId);
if (agent == null) {
return mRegisteredModuleAgents.get(mDefaultModuleId);
}
return agent;
}
@Override
public int getQuickSwitchToModuleId(int moduleId, SettingsManager settingsManager, Context context) {
final int photoModuleId = context.getResources().getInteger(R.integer.camera_mode_photo);
final int videoModuleId = context.getResources().getInteger(R.integer.camera_mode_video);
int quickSwitchTo = moduleId;
if (moduleId == photoModuleId || moduleId == context.getResources().getInteger(R.integer.camera_mode_gcam)) {
// Quick switch from camera to video.
quickSwitchTo = videoModuleId;
} else if (moduleId == videoModuleId) {
// Quick switch from video to last used camera (i.e. simple camera or hdr+)
quickSwitchTo = settingsManager.getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_MODULE_LAST_USED);
}
if (mRegisteredModuleAgents.get(quickSwitchTo) != null) {
return quickSwitchTo;
} else {
return moduleId;
}
}
}
19
Source : MediaItemsLoader.java
with Apache License 2.0
from yuchuangu85
with Apache License 2.0
from yuchuangu85
/**
* Returns all MediaItems in a MediaSet, wrapping them in a cursor to appear
* like a PhotoSetLoader
*/
public clreplaced MediaItemsLoader extends AsyncTaskLoader<Cursor> implements LoaderCompatShim<Cursor> {
private static final SyncListener sNullListener = new SyncListener() {
@Override
public void onSyncDone(MediaSet mediaSet, int resultCode) {
}
};
private final MediaSet mMediaSet;
private final DataManager mDataManager;
private Future<Integer> mSyncTask = null;
private ContentListener mObserver = new ContentListener() {
@Override
public void onContentDirty() {
onContentChanged();
}
};
private SparseArray<MediaItem> mMediaItems;
public MediaItemsLoader(Context context) {
super(context);
mDataManager = DataManager.from(context);
String path = mDataManager.getTopSetPath(DataManager.INCLUDE_ALL);
mMediaSet = mDataManager.getMediaSet(path);
}
public MediaItemsLoader(Context context, String parentPath) {
super(context);
mDataManager = DataManager.from(getContext());
mMediaSet = mDataManager.getMediaSet(parentPath);
}
@Override
protected void onStartLoading() {
super.onStartLoading();
mMediaSet.addContentListener(mObserver);
mSyncTask = mMediaSet.requestSync(sNullListener);
forceLoad();
}
@Override
protected boolean onCancelLoad() {
if (mSyncTask != null) {
mSyncTask.cancel();
mSyncTask = null;
}
return super.onCancelLoad();
}
@Override
protected void onStopLoading() {
super.onStopLoading();
cancelLoad();
mMediaSet.removeContentListener(mObserver);
}
@Override
protected void onReset() {
super.onReset();
onStopLoading();
}
@Override
public Cursor loadInBackground() {
// TODO: This probably doesn't work
mMediaSet.reload();
final MatrixCursor cursor = new MatrixCursor(PhotoSetLoader.PROJECTION);
final Object[] row = new Object[PhotoSetLoader.PROJECTION.length];
final SparseArray<MediaItem> mediaItems = new SparseArray<MediaItem>();
mMediaSet.enumerateTotalMediaItems(new ItemConsumer() {
@Override
public void consume(int index, MediaItem item) {
row[PhotoSetLoader.INDEX_ID] = index;
row[PhotoSetLoader.INDEX_DATA] = item.getContentUri().toString();
row[PhotoSetLoader.INDEX_DATE_ADDED] = item.getDateInMs();
row[PhotoSetLoader.INDEX_HEIGHT] = item.getHeight();
row[PhotoSetLoader.INDEX_WIDTH] = item.getWidth();
row[PhotoSetLoader.INDEX_WIDTH] = item.getWidth();
int rawMediaType = item.getMediaType();
int mappedMediaType = FileColumns.MEDIA_TYPE_NONE;
if (rawMediaType == MediaItem.MEDIA_TYPE_IMAGE) {
mappedMediaType = FileColumns.MEDIA_TYPE_IMAGE;
} else if (rawMediaType == MediaItem.MEDIA_TYPE_VIDEO) {
mappedMediaType = FileColumns.MEDIA_TYPE_VIDEO;
}
row[PhotoSetLoader.INDEX_MEDIA_TYPE] = mappedMediaType;
row[PhotoSetLoader.INDEX_SUPPORTED_OPERATIONS] = item.getSupportedOperations();
cursor.addRow(row);
mediaItems.append(index, item);
}
});
synchronized (mMediaSet) {
mMediaItems = mediaItems;
}
return cursor;
}
@Override
public Drawable drawableForItem(Cursor item, Drawable recycle) {
BitmapJobDrawable drawable = null;
if (recycle == null || !(recycle instanceof BitmapJobDrawable)) {
drawable = new BitmapJobDrawable();
} else {
drawable = (BitmapJobDrawable) recycle;
}
int index = item.getInt(PhotoSetLoader.INDEX_ID);
drawable.setMediaItem(mMediaItems.get(index));
return drawable;
}
public static int getThumbnailSize() {
return MediaItem.getTargetSize(MediaItem.TYPE_MICROTHUMBNAIL);
}
@Override
public Uri uriForItem(Cursor item) {
int index = item.getInt(PhotoSetLoader.INDEX_ID);
MediaItem mi = mMediaItems.get(index);
return mi == null ? null : mi.getContentUri();
}
@Override
public ArrayList<Uri> urisForSubItems(Cursor item) {
return null;
}
@Override
public void deleteItemWithPath(Object path) {
MediaObject o = mDataManager.getMediaObject((Path) path);
if (o != null) {
o.delete();
}
}
@Override
public Object getPathForItem(Cursor item) {
int index = item.getInt(PhotoSetLoader.INDEX_ID);
MediaItem mi = mMediaItems.get(index);
if (mi != null) {
return mi.getPath();
}
return null;
}
}
See More Examples