Commit 073c7aed authored by kenan's avatar kenan

Merge branch 'dev_click'

parents 7c69e0e2 17fddcba
...@@ -1800,6 +1800,12 @@ bool ClientField::OnCommonEvent(const irr::SEvent& event) { ...@@ -1800,6 +1800,12 @@ bool ClientField::OnCommonEvent(const irr::SEvent& event) {
return true; return true;
break; break;
} }
case BUTTON_REPORT:{
#ifdef _IRR_ANDROID_PLATFORM_
mainGame->onReportProblem();
#endif
break;
}
} }
break; break;
} }
...@@ -1837,6 +1843,7 @@ bool ClientField::OnCommonEvent(const irr::SEvent& event) { ...@@ -1837,6 +1843,7 @@ bool ClientField::OnCommonEvent(const irr::SEvent& event) {
return true; return true;
break; break;
} }
case CHECKBOX_DRAW_FIELD_SPELL: { case CHECKBOX_DRAW_FIELD_SPELL: {
mainGame->gameConf.draw_field_spell = mainGame->chkDrawFieldSpell->isChecked() ? 1 : 0; mainGame->gameConf.draw_field_spell = mainGame->chkDrawFieldSpell->isChecked() ? 1 : 0;
return true; return true;
......
...@@ -495,10 +495,10 @@ bool Game::Initialize() { ...@@ -495,10 +495,10 @@ bool Game::Initialize() {
scrTabSystem->setSmallStep(1); scrTabSystem->setSmallStep(1);
scrTabSystem->setVisible(false); scrTabSystem->setVisible(false);
posY = 0; posY = 0;
chkIgnore1 = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260, posY + 30 * yScale), tabSystem, CHECKBOX_DISABLE_CHAT, dataManager.GetSysString(1290)); chkIgnore1 = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260* xScale, posY + 30 * yScale), tabSystem, CHECKBOX_DISABLE_CHAT, dataManager.GetSysString(1290));
chkIgnore1->setChecked(gameConf.chkIgnore1 != 0); chkIgnore1->setChecked(gameConf.chkIgnore1 != 0);
posY += 60; posY += 60;
chkIgnore2 = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260, posY + 30 * yScale), tabSystem, -1, dataManager.GetSysString(1291)); chkIgnore2 = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260* xScale, posY + 30 * yScale), tabSystem, -1, dataManager.GetSysString(1291));
chkIgnore2->setChecked(gameConf.chkIgnore2 != 0); chkIgnore2->setChecked(gameConf.chkIgnore2 != 0);
posY += 60; posY += 60;
chkIgnoreDeckChanges = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260 * xScale, posY + 30 * yScale), tabSystem, -1, dataManager.GetSysString(1357)); chkIgnoreDeckChanges = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260 * xScale, posY + 30 * yScale), tabSystem, -1, dataManager.GetSysString(1357));
...@@ -512,10 +512,13 @@ bool Game::Initialize() { ...@@ -512,10 +512,13 @@ bool Game::Initialize() {
posY += 60; posY += 60;
chkQuickAnimation = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260 * xScale, posY + 30 * yScale), tabSystem, CHECKBOX_QUICK_ANIMATION, dataManager.GetSysString(1299)); chkQuickAnimation = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260 * xScale, posY + 30 * yScale), tabSystem, CHECKBOX_QUICK_ANIMATION, dataManager.GetSysString(1299));
chkQuickAnimation->setChecked(gameConf.quick_animation != 0); chkQuickAnimation->setChecked(gameConf.quick_animation != 0);
posY += 60; posY += 60;
chkPreferExpansionScript = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260 * xScale, posY + 30 * yScale), tabSystem, CHECKBOX_PREFER_EXPANSION, dataManager.GetSysString(1379)); chkPreferExpansionScript = env->addCheckBox(false, rect<s32>(posX, posY, posX + 260 * xScale, posY + 30 * yScale), tabSystem, CHECKBOX_PREFER_EXPANSION, dataManager.GetSysString(1379));
chkPreferExpansionScript->setChecked(gameConf.prefer_expansion_script != 0); chkPreferExpansionScript->setChecked(gameConf.prefer_expansion_script != 0);
elmTabSystemLast = chkPreferExpansionScript; posY += 60;
btnReport = env->addButton(rect<s32>(posX, posY, posX + 100 * xScale, posY + 30 * yScale), tabSystem, BUTTON_REPORT, dataManager.GetSysString(1800));
elmTabSystemLast = btnReport;
//show scroll //show scroll
s32 tabSystemLastY = elmTabSystemLast->getRelativePosition().LowerRightCorner.Y; s32 tabSystemLastY = elmTabSystemLast->getRelativePosition().LowerRightCorner.Y;
s32 tabSystemHeight = 300 * yScale; s32 tabSystemHeight = 300 * yScale;
...@@ -1806,6 +1809,10 @@ JNIEnv* Game::getJniEnv(){ ...@@ -1806,6 +1809,10 @@ JNIEnv* Game::getJniEnv(){
return android::getJniEnv(appMain); return android::getJniEnv(appMain);
} }
void Game::onReportProblem(){
gameUI->onReportProblem(getJniEnv());
}
#endif #endif
void Game::refreshTexture(){ void Game::refreshTexture(){
gMutex.lock(); gMutex.lock();
......
...@@ -278,6 +278,7 @@ public: ...@@ -278,6 +278,7 @@ public:
irr::gui::IGUIButton* btnReplayMode; irr::gui::IGUIButton* btnReplayMode;
irr::gui::IGUIButton* btnTestMode; irr::gui::IGUIButton* btnTestMode;
irr::gui::IGUIButton* btnDeckEdit; irr::gui::IGUIButton* btnDeckEdit;
irr::gui::IGUIButton* btnReport;
irr::gui::IGUIButton* btnModeExit; irr::gui::IGUIButton* btnModeExit;
//lan //lan
irr::gui::IGUIWindow* wLanWindow; irr::gui::IGUIWindow* wLanWindow;
...@@ -593,6 +594,7 @@ public: ...@@ -593,6 +594,7 @@ public:
#ifdef _IRR_ANDROID_PLATFORM_ #ifdef _IRR_ANDROID_PLATFORM_
ygo::AndroidGameHost *gameHost; ygo::AndroidGameHost *gameHost;
ygo::AndroidGameUI *gameUI; ygo::AndroidGameUI *gameUI;
void onReportProblem();
#endif #endif
private: private:
int touchLeft; int touchLeft;
...@@ -629,6 +631,7 @@ private: ...@@ -629,6 +631,7 @@ private:
#define BUTTON_TEST_MODE 103 #define BUTTON_TEST_MODE 103
#define BUTTON_DECK_EDIT 104 #define BUTTON_DECK_EDIT 104
#define BUTTON_MODE_EXIT 105 #define BUTTON_MODE_EXIT 105
#define BUTTON_REPORT 106
#define LISTBOX_LAN_HOST 110 #define LISTBOX_LAN_HOST 110
#define BUTTON_JOIN_HOST 111 #define BUTTON_JOIN_HOST 111
#define BUTTON_JOIN_CANCEL 112 #define BUTTON_JOIN_CANCEL 112
......
...@@ -430,6 +430,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) { ...@@ -430,6 +430,7 @@ bool MenuHandler::OnEvent(const irr::SEvent& event) {
mainGame->deckBuilder.Initialize(); mainGame->deckBuilder.Initialize();
break; break;
} }
case BUTTON_YES: { case BUTTON_YES: {
mainGame->HideElement(mainGame->wQuery); mainGame->HideElement(mainGame->wQuery);
if(prev_operation == BUTTON_DELETE_REPLAY) { if(prev_operation == BUTTON_DELETE_REPLAY) {
......
...@@ -30,6 +30,7 @@ void AndroidGameUI::initMethods(JNIEnv *env) { ...@@ -30,6 +30,7 @@ void AndroidGameUI::initMethods(JNIEnv *env) {
java_attachNativeDevice = env->GetMethodID(clazz, "attachNativeDevice", "(I)V"); java_attachNativeDevice = env->GetMethodID(clazz, "attachNativeDevice", "(I)V");
java_getJoinOptions = env->GetMethodID(clazz, "getJoinOptions", "()Ljava/nio/ByteBuffer;"); java_getJoinOptions = env->GetMethodID(clazz, "getJoinOptions", "()Ljava/nio/ByteBuffer;");
java_playSoundEffect = env->GetMethodID(clazz, "playSoundEffect", "(Ljava/lang/String;)V"); java_playSoundEffect = env->GetMethodID(clazz, "playSoundEffect", "(Ljava/lang/String;)V");
java_onReportProblem = env->GetMethodID(clazz, "onReportProblem", "()V");
env->DeleteLocalRef(clazz); env->DeleteLocalRef(clazz);
} }
...@@ -142,4 +143,11 @@ void AndroidGameUI::playSoundEffect(JNIEnv *env, const char *_name) { ...@@ -142,4 +143,11 @@ void AndroidGameUI::playSoundEffect(JNIEnv *env, const char *_name) {
if (name) { if (name) {
env->DeleteLocalRef(name); env->DeleteLocalRef(name);
} }
} }
\ No newline at end of file
void AndroidGameUI::onReportProblem(JNIEnv *env) {
if (!java_onReportProblem) {
return;
}
env->CallVoidMethod(app->activity->clazz, java_onReportProblem);
}
...@@ -25,6 +25,7 @@ namespace ygo { ...@@ -25,6 +25,7 @@ namespace ygo {
jmethodID java_attachNativeDevice; jmethodID java_attachNativeDevice;
jmethodID java_getJoinOptions; jmethodID java_getJoinOptions;
jmethodID java_playSoundEffect; jmethodID java_playSoundEffect;
jmethodID java_onReportProblem;
ANDROID_APP app; ANDROID_APP app;
public: public:
...@@ -58,6 +59,8 @@ namespace ygo { ...@@ -58,6 +59,8 @@ namespace ygo {
void playSoundEffect(JNIEnv *env, const char *name); void playSoundEffect(JNIEnv *env, const char *name);
void onReportProblem(JNIEnv *env);
}; };
} }
......
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="cn.garymb.ygomobile.lib">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<application>
<activity
android:name="cn.garymb.ygomobile.YGOMobileActivity"
android:clearTaskOnLaunch="true"
android:configChanges="orientation|keyboardHidden|screenSize"
android:exported="true"
android:label="YGOMobile"
android:process=":game"
android:launchMode="singleTop"
android:screenOrientation="sensorLandscape"
android:taskAffinity="cn.garymb.ygomobile.game"
android:theme="@android:style/Theme.Holo.NoActionBar.Fullscreen"
android:windowSoftInputMode="adjustPan"
tools:targetApi="honeycomb">
<meta-data
android:name="android.app.lib_name"
android:value="YGOMobile"/>
</activity>
</application>
</manifest>
package cn.garymb.ygodata;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.nio.ByteBuffer;
/**
* @author mabin
*/
public class YGOGameOptions implements Parcelable {
public static final long TIME_OUT = 8 * 1000;
public static final String YGO_GAME_OPTIONS_BUNDLE_TIME = "cn.garymb.ygomobile.ygogameoptions.time";
public static final String YGO_GAME_OPTIONS_BUNDLE_KEY = "cn.garymb.ygomobile.ygogameoptions";
public static final int MAX_BYTE_BUFFER_SIZE = 8192;
public String mServerAddr;
//用户名字
public String mUserName;
public String mRoomName;
public String mRoomPasswd;
//用户密码
public String mUserPassword;
public int mPort;
public int mMode = 0;
public int mRule = 0;
public int mStartLP = 8000;
public int mStartHand = 5;
public int mDrawCount = 1;
public boolean mEnablePriority = false;
public boolean mNoDeckCheck = false;
public boolean mNoDeckShuffle = false;
private boolean isCompleteOptions;
public YGOGameOptions() {
}
public boolean isCompleteOptions() {
return isCompleteOptions;
}
public void setCompleteOptions(boolean isCompleteOptions) {
this.isCompleteOptions = isCompleteOptions;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mServerAddr);
dest.writeString(mUserName);
dest.writeString(mRoomName);
dest.writeString(mRoomPasswd);
dest.writeString(mUserPassword);
dest.writeInt(mPort);
dest.writeInt(mMode);
dest.writeInt(isCompleteOptions ? 1 : 0);
dest.writeInt(mRule);
dest.writeInt(mStartLP);
dest.writeInt(mStartHand);
dest.writeInt(mDrawCount);
dest.writeInt(mEnablePriority ? 1 : 0);
dest.writeInt(mNoDeckCheck ? 1 : 0);
dest.writeInt(mNoDeckShuffle ? 1 : 0);
}
public static final Creator<YGOGameOptions> CREATOR = new Creator<YGOGameOptions>() {
@Override
public YGOGameOptions createFromParcel(Parcel source) {
YGOGameOptions options = new YGOGameOptions();
options.mServerAddr = source.readString();
options.mUserName = source.readString();
options.mRoomName = source.readString();
options.mRoomPasswd = source.readString();
options.mUserPassword = source.readString();
options.mPort = source.readInt();
options.mMode = source.readInt();
options.isCompleteOptions = source.readInt() == 1;
options.mRule = source.readInt();
options.mStartLP = source.readInt();
options.mStartHand = source.readInt();
options.mDrawCount = source.readInt();
options.mEnablePriority = source.readInt() == 1;
options.mNoDeckCheck = source.readInt() == 1;
options.mNoDeckShuffle = source.readInt() == 1;
return options;
}
@Override
public YGOGameOptions[] newArray(int size) {
return new YGOGameOptions[size];
}
};
public String toString() {
StringBuilder builder = new StringBuilder("YGOGameOptions: ");
builder.append("serverAddr: ").append(mServerAddr == null ? "(unspecified)" : mServerAddr).
append(", port: ").append(mPort).
append(", roomName: ").append(mRoomName == null ? "(unspecified)" : mRoomName.toString()).
append(", roomPassword: ").append(mRoomPasswd == null ? "(unspecified)" : mRoomPasswd.toString()).
append(", userName: ").append(mUserName == null ? "(unspecified)" : mUserName.toString()).
append(", mode: ").append(mMode).
append(", isCompleteRequest").append(isCompleteOptions).
append(", rule: ").append(mRule).
append(", startlp: ").append(mStartLP).
append(", startHand: ").append(mStartHand).
append(", drawCount: ").append(mDrawCount).
append(", enablePriority: ").append(mEnablePriority).
append(", noDeckCheck: ").append(mNoDeckCheck).
append(", noDeckShuffle: ").append(mNoDeckShuffle);
return builder.toString();
}
public ByteBuffer toByteBuffer() {
ByteBuffer buffer = ByteBuffer.allocateDirect(MAX_BYTE_BUFFER_SIZE);
putString(buffer, mServerAddr);
putString(buffer, mUserName);
putString(buffer, mRoomName);
putString(buffer, mRoomPasswd);
putString(buffer, mUserPassword);
buffer.putInt(Integer.reverseBytes(mPort));
buffer.putInt(Integer.reverseBytes(mMode));
buffer.putInt(Integer.reverseBytes(isCompleteOptions ? 1 : 0));
if (isCompleteOptions) {
buffer.putInt(Integer.reverseBytes(mRule));
buffer.putInt(Integer.reverseBytes(mStartLP));
buffer.putInt(Integer.reverseBytes(mStartHand));
buffer.putInt(Integer.reverseBytes(mDrawCount));
buffer.putInt(Integer.reverseBytes(mEnablePriority ? 1 : 0));
buffer.putInt(Integer.reverseBytes(mNoDeckCheck ? 1 : 0));
buffer.putInt(Integer.reverseBytes(mNoDeckShuffle ? 1 : 0));
}
return buffer;
}
private void putString(ByteBuffer buffer, String str) {
if (TextUtils.isEmpty(str)) {
buffer.putInt(Integer.reverseBytes(0));
} else {
buffer.putInt(Integer.reverseBytes(str.getBytes().length));
buffer.put(str.getBytes());
}
}
}
package cn.garymb.ygomobile;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import androidx.annotation.Keep;
import java.lang.reflect.Method;
import cn.garymb.ygomobile.interfaces.GameHost;
public abstract class GameApplication extends Application {
private static GameApplication sGameApplication;
private static String sProcessName;
@Override
public void onCreate() {
super.onCreate();
sProcessName = getCurrentProcessName();
sGameApplication = this;
}
public static boolean isGameProcess(){
return sProcessName != null && sProcessName.endsWith(":game");
}
public static String getAppProcessName() {
return sProcessName;
}
@SuppressLint({"PrivateApi", "DiscouragedPrivateApi"})
protected String getCurrentProcessName() {
if (Build.VERSION.SDK_INT >= 28) {
return getProcessName();
}
try {
Class<?> clazz = Class.forName("android.app.ActivityThread");
Method currentProcessName = clazz.getDeclaredMethod("currentProcessName");
return (String) currentProcessName.invoke(null);
} catch (Throwable e) {
Log.w("kk", "currentProcessName", e);
}
int pid = android.os.Process.myPid();
String processName = getPackageName();
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo info : am.getRunningAppProcesses()) {
if (info.pid == pid) {
processName = info.processName;
break;
}
}
return processName;
}
public static GameApplication get() {
return sGameApplication;
}
@Keep
public abstract GameHost getGameHost();
public static boolean isDebug(){
return get().getGameHost().isDebugMode();
}
/**
* @deprecated
*/
@Keep
public boolean canNdkCash() {
return true;
}
}
package cn.garymb.ygomobile;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.Keep;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public final class NativeInitOptions implements Parcelable {
private static final int BUFFER_MAX_SIZE = 8192;
public int mOpenglVersion;
public boolean mIsSoundEffectEnabled;
//工作目录
public String mWorkPath;
// /data/data/cards.cdb;a.cdb;b.cdb
public final List<String> mDbList;
//pics.zip;scripts.zip;a.zip;b.zip
public final List<String> mArchiveList;
public int mCardQuality;
public boolean mIsFontAntiAliasEnabled;
public boolean mIsPendulumScaleEnabled;
public String mFontFile;
public String mResDir;
public String mImageDir;
public NativeInitOptions() {
mDbList = new ArrayList<>();
mArchiveList = new ArrayList<>();
}
public ByteBuffer toNativeBuffer() {
ByteBuffer buffer = ByteBuffer.allocateDirect(BUFFER_MAX_SIZE);
putInt(buffer, mOpenglVersion);
putInt(buffer, mIsSoundEffectEnabled ? 1 : 0);
putInt(buffer, mCardQuality);
putInt(buffer, mIsFontAntiAliasEnabled ? 1 : 0);
putInt(buffer, mIsPendulumScaleEnabled ? 1 : 0);
putString(buffer, mWorkPath);
putInt(buffer, mDbList.size());
for(String str:mDbList){
putString(buffer, str);
}
putInt(buffer, mArchiveList.size());
for (String str : mArchiveList) {
putString(buffer, str);
}
putString(buffer, mFontFile);
putString(buffer, mResDir);
putString(buffer, mImageDir);
return buffer;
}
@Override
public String toString() {
return "NativeInitOptions{" +
"mOpenglVersion=" + mOpenglVersion +
", mIsSoundEffectEnabled=" + mIsSoundEffectEnabled +
", mWorkPath='" + mWorkPath + '\'' +
", mDbList='" + mDbList + '\'' +
", mArchiveList='" + mArchiveList + '\'' +
", mCardQuality=" + mCardQuality +
", mIsFontAntiAliasEnabled=" + mIsFontAntiAliasEnabled +
", mIsPendulumScaleEnabled=" + mIsPendulumScaleEnabled +
'}';
}
private void putString(ByteBuffer buffer, String str) {
if (TextUtils.isEmpty(str)) {
buffer.putInt(Integer.reverseBytes(0));
} else {
buffer.putInt(Integer.reverseBytes(str.getBytes().length));
buffer.put(str.getBytes());
}
}
@SuppressWarnings("unused")
private void putChar(ByteBuffer buffer, char value) {
Short svalue = (short) value;
buffer.putShort((Short.reverseBytes(svalue)));
}
private void putInt(ByteBuffer buffer, int value) {
buffer.putInt((Integer.reverseBytes(value)));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NativeInitOptions options = (NativeInitOptions) o;
return mOpenglVersion == options.mOpenglVersion &&
mIsSoundEffectEnabled == options.mIsSoundEffectEnabled &&
mCardQuality == options.mCardQuality &&
mIsFontAntiAliasEnabled == options.mIsFontAntiAliasEnabled &&
mIsPendulumScaleEnabled == options.mIsPendulumScaleEnabled &&
Objects.equals(mWorkPath, options.mWorkPath) &&
Objects.equals(mDbList, options.mDbList) &&
Objects.equals(mArchiveList, options.mArchiveList) &&
Objects.equals(mFontFile, options.mFontFile) &&
Objects.equals(mResDir, options.mResDir) &&
Objects.equals(mImageDir, options.mImageDir);
}
@Override
public int hashCode() {
return Objects.hash(mOpenglVersion, mIsSoundEffectEnabled, mWorkPath, mDbList, mArchiveList, mCardQuality, mIsFontAntiAliasEnabled, mIsPendulumScaleEnabled, mFontFile, mResDir, mImageDir);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.mOpenglVersion);
dest.writeByte(this.mIsSoundEffectEnabled ? (byte) 1 : (byte) 0);
dest.writeString(this.mWorkPath);
dest.writeStringList(this.mDbList);
dest.writeStringList(this.mArchiveList);
dest.writeInt(this.mCardQuality);
dest.writeByte(this.mIsFontAntiAliasEnabled ? (byte) 1 : (byte) 0);
dest.writeByte(this.mIsPendulumScaleEnabled ? (byte) 1 : (byte) 0);
dest.writeString(this.mFontFile);
dest.writeString(this.mResDir);
dest.writeString(this.mImageDir);
}
protected NativeInitOptions(Parcel in) {
this.mOpenglVersion = in.readInt();
this.mIsSoundEffectEnabled = in.readByte() != 0;
this.mWorkPath = in.readString();
this.mDbList = in.createStringArrayList();
this.mArchiveList = in.createStringArrayList();
this.mCardQuality = in.readInt();
this.mIsFontAntiAliasEnabled = in.readByte() != 0;
this.mIsPendulumScaleEnabled = in.readByte() != 0;
this.mFontFile = in.readString();
this.mResDir = in.readString();
this.mImageDir = in.readString();
}
public static final Parcelable.Creator<NativeInitOptions> CREATOR = new Parcelable.Creator<NativeInitOptions>() {
@Override
public NativeInitOptions createFromParcel(Parcel source) {
return new NativeInitOptions(source);
}
@Override
public NativeInitOptions[] newArray(int size) {
return new NativeInitOptions[size];
}
};
}
/*
* YGOMobileActivity.java
*
* Created on: 2014年2月24日
* Author: mabin
*/
package cn.garymb.ygomobile;
import android.annotation.SuppressLint;
import android.app.NativeActivity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.Process;
import android.util.Log;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.InputQueue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import java.nio.ByteBuffer;
import cn.garymb.ygodata.YGOGameOptions;
import cn.garymb.ygomobile.core.YGOCore;
import cn.garymb.ygomobile.interfaces.GameSize;
import cn.garymb.ygomobile.interfaces.IGameUI;
import cn.garymb.ygomobile.interfaces.GameHost;
import cn.garymb.ygomobile.interfaces.GameConfig;
import cn.garymb.ygomobile.interfaces.IGameActivity;
import cn.garymb.ygomobile.lib.R;
import cn.garymb.ygomobile.tool.GameSoundPlayer;
import cn.garymb.ygomobile.tool.ScreenKeeper;
import cn.garymb.ygomobile.widget.ComboBoxCompat;
import cn.garymb.ygomobile.widget.EditWindowCompat;
import cn.garymb.ygomobile.widget.overlay.OverlayOvalView;
import cn.garymb.ygomobile.widget.overlay.OverlayView;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
public class YGOMobileActivity extends NativeActivity implements
IGameActivity,
View.OnClickListener,
PopupWindow.OnDismissListener,
TextView.OnEditorActionListener,
OverlayOvalView.OnDuelOptionsSelectListener {
private static final String TAG = YGOMobileActivity.class.getSimpleName();
private static boolean DEBUG;
private static final int MAX_REFRESH = 30 * 1000;
//region flag
/**
* 沉浸全屏模式
*/
private static final int windowsFlags;
/**
* 非沉浸全屏模式
*/
private static final int windowsFlags2;
static {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
windowsFlags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE/* 系统UI变化不触发relayout */
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION/* 导航栏悬浮在布局上面 */
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN/* 状态栏悬浮在布局上面 */
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION/* 隐藏导航栏 */
| View.SYSTEM_UI_FLAG_FULLSCREEN/* 隐藏状态栏 */
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY/* 沉浸模式 */;
windowsFlags2 = View.SYSTEM_UI_FLAG_LAYOUT_STABLE/* 系统UI变化不触发relayout */
// | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION/* 隐藏导航栏 */
// | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION/* 隐藏状态栏 */
| View.SYSTEM_UI_FLAG_FULLSCREEN;
} else {
windowsFlags = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LOW_PROFILE;
windowsFlags2 = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LOW_PROFILE;
}
}
//endregion
protected View mContentView;
protected ComboBoxCompat mGlobalComboBox;
protected EditWindowCompat mGlobalEditText;
private volatile long lastRefresh;
private @NonNull
YGOCore mCore;
private @NonNull
GameHost mHost;
private @NonNull
GameConfig mGameConfig;
private volatile boolean mOverlayShowRequest = false;
private volatile int mCompatGUIMode;
private final GameSize mGameSize = new GameSize();
private FrameLayout mLayout;
//画面居中
private SurfaceView mSurfaceView;
private View mClickView;
private boolean replaced = false;
private boolean mInitView = false;
private ScreenKeeper mScreenKeeper;
private GameSoundPlayer mGameSoundPlayer;
private static final boolean USE_INPUT_QUEEN = true;
//点击修正
private static final boolean RESIZE_WINDOW_LAYOUT = false;
private GameApplication app() {
return GameApplication.get();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
DEBUG = GameApplication.isDebug();
mGameConfig = getIntent().getParcelableExtra(GameConfig.EXTRA_CONFIG);
mCore = YGOCore.getInstance();
mHost = app().getGameHost();
fullscreen();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
getWindow().setAttributes(lp);
}
if (mGameConfig != null) {
mGameSoundPlayer = new GameSoundPlayer(mHost.getGameAsset());
if (mGameConfig.isEnableSoundEffect()) {
mGameSoundPlayer.initSoundEffectPool();
}
}
mScreenKeeper = new ScreenKeeper(this);
mHost.onBeforeCreate(this);
super.onCreate(savedInstanceState);
if (mGameConfig == null) {
finish();
return;
}
if (DEBUG) {
Log.e("YGOStarter", "跳转完成" + System.currentTimeMillis());
}
if (mGameConfig.isLockScreenOrientation()) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
if (DEBUG) {
Log.i(TAG, "USE_INPUT_QUEEN:" + USE_INPUT_QUEEN + ",RESIZE_WINDOW_LAYOUT=" + RESIZE_WINDOW_LAYOUT);
}
initExtraView();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
fullscreen();
}
}
});
}
mHost.initWindbot(mGameConfig.getNativeInitOptions(), mGameConfig);
mHost.onAfterCreate(this);
}
@SuppressLint("WakelockTimeout")
@Override
protected void onResume() {
super.onResume();
if (DEBUG) {
Log.e("YGOStarter", "ygo显示" + System.currentTimeMillis());
}
mScreenKeeper.keep();
}
@Override
protected void onPause() {
super.onPause();
mScreenKeeper.release();
}
private void refreshTextures() {
if (System.currentTimeMillis() - lastRefresh >= MAX_REFRESH) {
lastRefresh = System.currentTimeMillis();
Toast.makeText(this, R.string.refresh_textures, Toast.LENGTH_SHORT).show();
mCore.refreshTexture();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.hasExtra(GameConfig.EXTRA_CONFIG)) {
GameConfig config = getIntent().getParcelableExtra(GameConfig.EXTRA_CONFIG);
if (!mGameConfig.equals(config)) {
mGameConfig = config;
if (config.isEnableSoundEffect()) {
mGameSoundPlayer.initSoundEffectPool();
} else {
mGameSoundPlayer.release();
}
}
}
handleExternalCommand(intent);
}
@Override
protected void onDestroy() {
super.onDestroy();
}
@Override
public void finish() {
mCore.release();
mHost.onGameExit(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAndRemoveTask();
} else {
super.finish();
}
}
private void handleExternalCommand(Intent intent) {
long time = intent.getLongExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_TIME, 0);
if (System.currentTimeMillis() - time >= YGOGameOptions.TIME_OUT) {
if (DEBUG)
Log.i(TAG, "command timeout");
return;
}
YGOGameOptions options = intent
.getParcelableExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_KEY);
if (options != null) {
if (DEBUG)
Log.i(TAG, "receive:" + time + ":" + options.toString());
ByteBuffer buffer = options.toByteBuffer();
mCore.joinGame(buffer, buffer.position());
} else {
if (DEBUG)
Log.i(TAG, "receive :null");
}
}
private void fullscreen() {
if (mGameConfig.isImmerSiveMode()) {
//沉浸模式
getWindow().getDecorView().setSystemUiVisibility(windowsFlags);
} else {
getWindow().getDecorView().setSystemUiVisibility(windowsFlags2);
}
int[] size = getGameSize();
setGameSize(size[0], size[1]);
}
private int[] getGameSize() {
GameSize gameSize = mHost.getGameSize(this, mGameConfig);
mGameSize.update(gameSize);
int w = gameSize.getWidth();
int h = gameSize.getHeight();
if (RESIZE_WINDOW_LAYOUT || !USE_INPUT_QUEEN) {
mGameSize.setTouch(0, 0);
}
if (DEBUG) {
Log.i(TAG, "GameSize:" + mGameSize);
}
return new int[]{w, h};
}
@Override
public void onBackPressed() {
//
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// mCore.sendKeyEvent(event);
return super.onKeyUp(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// mCore.sendKeyEvent(event);
return super.onKeyUp(keyCode, event);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void setContentView(View view) {
int[] size = getGameSize();
int w = size[0];
int h = size[1];
setGameSize(w, h);
mLayout = new FrameLayout(this);
mSurfaceView = new SurfaceView(this);
// mLayout.setFitsSystemWindows(true);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(w, h);
lp.gravity = Gravity.CENTER;
mLayout.addView(mSurfaceView, lp);
mLayout.addView(view, lp);
super.setContentView(mLayout);
mClickView = view;
getWindow().takeSurface(null);
replaced = true;
mSurfaceView.getHolder().addCallback(this);
if (!USE_INPUT_QUEEN) {
getWindow().takeInputQueue(null);
mClickView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return mCore.sendTouchEvent(event.getAction(), (int) event.getX(), (int) event.getY(), event.getPointerId(0));
}
});
} else if (RESIZE_WINDOW_LAYOUT) {
getWindow().setLayout(w, h);
}
mSurfaceView.requestFocus();
mInitView = true;
}
private void setGameSize(int w, int h) {
if (mInitView) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mClickView.getLayoutParams();
lp.width = w;
lp.height = h;
mClickView.setLayoutParams(lp);
mSurfaceView.setLayoutParams(lp);
mSurfaceView.getHolder().setFixedSize(w, h);
}
if (RESIZE_WINDOW_LAYOUT) {
getWindow().setLayout(w, h);
}
}
@Override
public void onGlobalLayout() {
// super.onGlobalLayout();
}
//region popup window
private void initExtraView() {
mContentView = getWindow().getDecorView().findViewById(android.R.id.content);
mGlobalComboBox = new ComboBoxCompat(this);
mGlobalComboBox.setButtonListener(this);
mGlobalEditText = new EditWindowCompat(this);
mGlobalEditText.setEditActionListener(this);
mGlobalEditText.setOnDismissListener(this);
// mChainOverlayView = new OverlayRectView(this);
// mOverlayView = new OverlayOvalView(this);
// mChainOverlayView.setDuelOpsListener(this);
// mOverlayView.setDuelOpsListener(this);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
// Log.e("YGOMobileActivity","窗口变化"+hasFocus);
if (hasFocus) {
fullscreen();
mContentView.setHapticFeedbackEnabled(true);
} else {
mContentView.setHapticFeedbackEnabled(false);
}
super.onWindowFocusChanged(hasFocus);
}
@Override
public void onDismiss() {
if (mOverlayShowRequest) {
// mOverlayView.show();
// mChainOverlayView.show();
}
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.cancel) {
} else if (v.getId() == R.id.submit) {
int idx = mGlobalComboBox.getCurrentSelection();
if (DEBUG) {
Log.d(TAG, "showComboBoxCompat: receive selection: " + idx);
}
if (mCompatGUIMode == ComboBoxCompat.COMPAT_GUI_MODE_COMBOBOX) {
mCore.setComboBoxSelection(idx);
} else if (mCompatGUIMode == ComboBoxCompat.COMPAT_GUI_MODE_CHECKBOXES_PANEL) {
mCore.setCheckBoxesSelection(idx);
}
}
mGlobalComboBox.dismiss();
}
@Override
public void onDuelOptionsSelected(int mode, boolean action) {
switch (mode) {
case OverlayView.MODE_CANCEL_CHAIN_OPTIONS:
if (DEBUG) {
Log.d(TAG, "Constants.MODE_CANCEL_CHAIN_OPTIONS: " + action);
}
mCore.cancelChain();
break;
case OverlayView.MODE_REFRESH_OPTION:
if (DEBUG) {
Log.d(TAG, "Constants.MODE_REFRESH_OPTION: " + action);
}
mCore.refreshTexture();
break;
case OverlayView.MODE_REACT_CHAIN_OPTION:
if (DEBUG) {
Log.d(TAG, "Constants.MODE_REACT_CHAIN_OPTION: " + action);
}
mCore.reactChain(action);
break;
case OverlayView.MODE_IGNORE_CHAIN_OPTION:
if (DEBUG) {
Log.d(TAG, "Constants.MODE_IGNORE_CHAIN_OPTION: " + action);
}
mCore.ignoreChain(action);
break;
default:
break;
}
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
final String text = v.getText().toString();
mCore.insertText(text);
mGlobalEditText.dismiss();
return false;
}
//endregion
@Override
public ByteBuffer getJoinOptions() {
Intent intent = getIntent();
long time = intent.getLongExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_TIME, 0);
if (System.currentTimeMillis() - time >= YGOGameOptions.TIME_OUT) {
if (DEBUG)
Log.i(TAG, "command timeout");
return null;
}
if (intent.hasExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_KEY)) {
YGOGameOptions options = intent
.getParcelableExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_KEY);
intent.removeExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_KEY);
if (options != null) {
return options.toByteBuffer();
}
}
return null;
}
@Override
public void playSoundEffect(String path) {
if (mGameConfig.isEnableSoundEffect()) {
mGameSoundPlayer.playSoundEffect(path);
}
}
@Override
public void toggleIME(final boolean show, final String hint) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (show) {
// if (mOverlayShowRequest) {
// mOverlayView.hide();
// mChainOverlayView.hide();
// }
mGlobalEditText.fillContent(hint);
mGlobalEditText.showAtLocation(mContentView,
Gravity.BOTTOM, 0, 0);
} else {
mGlobalEditText.dismiss();
}
}
});
}
@Override
public void showComboBoxCompat(final String[] items, final boolean isShow, final int mode) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mCompatGUIMode = mode;
if (isShow) {
mGlobalComboBox.fillContent(items);
mGlobalComboBox.showAtLocation(mContentView,
Gravity.BOTTOM, 0, 0);
}
}
});
}
@Override
public void performHapticFeedback() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mContentView.performHapticFeedback(
HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
});
}
@Override
public int getWindowLeft() {
if (DEBUG) {
Log.i(TAG, "getWindowLeft:" + mGameSize.getTouchX());
}
return mGameSize.getTouchX();
}
@Override
public int getWindowTop() {
if (DEBUG) {
Log.i(TAG, "getWindowTop:" + mGameSize.getTouchY());
}
return mGameSize.getTouchY();
}
@Override
public int getWindowWidth() {
if (DEBUG) {
Log.i(TAG, "getWindowWidth:" + mGameSize.getWidth());
}
return mGameSize.getWidth();
}
@Override
public int getWindowHeight() {
if (DEBUG) {
Log.i(TAG, "getWindowHeight:" + mGameSize.getHeight());
}
return mGameSize.getHeight();
}
@Override
public ByteBuffer getInitOptions() {
return mGameConfig.getNativeInitOptions().toNativeBuffer();
}
@Override
public void attachNativeDevice(int device) {
mCore.setNativeAndroidDevice(device);
}
@Override
public void onInputQueueCreated(InputQueue queue) {
if (USE_INPUT_QUEEN) {
super.onInputQueueCreated(queue);
}
}
@Override
public void onInputQueueDestroyed(InputQueue queue) {
if (USE_INPUT_QUEEN) {
super.onInputQueueDestroyed(queue);
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!replaced) {
return;
}
super.surfaceCreated(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (!replaced) {
return;
}
super.surfaceChanged(holder, format, width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (!replaced) {
return;
}
super.surfaceDestroyed(holder);
}
@Override
public void surfaceRedrawNeeded(SurfaceHolder holder) {
if (!replaced) {
return;
}
super.surfaceRedrawNeeded(holder);
}
@Override
public String getSetting(String key) {
return mHost.getSetting(key);
}
@Override
public int getIntSetting(String key, int def) {
return mHost.getIntSetting(key, def);
}
@Override
public void saveIntSetting(String key, int value) {
mHost.saveIntSetting(key, value);
}
@Override
public void saveSetting(String key, String value) {
mHost.saveSetting(key, value);
}
@Override
public void runWindbot(String cmd) {
mHost.runWindbot(cmd);
}
@Override
public int getLocalAddr() {
return mHost.getLocalAddr();
}
@Override
public void onReportProblem() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mHost.onGameReport(YGOMobileActivity.this, mGameConfig);
}
});
}
}
package cn.garymb.ygomobile.core;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.util.Log;
import androidx.annotation.Keep;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import cn.garymb.ygomobile.GameApplication;
import static android.content.ContentValues.TAG;
public class BpgImage {
@Keep
public static Bitmap getBpgImage(InputStream inputStream, Bitmap.Config config) {
ByteArrayOutputStream outputStream = null;
try {
outputStream = new ByteArrayOutputStream();
byte[] tmp = new byte[4096];
int len = 0;
while ((len = inputStream.read(tmp)) != -1) {
outputStream.write(tmp, 0, len);
}
//解码前
byte[] bpg = outputStream.toByteArray();
return getBpgImage(bpg, config);
} catch (Exception e) {
Log.e(TAG, "zip bpg image", e);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
@Keep
public static Bitmap getBpgImage(byte[] bpg, Bitmap.Config config) {
try {
//解码后
byte[] data = nativeBpgImage(bpg);
int start = 8;
int w = byte2int(Arrays.copyOfRange(data, 0, 4));
int h = byte2int(Arrays.copyOfRange(data, 4, 8));
if (w < 0 || h < 0) {
if (GameApplication.isDebug()) {
Log.e(TAG, "zip image:w=" + w + ",h=" + h);
}
return null;
}
int index = 0;
int[] colors = new int[(data.length - start) / 3];
for (int i = 0; i < colors.length; i++) {
index = start + i * 3;
colors[i] = Color.rgb(byte2uint(data[index + 0]), byte2uint(data[index + 1]), byte2uint(data[index + 2]));
}
return Bitmap.createBitmap(colors, w, h, config);
} catch (Throwable e) {
Log.e(TAG, "zip bpg image", e);
return null;
}
}
private static int byte2uint(byte b) {
int i = b;
if (b < 0) {
i = 0xff + 1 + b;
}
return i;
}
private static int byte2int(byte[] res) {
String str = String.format("%02x%02x%02x%02x", res[3], res[2], res[1], res[0]);
return Integer.parseInt(str, 16);
}
//显示卡图
private static native byte[] nativeBpgImage(byte[] data);
}
package cn.garymb.ygomobile.core;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.MotionEvent;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
import cn.garymb.ygodata.YGOGameOptions;
import cn.garymb.ygomobile.YGOMobileActivity;
import cn.garymb.ygomobile.interfaces.GameConfig;
public class YGOCore {
public static final float GAME_WIDTH = 1024.0f;
public static final float GAME_HEIGHT = 640.0f;
private static final String TAG = "ygomobile";
public static final String CONF_LAST_DECK = "lastdeck";
public static final String CONF_LAST_CATEGORY = "lastcategory";
private static YGOCore sYGOCore;
private static final boolean DISABLE_THREAD = true;
public static YGOCore getInstance() {
if (sYGOCore == null) {
synchronized (YGOCore.class) {
if (sYGOCore == null) {
sYGOCore = new YGOCore();
}
}
}
return sYGOCore;
}
static {
try {
System.loadLibrary("YGOMobile");
} catch (Throwable e) {
//ignore
}
}
private int nativeAndroidDevice = 0;
private HandlerThread mWorker;
private Handler H;
private YGOCore() {
if (!DISABLE_THREAD) {
mWorker = new HandlerThread("ygopro_core_work");
mWorker.start();
H = new Handler(mWorker.getLooper());
}
}
@Keep
public void release() {
if (!DISABLE_THREAD) {
if (mWorker != null) {
mWorker.quitSafely();
mWorker = null;
}
}
}
@Keep
public void setNativeAndroidDevice(int nativeAndroidDevice) {
Log.i(TAG, "setNativeAndroidDevice:" + nativeAndroidDevice);
this.nativeAndroidDevice = nativeAndroidDevice;
}
@Keep
public boolean sendTouchEvent(final int action, final int x, final int y, final int id) {
if (nativeAndroidDevice == 0) {
Log.w(TAG, "sendTouchEvent fail nativeAndroidDevice = 0");
return false;
}
final int eventType = action & MotionEvent.ACTION_MASK;
boolean touchReceived = true;
switch (eventType) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
break;
default:
touchReceived = false;
break;
}
if (!touchReceived) {
return false;
}
if (id != 0) {
return false;
}
if (!DISABLE_THREAD) {
H.post(new Runnable() {
@Override
public void run() {
sendTouchInner(action, x, y, id);
}
});
} else {
sendTouchInner(action, x, y, id);
}
return true;
}
private void sendTouchInner(int action, int x, int y, int id) {
if (nativeAndroidDevice != 0) {
nativeSendTouch(nativeAndroidDevice, action, x, y, id);
}
}
@Keep
public void cancelChain() {
if (nativeAndroidDevice != 0) {
nativeCancelChain(nativeAndroidDevice);
}
}
@Keep
public void ignoreChain(boolean begin) {
if (nativeAndroidDevice != 0) {
nativeIgnoreChain(nativeAndroidDevice, begin);
}
}
@Keep
public void reactChain(boolean begin) {
if (nativeAndroidDevice != 0) {
nativeReactChain(nativeAndroidDevice, begin);
}
}
@Keep
public void insertText(String text) {
if (nativeAndroidDevice != 0) {
nativeInsertText(nativeAndroidDevice, text);
}
}
@Keep
public void setComboBoxSelection(int idx) {
if (nativeAndroidDevice != 0) {
nativeSetComboBoxSelection(nativeAndroidDevice, idx);
}
}
@Keep
public void refreshTexture() {
if (nativeAndroidDevice != 0) {
nativeRefreshTexture(nativeAndroidDevice);
}
}
@Keep
public void setCheckBoxesSelection(int idx) {
if (nativeAndroidDevice != 0) {
nativeSetCheckBoxesSelection(nativeAndroidDevice, idx);
}
}
@Keep
public void joinGame(ByteBuffer options, int length) {
if (nativeAndroidDevice != 0) {
nativeJoinGame(nativeAndroidDevice, options, length);
}
}
public static void startGame(Context activity, @Nullable YGOGameOptions options, @NonNull GameConfig gameConfig) {
Intent intent = new Intent(activity, YGOMobileActivity.class);
if (options != null) {
intent.putExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_KEY, options);
intent.putExtra(YGOGameOptions.YGO_GAME_OPTIONS_BUNDLE_TIME, System.currentTimeMillis());
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(GameConfig.EXTRA_CONFIG, gameConfig);
activity.startActivity(intent);
}
//插入文本(大概是发送消息)
private native void nativeInsertText(int device, String text);
//刷新文字
private native void nativeRefreshTexture(int device);
//忽略时点
private native void nativeIgnoreChain(int device, boolean begin);
//强制时点
private native void nativeReactChain(int device, boolean begin);
//取消连锁
private native void nativeCancelChain(int device);
private native void nativeSetCheckBoxesSelection(int device, int idx);
private native void nativeSetComboBoxSelection(int device, int idx);
private native void nativeJoinGame(int device, ByteBuffer buffer, int length);
private native boolean nativeSendTouch(int device, int action, int x, int y, int id);
}
package cn.garymb.ygomobile.interfaces;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.Keep;
import java.util.Objects;
import cn.garymb.ygomobile.NativeInitOptions;
@Keep
public class GameConfig implements Parcelable {
public static final String EXTRA_CONFIG = "ygo_config";
private NativeInitOptions nativeInitOptions;
private boolean lockScreenOrientation;
private boolean sensorRefresh;
private boolean keepScale;
/***
* 隐藏底部导航栏
*/
private boolean immerSiveMode;
private boolean enableSoundEffect;
private int notchHeight;
public int getNotchHeight() {
return notchHeight;
}
public void setNotchHeight(int notchHeight) {
this.notchHeight = notchHeight;
}
public NativeInitOptions getNativeInitOptions() {
return nativeInitOptions;
}
public void setNativeInitOptions(NativeInitOptions nativeInitOptions) {
this.nativeInitOptions = nativeInitOptions;
}
public boolean isLockScreenOrientation() {
return lockScreenOrientation;
}
public void setLockScreenOrientation(boolean lockScreenOrientation) {
this.lockScreenOrientation = lockScreenOrientation;
}
public boolean isSensorRefresh() {
return sensorRefresh;
}
public void setSensorRefresh(boolean sensorRefresh) {
this.sensorRefresh = sensorRefresh;
}
public boolean isImmerSiveMode() {
return immerSiveMode;
}
public void setImmerSiveMode(boolean immerSiveMode) {
this.immerSiveMode = immerSiveMode;
}
public boolean isEnableSoundEffect() {
return enableSoundEffect;
}
public void setEnableSoundEffect(boolean enableSoundEffect) {
this.enableSoundEffect = enableSoundEffect;
}
public boolean isKeepScale() {
return keepScale;
}
public void setKeepScale(boolean keepScale) {
this.keepScale = keepScale;
}
public GameConfig() {
nativeInitOptions = new NativeInitOptions();
lockScreenOrientation = false;
sensorRefresh = true;
immerSiveMode = false;
enableSoundEffect = true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GameConfig config = (GameConfig) o;
return lockScreenOrientation == config.lockScreenOrientation &&
sensorRefresh == config.sensorRefresh &&
keepScale == config.keepScale &&
immerSiveMode == config.immerSiveMode &&
enableSoundEffect == config.enableSoundEffect &&
Objects.equals(nativeInitOptions, config.nativeInitOptions);
}
@Override
public int hashCode() {
return Objects.hash(nativeInitOptions, lockScreenOrientation, sensorRefresh, keepScale, immerSiveMode, enableSoundEffect);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(this.nativeInitOptions, flags);
dest.writeByte(this.lockScreenOrientation ? (byte) 1 : (byte) 0);
dest.writeByte(this.sensorRefresh ? (byte) 1 : (byte) 0);
dest.writeByte(this.keepScale ? (byte) 1 : (byte) 0);
dest.writeByte(this.immerSiveMode ? (byte) 1 : (byte) 0);
dest.writeByte(this.enableSoundEffect ? (byte) 1 : (byte) 0);
dest.writeInt(this.notchHeight);
}
protected GameConfig(Parcel in) {
this.nativeInitOptions = in.readParcelable(NativeInitOptions.class.getClassLoader());
this.lockScreenOrientation = in.readByte() != 0;
this.sensorRefresh = in.readByte() != 0;
this.keepScale = in.readByte() != 0;
this.immerSiveMode = in.readByte() != 0;
this.enableSoundEffect = in.readByte() != 0;
this.notchHeight = in.readInt();
}
public static final Creator<GameConfig> CREATOR = new Creator<GameConfig>() {
@Override
public GameConfig createFromParcel(Parcel source) {
return new GameConfig(source);
}
@Override
public GameConfig[] newArray(int size) {
return new GameConfig[size];
}
};
@Override
public String toString() {
return "GameConfig{" +
"nativeInitOptions=" + nativeInitOptions +
", lockScreenOrientation=" + lockScreenOrientation +
", sensorRefresh=" + sensorRefresh +
", keepScale=" + keepScale +
", immerSiveMode=" + immerSiveMode +
", enableSoundEffect=" + enableSoundEffect +
", notchHeight=" + notchHeight +
'}';
}
}
package cn.garymb.ygomobile.interfaces;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import androidx.annotation.Keep;
import cn.garymb.ygomobile.NativeInitOptions;
import cn.garymb.ygomobile.tool.GameSoundPlayer;
import cn.garymb.ygomobile.tool.NetworkController;
@Keep
public abstract class GameHost implements IGameHost{
private final NetworkController mNetworkController;
public GameHost(Context context) {
mNetworkController = new NetworkController(context);
}
@Override
public int getLocalAddr() {
return mNetworkController.getIPAddress();
}
public abstract AssetManager getGameAsset();
public abstract GameSize getGameSize(Activity activity, GameConfig config);
public boolean isDebugMode(){
return false;
}
public void onBeforeCreate(Activity activity){
}
public void onAfterCreate(Activity activity){
}
public void onGameExit(Activity activity){
}
public void onGameReport(Activity activity, GameConfig config){
}
public abstract void initWindbot(NativeInitOptions options, GameConfig config);
}
package cn.garymb.ygomobile.interfaces;
public class GameSize {
private int width;
private int height;
private int touchX;
private int touchY;
private int fullW;
private int fullH;
private int actW;
private int actH;
public void update(GameSize size) {
synchronized (this) {
this.width = size.width;
this.height = size.height;
this.touchX = size.touchX;
this.touchY = size.touchY;
}
}
public void setTouch(int touchX, int touchY) {
synchronized (this) {
this.touchX = touchX;
this.touchY = touchY;
}
}
public int getWidth() {
synchronized (this) {
return width;
}
}
public int getHeight() {
synchronized (this) {
return height;
}
}
public int getTouchX() {
synchronized (this) {
return touchX;
}
}
public int getTouchY() {
synchronized (this) {
return touchY;
}
}
public int getFullW() {
return fullW;
}
public int getFullH() {
return fullH;
}
public int getActW() {
return actW;
}
public int getActH() {
return actH;
}
public void setScreen(int fullW, int fullH, int actW, int actH) {
this.fullW = fullW;
this.fullH = fullH;
this.actW = actW;
this.actH = actH;
}
public GameSize() {
}
public GameSize(int width, int height, int touchX, int touchY) {
this.width = width;
this.height = height;
this.touchX = touchX < 0 ? 0 : touchX;
this.touchY = touchY < 0 ? 0 : touchY;
}
@Override
public String toString() {
return "GameSize{" +
"width=" + width +
", height=" + height +
", touchX=" + touchX +
", touchY=" + touchY +
", fullW=" + fullW +
", fullH=" + fullH +
", actW=" + actW +
", actH=" + actH +
'}';
}
}
package cn.garymb.ygomobile.interfaces;
import androidx.annotation.Keep;
@Keep
public interface IGameActivity extends IGameUI, IGameHost{
}
package cn.garymb.ygomobile.interfaces;
public interface IGameHost {
String getSetting(String key);
int getIntSetting(String key, int def);
void saveIntSetting(String key, int value);
void saveSetting(String key, String value);
void runWindbot(String cmd);
int getLocalAddr();
}
package cn.garymb.ygomobile.interfaces;
import androidx.annotation.Keep;
import java.nio.ByteBuffer;
@Keep
public interface IGameUI {
int getWindowLeft();
int getWindowTop();
int getWindowWidth();
int getWindowHeight();
void toggleIME(boolean show, String message);
void performHapticFeedback();
void showComboBoxCompat(String[] items, boolean isShow, int mode);
ByteBuffer getInitOptions();
void attachNativeDevice(int device);
ByteBuffer getJoinOptions();
void playSoundEffect(String path);
void onReportProblem();
}
package cn.garymb.ygomobile.tool;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.AudioManager;
import android.media.SoundPool;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class GameSoundPlayer {
private AssetManager mAssetManager;
private SoundPool mSoundEffectPool;
private final Map<String, Integer> mSoundIdMap;
private boolean mInit = false;
public GameSoundPlayer(AssetManager mAssetManager) {
this.mAssetManager = mAssetManager;
mSoundIdMap = new HashMap<>();
}
@SuppressWarnings("deprecation")
public void initSoundEffectPool() {
synchronized (this) {
if (mInit) {
return;
}
mInit = true;
}
mSoundEffectPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
AssetManager am = mAssetManager;
String[] sounds = null;
try {
sounds = am.list("sound");
} catch (IOException e) {
e.printStackTrace();
}
if (sounds != null) {
int ret = 0;
String path = null;
for (String sound : sounds) {
path = "sound" + File.separator + sound;
AssetFileDescriptor fd = null;
ret = 0;
try {
fd = am.openFd(path);
ret = mSoundEffectPool.load(fd, 1);
} catch (Throwable e) {
//ignore
} finally {
if (fd != null) {
try {
fd.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ret != 0) {
mSoundIdMap.put(path, ret);
}
}
}
}
}
public void playSoundEffect(String path) {
if (mSoundEffectPool == null) {
return;
}
Integer id = mSoundIdMap.get(path);
if (id != null) {
mSoundEffectPool.play(id, 0.5f, 0.5f, 2, 0, 1.0f);
}
}
public void release() {
synchronized (this) {
if (!mInit) {
return;
}
mInit = false;
}
mSoundIdMap.clear();
if (mSoundEffectPool != null) {
mSoundEffectPool.release();
mSoundEffectPool = null;
}
}
}
package cn.garymb.ygomobile.tool;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
/**
* @author mabin
*/
public final class NetworkController {
private WifiManager mWM;
private ConnectivityManager mCM;
/**
*
*/
public NetworkController(Context context) {
mWM = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
mCM = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
}
/**
* @return
* @author: mabin
**/
public boolean isWifiConnected() {
boolean isWifiEnabled = mWM.isWifiEnabled();
NetworkInfo ni = mCM.getActiveNetworkInfo();
if (isWifiEnabled && null != ni && ni.isConnected()
&& ni.getType() == ConnectivityManager.TYPE_WIFI) {
return true;
}
return false;
}
public int getIPAddress() {
if (!isWifiConnected()) {
return -1;
}
WifiInfo wi = mWM.getConnectionInfo();
return wi.getIpAddress();
}
}
package cn.garymb.ygomobile.tool;
import android.content.Context;
import android.os.PowerManager;
public class ScreenKeeper {
//电池管理
private PowerManager mPM;
private PowerManager.WakeLock mLock;
private String TAG = "ScreenKeeper";
public ScreenKeeper(Context context){
mPM = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
}
public void keep(){
if (mLock == null) {
mLock = mPM.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, TAG);
}
mLock.acquire();
}
public void release(){
if (mLock != null) {
if (mLock.isHeld()) {
mLock.release();
}
}
}
}
/*
* ComboBoxCompat.java
*
* Created on: 2014年3月15日
* Author: mabin
*/
package cn.garymb.ygomobile.widget;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.PopupWindow;
import android.widget.ViewFlipper;
import cn.garymb.ygomobile.lib.R;
import cn.garymb.ygomobile.widget.wheelview.ArrayWheelAdapter;
import cn.garymb.ygomobile.widget.wheelview.WheelView;
public class ComboBoxCompat extends PopupWindow {
/*change this will affect C++ code, be careful!*/
public static final int COMPAT_GUI_MODE_COMBOBOX = 0;
/*change this will affect C++ code, be careful!*/
public static final int COMPAT_GUI_MODE_CHECKBOXES_PANEL = 1;
private Context mContext;
private WheelView mComboBoxContent;
private Button mSubmitButton;
private Button mCancelButton;
private ViewFlipper mFlipper;
private ArrayWheelAdapter<String> mAdapter;
public ComboBoxCompat(Context context) {
// TODO Auto-generated constructor stub
super(context);
mContext = context;
View menuView = LayoutInflater.from(mContext).inflate(R.layout.combobox_compat_layout, null);
mComboBoxContent = (WheelView) menuView.findViewById(R.id.combobox_content);
mSubmitButton = (Button) menuView.findViewById(R.id.submit);
mCancelButton = (Button) menuView.findViewById(R.id.cancel);
mFlipper = new ViewFlipper(context);
mFlipper.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
mFlipper.addView(menuView);
mFlipper.setFlipInterval(6000000);
setContentView(mFlipper);
}
public void setButtonListener(OnClickListener listener) {
mSubmitButton.setOnClickListener(listener);
mCancelButton.setOnClickListener(listener);
}
public void fillContent(String[] items) {
mAdapter = new ArrayWheelAdapter<String>(mContext, items);
mComboBoxContent.setViewAdapter(mAdapter);
this.setWidth(LayoutParams.MATCH_PARENT);
this.setHeight(LayoutParams.WRAP_CONTENT);
this.setFocusable(true);
ColorDrawable dw = new ColorDrawable(0x00000000);
this.setBackgroundDrawable(dw);
this.update();
}
public int getCurrentSelection() {
return mComboBoxContent.getCurrentItem();
}
@Override
public void showAtLocation(View parent, int gravity, int x, int y) {
super.showAtLocation(parent, gravity, x, y);
mFlipper.startFlipping();
}
@Override
public void dismiss() {
// TODO Auto-generated method stub
super.dismiss();
}
}
/*
* EditWindowCompat.java
*
* Created on: 2014年3月15日
* Author: mabin
*/
package cn.garymb.ygomobile.widget;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.PopupWindow;
import android.widget.TextView.OnEditorActionListener;
import cn.garymb.ygomobile.lib.R;
/**
* @author mabin
*
*/
public class EditWindowCompat extends PopupWindow {
private Context mContext;
private ViewGroup mContentView;
private EditText mEditText;
private InputMethodManager mIM;
public EditWindowCompat(Context context) {
super(context);
mContext = context;
mContentView = (ViewGroup) LayoutInflater.from(mContext).inflate(
R.layout.text_input_compat_layout, null);
mEditText = (EditText) mContentView.findViewById(R.id.global_input);
mIM = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
setContentView(mContentView);
}
public void fillContent(String hint) {
mEditText.setText(hint);
this.setWidth(LayoutParams.MATCH_PARENT);
this.setHeight(LayoutParams.WRAP_CONTENT);
this.setFocusable(true);
this.update();
}
/* (non-Javadoc)
* @see android.widget.PopupWindow#showAtLocation(android.view.View, int, int, int)
*/
@Override
public void showAtLocation(View parent, int gravity, int x, int y) {
// TODO Auto-generated method stub
mEditText.requestFocus();
mIM.showSoftInput(mEditText, 0);
super.showAtLocation(parent, gravity, x, y);
}
/* (non-Javadoc)
* @see android.widget.PopupWindow#dismiss()
*/
@Override
public void dismiss() {
// TODO Auto-generated method stub
mEditText.clearFocus();
mIM.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
super.dismiss();
}
public void setEditActionListener(OnEditorActionListener listener) {
mEditText.setOnEditorActionListener(listener);
}
}
/*
Copyright 2011 jawsware international
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cn.garymb.ygomobile.widget.overlay;
import android.content.Context;
import android.view.View;
import android.widget.TextView;
import cn.garymb.ygomobile.lib.R;
public class OverlayOvalView extends OverlayView implements View.OnClickListener {
/**
* @author mabin
*
*/
public interface OnDuelOptionsSelectListener {
void onDuelOptionsSelected(int mode, boolean action);
}
private TextView mInfo;
private OnDuelOptionsSelectListener mListener;
public OverlayOvalView(Context context) {
super(context, R.layout.overlay_oval);
}
@Override
protected void onInflateView() {
mInfo = (TextView) this.findViewById(R.id.textview_info);
mInfo.setOnClickListener(this);
}
public void setDuelOpsListener(OnDuelOptionsSelectListener listener) {
mListener = listener;
}
@Override
public void onClick(View v) {
mListener.onDuelOptionsSelected(MODE_CANCEL_CHAIN_OPTIONS, true);
}
}
package cn.garymb.ygomobile.widget.overlay;
import android.content.Context;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ToggleButton;
import cn.garymb.ygomobile.lib.R;
import cn.garymb.ygomobile.widget.overlay.OverlayOvalView.OnDuelOptionsSelectListener;
public class OverlayRectView extends OverlayView implements OnCheckedChangeListener {
private ToggleButton mReactButton;
private ToggleButton mIgnoreButton;
private OnDuelOptionsSelectListener mListener;
public OverlayRectView(Context context) {
super(context, R.layout.overlay_rect);
}
@Override
protected void onInflateView() {
super.onInflateView();
mIgnoreButton= (ToggleButton) findViewById(R.id.overlay_ignore);
mIgnoreButton.setOnCheckedChangeListener(this);
mReactButton = (ToggleButton) findViewById(R.id.overlay_react);
mReactButton.setOnCheckedChangeListener(this);
}
public void setDuelOpsListener(OnDuelOptionsSelectListener listener) {
mListener = listener;
}
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (buttonView.equals(mIgnoreButton)) {
if (isChecked && mReactButton.isChecked()) {
mReactButton.setChecked(false);
mListener.onDuelOptionsSelected(MODE_REACT_CHAIN_OPTION, false);
}
mListener.onDuelOptionsSelected(MODE_IGNORE_CHAIN_OPTION, isChecked);
} else {
if (isChecked && mIgnoreButton.isChecked()) {
mIgnoreButton.setChecked(false);
mListener.onDuelOptionsSelected(MODE_IGNORE_CHAIN_OPTION, false);
}
mListener.onDuelOptionsSelected(MODE_REACT_CHAIN_OPTION, isChecked);
}
}
}
/*
Copyright 2011 jawsware international
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cn.garymb.ygomobile.widget.overlay;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.RelativeLayout;
public abstract class OverlayView extends RelativeLayout {
public static final int MODE_CANCEL_CHAIN_OPTIONS = 0;
public static final int MODE_REFRESH_OPTION = 1;
public static final int MODE_IGNORE_CHAIN_OPTION = 2;
public static final int MODE_REACT_CHAIN_OPTION = 3;
protected WindowManager.LayoutParams layoutParams;
private int layoutResId;
private boolean mIsAdded = false;
public OverlayView(Context context, int layoutResId) {
super(context);
this.layoutResId = layoutResId;
this.setLongClickable(true);
this.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return onTouchEvent_LongPress();
}
});
inflateView();
}
/*
* (non-Javadoc)
*
* @see android.view.View#getLayoutParams()
*/
@Override
public android.view.ViewGroup.LayoutParams getLayoutParams() {
// TODO Auto-generated method stub
return layoutParams;
}
private void setupLayoutParams(int x, int y) {
layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT>=19?
WindowManager.LayoutParams.TYPE_TOAST:WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM;
layoutParams.y = y;
layoutParams.x = x;
onSetupLayoutParams();
}
protected void onSetupLayoutParams() {
// Override this to modify the initial LayoutParams. Be sure to call
// super.setupLayoutParams() first.
}
private void inflateView() {
// Inflates the layout resource, sets up the LayoutParams and adds the
// View to the WindowManager service.
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(layoutResId, this);
onInflateView();
}
protected void onInflateView() {
// Override this to make calls to findViewById() to setup references to
// the views that were inflated.
// This is called automatically when the object is created right after
// the resource is inflated.
}
public boolean isVisible() {
// Override this method to control when the Overlay is visible without
// destroying it.
return true;
}
public void refreshLayout() {
// Call this to force the updating of the view's layout.
if (isVisible()) {
removeAllViews();
inflateView();
onSetupLayoutParams();
((WindowManager) getContext().getSystemService(
Context.WINDOW_SERVICE)).updateViewLayout(this,
layoutParams);
refresh();
}
}
protected void addView(int x, int y) {
setupLayoutParams(x, y);
((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
.addView(this, layoutParams);
super.setVisibility(View.GONE);
}
public void showAtScreen(int x, int y) {
if (!mIsAdded) {
addView(x, y);
refresh();
mIsAdded = true;
}
}
public void removeFromScreen() {
if (mIsAdded) {
((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
.removeView(this);
mIsAdded = false;
}
}
public void refresh() {
// Call this to update the contents of the Overlay.
if (!isVisible()) {
setVisibility(View.GONE);
} else {
setVisibility(View.VISIBLE);
refreshViews();
}
}
protected void refreshViews() {
// Override this method to refresh the views inside of the Overlay. Only
// called when Overlay is visible.
}
protected boolean showNotificationHidden() {
// Override this to configure the notification to remain even when the
// overlay is invisible.
return true;
}
protected boolean onVisibilityToChange(int visibility) {
// Catch changes to the Overlay's visibility in order to animate
return true;
}
protected View animationView() {
return this;
}
public void hide() {
// Set visibility, but bypass onVisibilityToChange()
super.setVisibility(View.GONE);
}
public void show() {
// Set visibility, but bypass onVisibilityToChange()
super.setVisibility(View.VISIBLE);
}
@Override
public void setVisibility(int visibility) {
if (getVisibility() != visibility) {
if (onVisibilityToChange(visibility)) {
super.setVisibility(visibility);
}
}
}
protected int getLeftOnScreen() {
int[] location = new int[2];
getLocationOnScreen(location);
return location[0];
}
protected int getTopOnScreen() {
int[] location = new int[2];
getLocationOnScreen(location);
return location[1];
}
protected boolean isInside(View view, int x, int y) {
// Use this to test if the X, Y coordinates of the MotionEvent are
// inside of the View specified.
int[] location = new int[2];
view.getLocationOnScreen(location);
if (x >= location[0]) {
if (x <= location[0] + view.getWidth()) {
if (y >= location[1]) {
if (y <= location[1] + view.getHeight()) {
return true;
}
}
}
}
return false;
}
protected void onTouchEvent_Up(MotionEvent event) {
}
protected void onTouchEvent_Move(MotionEvent event) {
}
protected void onTouchEvent_Press(MotionEvent event) {
}
protected void onTouchEvent_Cancel(MotionEvent event) {
}
public boolean onTouchEvent_LongPress() {
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
onTouchEvent_Press(event);
} else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
onTouchEvent_Up(event);
} else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
onTouchEvent_Move(event);
} else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
onTouchEvent_Cancel(event);
}
return super.onTouchEvent(event);
}
}
package cn.garymb.ygomobile.widget.wheelview;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import java.util.LinkedList;
import java.util.List;
/**
* Abstract Wheel adapter.
*/
public abstract class AbstractWheelAdapter implements WheelViewAdapter {
// Observers
private List<DataSetObserver> datasetObservers;
public View getEmptyItem(View convertView, ViewGroup parent) {
return null;
}
public void registerDataSetObserver(DataSetObserver observer) {
if (datasetObservers == null) {
datasetObservers = new LinkedList<DataSetObserver>();
}
datasetObservers.add(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
if (datasetObservers != null) {
datasetObservers.remove(observer);
}
}
/**
* Notifies observers about data changing
*/
protected void notifyDataChangedEvent() {
if (datasetObservers != null) {
for (DataSetObserver observer : datasetObservers) {
observer.onChanged();
}
}
}
/**
* Notifies observers about invalidating data
*/
protected void notifyDataInvalidatedEvent() {
if (datasetObservers != null) {
for (DataSetObserver observer : datasetObservers) {
observer.onInvalidated();
}
}
}
}
\ No newline at end of file
package cn.garymb.ygomobile.widget.wheelview;
/*
* Copyright 2011 Yuri Kanivets
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.content.Context;
import android.graphics.Typeface;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* Abstract wheel adapter provides common functionality for adapters.
*/
public abstract class AbstractWheelTextAdapter extends AbstractWheelAdapter {
/** Text view resource. Used as a default view for adapter. */
public static final int TEXT_VIEW_ITEM_RESOURCE = -1;
/** No resource constant. */
protected static final int NO_RESOURCE = 0;
/** Default text color */
public static final int DEFAULT_TEXT_COLOR = 0xFF101010;
/** Default text color */
public static final int LABEL_COLOR = 0xFF700070;
public static final int CONFIG_TEXT_COLOR = 0xFF6699ff;
/** Default text size */
public static final int DEFAULT_TEXT_SIZE = 24;
// Text settings
private int textColor = DEFAULT_TEXT_COLOR;
private int textSize = DEFAULT_TEXT_SIZE;
private String textType = "";
// Current context
protected Context context;
// Layout inflater
protected LayoutInflater inflater;
// Items resources
protected int itemResourceId;
protected int itemTextResourceId;
// Empty items resources
protected int emptyItemResourceId;
public String getTextType() {
return textType;
}
public void setTextType(String textType) {
this.textType = textType;
}
/**
* Constructor
*
* @param context
* the current context
*/
protected AbstractWheelTextAdapter(Context context) {
this(context, TEXT_VIEW_ITEM_RESOURCE);
}
/**
* Constructor
*
* @param context
* the current context
* @param itemResource
* the resource ID for a layout file containing a TextView to use
* when instantiating items views
*/
protected AbstractWheelTextAdapter(Context context, int itemResource) {
this(context, itemResource, NO_RESOURCE);
}
/**
* Constructor
*
* @param context
* the current context
* @param itemResource
* the resource ID for a layout file containing a TextView to use
* when instantiating items views
* @param itemTextResource
* the resource ID for a text view in the item layout
*/
protected AbstractWheelTextAdapter(Context context, int itemResource,
int itemTextResource) {
this.context = context;
itemResourceId = itemResource;
itemTextResourceId = itemTextResource;
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
/**
* Gets text color
*
* @return the text color
*/
public int getTextColor() {
return textColor;
}
/**
* Sets text color
*
* @param textColor
* the text color to set
*/
public void setTextColor(int textColor) {
this.textColor = textColor;
}
/**
* Gets text size
*
* @return the text size
*/
public int getTextSize() {
return textSize;
}
/**
* Sets text size
*
* @param textSize
* the text size to set
*/
public void setTextSize(int textSize) {
this.textSize = textSize;
}
/**
* Gets resource Id for items views
*
* @return the item resource Id
*/
public int getItemResource() {
return itemResourceId;
}
/**
* Sets resource Id for items views
*
* @param itemResourceId
* the resource Id to set
*/
public void setItemResource(int itemResourceId) {
this.itemResourceId = itemResourceId;
}
/**
* Gets resource Id for text view in item layout
*
* @return the item text resource Id
*/
public int getItemTextResource() {
return itemTextResourceId;
}
/**
* Sets resource Id for text view in item layout
*
* @param itemTextResourceId
* the item text resource Id to set
*/
public void setItemTextResource(int itemTextResourceId) {
this.itemTextResourceId = itemTextResourceId;
}
/**
* Gets resource Id for empty items views
*
* @return the empty item resource Id
*/
public int getEmptyItemResource() {
return emptyItemResourceId;
}
/**
* Sets resource Id for empty items views
*
* @param emptyItemResourceId
* the empty item resource Id to set
*/
public void setEmptyItemResource(int emptyItemResourceId) {
this.emptyItemResourceId = emptyItemResourceId;
}
/**
* Returns text for specified item
*
* @param index
* the item index
* @return the text of specified items
*/
protected abstract CharSequence getItemText(int index);
public View getItem(int index, View convertView, ViewGroup parent) {
if (index >= 0 && index < getItemsCount()) {
if (convertView == null) {
convertView = getView(itemResourceId, parent);
}
TextView textView = getTextView(convertView, itemTextResourceId);
textView.setPadding(0, 6, 0, 6);
if (textView != null) {
CharSequence text = getItemText(index);
if (text == null) {
text = "";
}
textView.setText(text);
if (itemResourceId == TEXT_VIEW_ITEM_RESOURCE) {
configureTextView(textView);
}
}
return convertView;
}
return null;
}
@Override
public View getEmptyItem(View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getView(emptyItemResourceId, parent);
}
if (emptyItemResourceId == TEXT_VIEW_ITEM_RESOURCE
&& convertView instanceof TextView) {
configureTextView((TextView) convertView);
}
return convertView;
}
/**
* Configures text view. Is called for the TEXT_VIEW_ITEM_RESOURCE views.
*
* @param view
* the text view to be configured
*/
protected void configureTextView(TextView view) {
view.setTextColor(textColor);
view.setGravity(Gravity.CENTER);
view.setTextSize(textSize);
view.setLines(1);
view.setText(view.getText()+textType);
view.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
}
/**
* Loads a text view from view
*
* @param view
* the text view or layout containing it
* @param textResource
* the text resource Id in layout
* @return the loaded text view
*/
private TextView getTextView(View view, int textResource) {
TextView text = null;
try {
if (textResource == NO_RESOURCE && view instanceof TextView) {
text = (TextView) view;
} else if (textResource != NO_RESOURCE) {
text = (TextView) view.findViewById(textResource);
}
} catch (ClassCastException e) {
Log.e("AbstractWheelAdapter",
"You must supply a resource ID for a TextView");
throw new IllegalStateException(
"AbstractWheelAdapter requires the resource ID to be a TextView",
e);
}
return text;
}
/**
* Loads view from resources
*
* @param resource
* the resource Id
* @return the loaded view or null if resource is not set
*/
private View getView(int resource, ViewGroup parent) {
switch (resource) {
case NO_RESOURCE:
return null;
case TEXT_VIEW_ITEM_RESOURCE:
return new TextView(context);
default:
return inflater.inflate(resource, parent, false);
}
}
}
\ No newline at end of file
package cn.garymb.ygomobile.widget.wheelview;
import android.content.Context;
/**
* Adapter class for old wheel adapter (deprecated WheelAdapter class).
*
* @deprecated Will be removed soon
*/
public class AdapterWheel extends AbstractWheelTextAdapter {
// Source adapter
private WheelAdapter adapter;
/**
* Constructor
*
* @param context
* the current context
* @param adapter
* the source adapter
*/
public AdapterWheel(Context context, WheelAdapter adapter) {
super(context);
this.adapter = adapter;
}
/**
* Gets original adapter
*
* @return the original adapter
*/
public WheelAdapter getAdapter() {
return adapter;
}
public int getItemsCount() {
return adapter.getItemsCount();
}
@Override
protected CharSequence getItemText(int index) {
return adapter.getItem(index);
}
}
\ No newline at end of file
package cn.garymb.ygomobile.widget.wheelview;
import android.content.Context;
/**
* The simple Array wheel adapter
*
* @param <T>
* the element type
*/
public class ArrayWheelAdapter<T> extends AbstractWheelTextAdapter {
// items
private T items[];
/**
* Constructor
*
* @param context
* the current context
* @param items
* the items
*/
public ArrayWheelAdapter(Context context, T items[]) {
super(context);
// setEmptyItemResource(TEXT_VIEW_ITEM_RESOURCE);
this.items = items;
}
@Override
public CharSequence getItemText(int index) {
if (index >= 0 && index < items.length) {
T item = items[index];
if (item instanceof CharSequence) {
return (CharSequence) item;
}
return item.toString();
}
return null;
}
public int getItemsCount() {
return items.length;
}
}
\ No newline at end of file
package cn.garymb.ygomobile.widget.wheelview;
public class ItemsRange {
// First item number
private int first;
// Items count
private int count;
/**
* Default constructor. Creates an empty range
*/
public ItemsRange() {
this(0, 0);
}
/**
* Constructor
*
* @param first
* the number of first item
* @param count
* the count of items
*/
public ItemsRange(int first, int count) {
this.first = first;
this.count = count;
}
/**
* Gets number of first item
*
* @return the number of the first item
*/
public int getFirst() {
return first;
}
/**
* Gets number of last item
*
* @return the number of last item
*/
public int getLast() {
return getFirst() + getCount() - 1;
}
/**
* Get items count
*
* @return the count of items
*/
public int getCount() {
return count;
}
/**
* Tests whether item is contained by range
*
* @param index
* the item number
* @return true if item is contained
*/
public boolean contains(int index) {
return index >= getFirst() && index <= getLast();
}
}
\ No newline at end of file
package cn.garymb.ygomobile.widget.wheelview;
public interface OnWheelChangedListener {
void onChanged(WheelView wheel, int oldValue, int newValue);
}
package cn.garymb.ygomobile.widget.wheelview;
public interface OnWheelClickedListener {
/**
* Callback method to be invoked when current item clicked
*
* @param wheel
* the wheel view
* @param itemIndex
* the index of clicked item
*/
void onItemClicked(WheelView wheel, int itemIndex);
}
package cn.garymb.ygomobile.widget.wheelview;
public interface OnWheelScrollListener {
/**
* Callback method to be invoked when scrolling started.
*
* @param wheel
* the wheel view whose state has changed.
*/
void onScrollingStarted(WheelView wheel);
/**
* Callback method to be invoked when scrolling ended.
*
* @param wheel
* the wheel view whose state has changed.
*/
void onScrollingFinished(WheelView wheel);
}
package cn.garymb.ygomobile.widget.wheelview;
public interface WheelAdapter {
/**
* Gets items count
*
* @return the count of wheel items
*/
public int getItemsCount();
/**
* Gets a wheel item by index.
*
* @param index
* the item index
* @return the wheel item text or null
*/
public String getItem(int index);
/**
* Gets maximum item length. It is used to determine the wheel width. If -1
* is returned there will be used the default wheel width.
*
* @return the maximum item length or -1
*/
public int getMaximumLength();
}
\ No newline at end of file
package cn.garymb.ygomobile.widget.wheelview;
import android.view.View;
import android.widget.LinearLayout;
import java.util.LinkedList;
import java.util.List;
/**
* Recycle stores wheel items to reuse.
*/
public class WheelRecycle {
// Cached items
private List<View> items;
// Cached empty items
private List<View> emptyItems;
// Wheel view
private WheelView wheel;
/**
* Constructor
*
* @param wheel
* the wheel view
*/
public WheelRecycle(WheelView wheel) {
this.wheel = wheel;
}
/**
* Recycles items from specified layout. There are saved only items not
* included to specified range. All the cached items are removed from
* original layout.
*
* @param layout
* the layout containing items to be cached
* @param firstItem
* the number of first item in layout
* @param range
* the range of current wheel items
* @return the new value of first item number
*/
public int recycleItems(LinearLayout layout, int firstItem, ItemsRange range) {
int index = firstItem;
for (int i = 0; i < layout.getChildCount();) {
if (!range.contains(index)) {
recycleView(layout.getChildAt(i), index);
layout.removeViewAt(i);
if (i == 0) { // first item
firstItem++;
}
} else {
i++; // go to next item
}
index++;
}
return firstItem;
}
/**
* Gets item view
*
* @return the cached view
*/
public View getItem() {
return getCachedView(items);
}
/**
* Gets empty item view
*
* @return the cached empty view
*/
public View getEmptyItem() {
return getCachedView(emptyItems);
}
/**
* Clears all views
*/
public void clearAll() {
if (items != null) {
items.clear();
}
if (emptyItems != null) {
emptyItems.clear();
}
}
/**
* Adds view to specified cache. Creates a cache list if it is null.
*
* @param view
* the view to be cached
* @param cache
* the cache list
* @return the cache list
*/
private List<View> addView(View view, List<View> cache) {
if (cache == null) {
cache = new LinkedList<View>();
}
cache.add(view);
return cache;
}
/**
* Adds view to cache. Determines view type (item view or empty one) by
* index.
*
* @param view
* the view to be cached
* @param index
* the index of view
*/
private void recycleView(View view, int index) {
int count = wheel.getViewAdapter().getItemsCount();
if ((index < 0 || index >= count) && !wheel.isCyclic()) {
// empty view
emptyItems = addView(view, emptyItems);
} else {
while (index < 0) {
index = count + index;
}
index %= count;
items = addView(view, items);
}
}
/**
* Gets view from specified cache.
*
* @param cache
* the cache
* @return the first view from cache.
*/
private View getCachedView(List<View> cache) {
if (cache != null && cache.size() > 0) {
View view = cache.get(0);
cache.remove(0);
return view;
}
return null;
}
}
package cn.garymb.ygomobile.widget.wheelview;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
import android.widget.Scroller;
/**
* Scroller class handles scrolling events and updates the
*/
public class WheelScroller {
/**
* Scrolling listener interface
*/
public interface ScrollingListener {
/**
* Scrolling callback called when scrolling is performed.
*
* @param distance
* the distance to scroll
*/
void onScroll(int distance);
/**
* Starting callback called when scrolling is started
*/
void onStarted();
/**
* Finishing callback called after justifying
*/
void onFinished();
/**
* Justifying callback called to justify a view when scrolling is ended
*/
void onJustify();
}
/** Scrolling duration */
private static final int SCROLLING_DURATION = 400;
/** Minimum delta for scrolling */
public static final int MIN_DELTA_FOR_SCROLLING = 1;
// Listener
private ScrollingListener listener;
// Context
private Context context;
// Scrolling
private GestureDetector gestureDetector;
private Scroller scroller;
private int lastScrollY;
private float lastTouchedY;
private boolean isScrollingPerformed;
/**
* Constructor
*
* @param context
* the current context
* @param listener
* the scrolling listener
*/
public WheelScroller(Context context, ScrollingListener listener) {
gestureDetector = new GestureDetector(context, gestureListener);
gestureDetector.setIsLongpressEnabled(false);
scroller = new Scroller(context);
this.listener = listener;
this.context = context;
}
/**
* Set the the specified scrolling interpolator
*
* @param interpolator
* the interpolator
*/
public void setInterpolator(Interpolator interpolator) {
scroller.forceFinished(true);
scroller = new Scroller(context, interpolator);
}
/**
* Scroll the wheel
*
* @param distance
* the scrolling distance
* @param time
* the scrolling duration
*/
public void scroll(int distance, int time) {
scroller.forceFinished(true);
lastScrollY = 0;
scroller.startScroll(0, 0, 0, distance, time != 0 ? time
: SCROLLING_DURATION);
setNextMessage(MESSAGE_SCROLL);
startScrolling();
}
/**
* Stops scrolling
*/
public void stopScrolling() {
scroller.forceFinished(true);
}
/**
* Handles Touch event
*
* @param event
* the motion event
* @return
*/
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastTouchedY = event.getY();
scroller.forceFinished(true);
clearMessages();
break;
case MotionEvent.ACTION_MOVE:
// perform scrolling
int distanceY = (int) (event.getY() - lastTouchedY);
if (distanceY != 0) {
startScrolling();
listener.onScroll(distanceY);
lastTouchedY = event.getY();
}
break;
}
if (!gestureDetector.onTouchEvent(event)
&& event.getAction() == MotionEvent.ACTION_UP) {
justify();
}
return true;
}
// gesture listener
private SimpleOnGestureListener gestureListener = new SimpleOnGestureListener() {
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// Do scrolling in onTouchEvent() since onScroll() are not call
// immediately
// when user touch and move the wheel
return true;
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
lastScrollY = 0;
final int maxY = 0x7FFFFFFF;
final int minY = -maxY;
scroller.fling(0, lastScrollY, 0, (int) -velocityY, 0, 0, minY,
maxY);
setNextMessage(MESSAGE_SCROLL);
return true;
}
};
// Messages
private final int MESSAGE_SCROLL = 0;
private final int MESSAGE_JUSTIFY = 1;
/**
* Set next message to queue. Clears queue before.
*
* @param message
* the message to set
*/
private void setNextMessage(int message) {
clearMessages();
animationHandler.sendEmptyMessage(message);
}
/**
* Clears messages from queue
*/
private void clearMessages() {
animationHandler.removeMessages(MESSAGE_SCROLL);
animationHandler.removeMessages(MESSAGE_JUSTIFY);
}
// animation handler
private Handler animationHandler = new Handler() {
public void handleMessage(Message msg) {
scroller.computeScrollOffset();
int currY = scroller.getCurrY();
int delta = lastScrollY - currY;
lastScrollY = currY;
if (delta != 0) {
listener.onScroll(delta);
}
// scrolling is not finished when it comes to final Y
// so, finish it manually
if (Math.abs(currY - scroller.getFinalY()) < MIN_DELTA_FOR_SCROLLING) {
currY = scroller.getFinalY();
scroller.forceFinished(true);
}
if (!scroller.isFinished()) {
animationHandler.sendEmptyMessage(msg.what);
} else if (msg.what == MESSAGE_SCROLL) {
justify();
} else {
finishScrolling();
}
}
};
/**
* Justifies wheel
*/
private void justify() {
listener.onJustify();
setNextMessage(MESSAGE_JUSTIFY);
}
/**
* Starts scrolling
*/
private void startScrolling() {
if (!isScrollingPerformed) {
isScrollingPerformed = true;
listener.onStarted();
}
}
/**
* Finishes scrolling
*/
void finishScrolling() {
if (isScrollingPerformed) {
listener.onFinished();
isScrollingPerformed = false;
}
}
}
package cn.garymb.ygomobile.widget.wheelview;
import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import java.util.LinkedList;
import java.util.List;
import cn.garymb.ygomobile.lib.R;
/**
* Numeric wheel view.
*
* @author Yuri Kanivets
*/
public class WheelView extends View {
/** Top and bottom shadows colors */
private static final int[] SHADOWS_COLORS = new int[] { 0xFF111111,
0x00AAAAAA, 0x00AAAAAA };
/** Top and bottom items offset (to hide that) */
private static final int ITEM_OFFSET_PERCENT = 10;
/** Left and right padding value */
private static final int PADDING = 5;
/** Default count of visible items */
private static final int DEF_VISIBLE_ITEMS = 5;
// Wheel Values
private int currentItem = 0;
// Count of visible items
private int visibleItems = DEF_VISIBLE_ITEMS;
// Item height
private int itemHeight = 0;
// Center Line
private Drawable centerDrawable;
// Shadows drawables
private GradientDrawable topShadow;
private GradientDrawable bottomShadow;
// Scrolling
private WheelScroller scroller;
private boolean isScrollingPerformed;
private int scrollingOffset;
// Cyclic
boolean isCyclic = false;
// Items layout
private LinearLayout itemsLayout;
// The number of first item in layout
private int firstItem;
// View adapter
private WheelViewAdapter viewAdapter;
// Recycle
private WheelRecycle recycle = new WheelRecycle(this);
// Listeners
private List<OnWheelChangedListener> changingListeners = new LinkedList<OnWheelChangedListener>();
private List<OnWheelScrollListener> scrollingListeners = new LinkedList<OnWheelScrollListener>();
private List<OnWheelClickedListener> clickingListeners = new LinkedList<OnWheelClickedListener>();
/**
* Constructor
*/
public WheelView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initData(context);
}
/**
* Constructor
*/
public WheelView(Context context, AttributeSet attrs) {
super(context, attrs);
initData(context);
}
/**
* Constructor
*/
public WheelView(Context context) {
super(context);
initData(context);
}
/**
* Initializes class data
*
* @param context
* the context
*/
private void initData(Context context) {
scroller = new WheelScroller(getContext(), scrollingListener);
}
// Scrolling listener
WheelScroller.ScrollingListener scrollingListener = new WheelScroller.ScrollingListener() {
public void onStarted() {
isScrollingPerformed = true;
notifyScrollingListenersAboutStart();
}
public void onScroll(int distance) {
doScroll(distance);
int height = getHeight();
if (scrollingOffset > height) {
scrollingOffset = height;
scroller.stopScrolling();
} else if (scrollingOffset < -height) {
scrollingOffset = -height;
scroller.stopScrolling();
}
}
public void onFinished() {
if (isScrollingPerformed) {
notifyScrollingListenersAboutEnd();
isScrollingPerformed = false;
}
scrollingOffset = 0;
invalidate();
}
public void onJustify() {
if (Math.abs(scrollingOffset) > WheelScroller.MIN_DELTA_FOR_SCROLLING) {
scroller.scroll(scrollingOffset, 0);
}
}
};
/**
* Set the the specified scrolling interpolator
*
* @param interpolator
* the interpolator
*/
public void setInterpolator(Interpolator interpolator) {
scroller.setInterpolator(interpolator);
}
/**
* Gets count of visible items
*
* @return the count of visible items
*/
public int getVisibleItems() {
return visibleItems;
}
/**
* Sets the desired count of visible items. Actual amount of visible items
* depends on wheel layout parameters. To apply changes and rebuild view
* call measure().
*
* @param count
* the desired count for visible items
*/
public void setVisibleItems(int count) {
visibleItems = count;
}
/**
* Gets view adapter
*
* @return the view adapter
*/
public WheelViewAdapter getViewAdapter() {
return viewAdapter;
}
// Adapter listener
private DataSetObserver dataObserver = new DataSetObserver() {
@Override
public void onChanged() {
invalidateWheel(false);
}
@Override
public void onInvalidated() {
invalidateWheel(true);
}
};
/**
* Sets view adapter. Usually new adapters contain different views, so it
* needs to rebuild view by calling measure().
*
* @param viewAdapter
* the view adapter
*/
public void setViewAdapter(WheelViewAdapter viewAdapter) {
if (this.viewAdapter != null) {
this.viewAdapter.unregisterDataSetObserver(dataObserver);
}
this.viewAdapter = viewAdapter;
if (this.viewAdapter != null) {
this.viewAdapter.registerDataSetObserver(dataObserver);
}
invalidateWheel(true);
}
/**
* Adds wheel changing listener
*
* @param listener
* the listener
*/
public void addChangingListener(OnWheelChangedListener listener) {
changingListeners.add(listener);
}
/**
* Removes wheel changing listener
*
* @param listener
* the listener
*/
public void removeChangingListener(OnWheelChangedListener listener) {
changingListeners.remove(listener);
}
/**
* Notifies changing listeners
*
* @param oldValue
* the old wheel value
* @param newValue
* the new wheel value
*/
protected void notifyChangingListeners(int oldValue, int newValue) {
for (OnWheelChangedListener listener : changingListeners) {
listener.onChanged(this, oldValue, newValue);
}
}
/**
* Adds wheel scrolling listener
*
* @param listener
* the listener
*/
public void addScrollingListener(OnWheelScrollListener listener) {
scrollingListeners.add(listener);
}
/**
* Removes wheel scrolling listener
*
* @param listener
* the listener
*/
public void removeScrollingListener(OnWheelScrollListener listener) {
scrollingListeners.remove(listener);
}
/**
* Notifies listeners about starting scrolling
*/
protected void notifyScrollingListenersAboutStart() {
for (OnWheelScrollListener listener : scrollingListeners) {
listener.onScrollingStarted(this);
}
}
/**
* Notifies listeners about ending scrolling
*/
protected void notifyScrollingListenersAboutEnd() {
for (OnWheelScrollListener listener : scrollingListeners) {
listener.onScrollingFinished(this);
}
}
/**
* Adds wheel clicking listener
*
* @param listener
* the listener
*/
public void addClickingListener(OnWheelClickedListener listener) {
clickingListeners.add(listener);
}
/**
* Removes wheel clicking listener
*
* @param listener
* the listener
*/
public void removeClickingListener(OnWheelClickedListener listener) {
clickingListeners.remove(listener);
}
/**
* Notifies listeners about clicking
*/
protected void notifyClickListenersAboutClick(int item) {
for (OnWheelClickedListener listener : clickingListeners) {
listener.onItemClicked(this, item);
}
}
/**
* Gets current value
*
* @return the current value
*/
public int getCurrentItem() {
return currentItem;
}
/**
* Sets the current item. Does nothing when index is wrong.
*
* @param index
* the item index
* @param animated
* the animation flag
*/
public void setCurrentItem(int index, boolean animated) {
if (viewAdapter == null || viewAdapter.getItemsCount() == 0) {
return; // throw?
}
int itemCount = viewAdapter.getItemsCount();
if (index < 0 || index >= itemCount) {
if (isCyclic) {
while (index < 0) {
index += itemCount;
}
index %= itemCount;
} else {
return; // throw?
}
}
if (index != currentItem) {
if (animated) {
int itemsToScroll = index - currentItem;
if (isCyclic) {
int scroll = itemCount + Math.min(index, currentItem)
- Math.max(index, currentItem);
if (scroll < Math.abs(itemsToScroll)) {
itemsToScroll = itemsToScroll < 0 ? scroll : -scroll;
}
}
scroll(itemsToScroll, 0);
} else {
scrollingOffset = 0;
int old = currentItem;
currentItem = index;
notifyChangingListeners(old, currentItem);
invalidate();
}
}
}
/**
* Sets the current item w/o animation. Does nothing when index is wrong.
*
* @param index
* the item index
*/
public void setCurrentItem(int index) {
setCurrentItem(index, false);
}
/**
* Tests if wheel is cyclic. That means before the 1st item there is shown
* the last one
*
* @return true if wheel is cyclic
*/
public boolean isCyclic() {
return isCyclic;
}
/**
* Set wheel cyclic flag
*
* @param isCyclic
* the flag to set
*/
public void setCyclic(boolean isCyclic) {
this.isCyclic = isCyclic;
invalidateWheel(false);
}
/**
* Invalidates wheel
*
* @param clearCaches
* if true then cached views will be clear
*/
public void invalidateWheel(boolean clearCaches) {
if (clearCaches) {
recycle.clearAll();
if (itemsLayout != null) {
itemsLayout.removeAllViews();
}
scrollingOffset = 0;
} else if (itemsLayout != null) {
// cache all items
recycle.recycleItems(itemsLayout, firstItem, new ItemsRange());
}
invalidate();
}
/**
* Initializes resources
*/
private void initResourcesIfNecessary() {
if (centerDrawable == null) {
centerDrawable = getContext().getResources().getDrawable(
R.drawable.com_ttshrk_view_scroll_picker_bar);
}
if (topShadow == null) {
topShadow = new GradientDrawable(Orientation.TOP_BOTTOM,
SHADOWS_COLORS);
}
if (bottomShadow == null) {
bottomShadow = new GradientDrawable(Orientation.BOTTOM_TOP,
SHADOWS_COLORS);
}
setBackgroundResource(R.drawable.wheel_bg);
}
/**
* Calculates desired height for layout
*
* @param layout
* the source layout
* @return the desired layout height
*/
private int getDesiredHeight(LinearLayout layout) {
if (layout != null && layout.getChildAt(0) != null) {
itemHeight = layout.getChildAt(0).getMeasuredHeight();
}
int desired = itemHeight * visibleItems - itemHeight
* ITEM_OFFSET_PERCENT / 50;
return Math.max(desired, getSuggestedMinimumHeight());
}
/**
* Returns height of wheel item
*
* @return the item height
*/
private int getItemHeight() {
if (itemHeight != 0) {
return itemHeight;
}
if (itemsLayout != null && itemsLayout.getChildAt(0) != null) {
itemHeight = itemsLayout.getChildAt(0).getHeight();
return itemHeight;
}
return getHeight() / visibleItems;
}
/**
* Calculates control width and creates text layouts
*
* @param widthSize
* the input layout width
* @param mode
* the layout mode
* @return the calculated control width
*/
private int calculateLayoutWidth(int widthSize, int mode) {
initResourcesIfNecessary();
// TODO: make it static
itemsLayout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
50));
itemsLayout
.measure(MeasureSpec.makeMeasureSpec(widthSize,
MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(
0, MeasureSpec.UNSPECIFIED));
int width = itemsLayout.getMeasuredWidth();
if (mode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width += 2 * PADDING;
// Check against our minimum width
width = Math.max(width, getSuggestedMinimumWidth());
if (mode == MeasureSpec.AT_MOST && widthSize < width) {
width = widthSize;
}
}
itemsLayout.measure(MeasureSpec.makeMeasureSpec(width - 2 * PADDING,
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0,
MeasureSpec.UNSPECIFIED));
return width;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
buildViewForMeasuring();
int width = calculateLayoutWidth(widthSize, widthMode);
int height;
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = getDesiredHeight(itemsLayout);
if (heightMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightSize);
}
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
layout(r - l, b - t);
}
/**
* Sets layouts width and height
*
* @param width
* the layout width
* @param height
* the layout height
*/
private void layout(int width, int height) {
int itemsWidth = width - 2 * PADDING;
itemsLayout.layout(0, 0, itemsWidth, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (viewAdapter != null && viewAdapter.getItemsCount() > 0) {
updateView();
drawItems(canvas);
drawCenterRect(canvas);
}
drawShadows(canvas);
}
/**
* Draws shadows on top and bottom of control
*
* @param canvas
* the canvas for drawing
*/
private void drawShadows(Canvas canvas) {
int height = (int) (1.5 * getItemHeight());
topShadow.setBounds(0, 0, getWidth(), height);
topShadow.draw(canvas);
bottomShadow
.setBounds(0, getHeight() - height, getWidth(), getHeight());
bottomShadow.draw(canvas);
}
/**
* Draws items
*
* @param canvas
* the canvas for drawing
*/
private void drawItems(Canvas canvas) {
canvas.save();
int top = (currentItem - firstItem) * getItemHeight()
+ (getItemHeight() - getHeight()) / 2;
canvas.translate(PADDING, -top + scrollingOffset);
itemsLayout.draw(canvas);
canvas.restore();
}
/**
* Draws rect for current value
*
* @param canvas
* the canvas for drawing
*/
private void drawCenterRect(Canvas canvas) {
int center = getHeight() / 2;
int offset = (int) (getItemHeight() / 2 * 1.2);
centerDrawable.setBounds(0, center - offset, getWidth(), center
+ offset);
centerDrawable.draw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled() || getViewAdapter() == null) {
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
if (!isScrollingPerformed) {
int distance = (int) event.getY() - getHeight() / 2;
if (distance > 0) {
distance += getItemHeight() / 2;
} else {
distance -= getItemHeight() / 2;
}
int items = distance / getItemHeight();
if (items != 0 && isValidItemIndex(currentItem + items)) {
notifyClickListenersAboutClick(currentItem + items);
}
}
break;
}
return scroller.onTouchEvent(event);
}
/**
* Scrolls the wheel
*
* @param delta
* the scrolling value
*/
private void doScroll(int delta) {
scrollingOffset += delta;
int itemHeight = getItemHeight();
int count = scrollingOffset / itemHeight;
int pos = currentItem - count;
int itemCount = viewAdapter.getItemsCount();
int fixPos = scrollingOffset % itemHeight;
if (Math.abs(fixPos) <= itemHeight / 2) {
fixPos = 0;
}
if (isCyclic && itemCount > 0) {
if (fixPos > 0) {
pos--;
count++;
} else if (fixPos < 0) {
pos++;
count--;
}
// fix position by rotating
while (pos < 0) {
pos += itemCount;
}
pos %= itemCount;
} else {
//
if (pos < 0) {
count = currentItem;
pos = 0;
} else if (pos >= itemCount) {
count = currentItem - itemCount + 1;
pos = itemCount - 1;
} else if (pos > 0 && fixPos > 0) {
pos--;
count++;
} else if (pos < itemCount - 1 && fixPos < 0) {
pos++;
count--;
}
}
int offset = scrollingOffset;
if (pos != currentItem) {
setCurrentItem(pos, false);
} else {
invalidate();
}
// update offset
scrollingOffset = offset - count * itemHeight;
if (scrollingOffset > getHeight()) {
scrollingOffset = scrollingOffset % getHeight() + getHeight();
}
}
/**
* Scroll the wheel
*
* items to scroll
* @param time
* scrolling duration
*/
public void scroll(int itemsToScroll, int time) {
int distance = itemsToScroll * getItemHeight() - scrollingOffset;
scroller.scroll(distance, time);
}
/**
* Calculates range for wheel items
*
* @return the items range
*/
private ItemsRange getItemsRange() {
if (getItemHeight() == 0) {
return null;
}
int first = currentItem;
int count = 1;
while (count * getItemHeight() < getHeight()) {
first--;
count += 2; // top + bottom items
}
if (scrollingOffset != 0) {
if (scrollingOffset > 0) {
first--;
}
count++;
// process empty items above the first or below the second
int emptyItems = scrollingOffset / getItemHeight();
first -= emptyItems;
count += Math.asin(emptyItems);
}
return new ItemsRange(first, count);
}
/**
* Rebuilds wheel items if necessary. Caches all unused items.
*
* @return true if items are rebuilt
*/
private boolean rebuildItems() {
boolean updated = false;
ItemsRange range = getItemsRange();
if (itemsLayout != null) {
int first = recycle.recycleItems(itemsLayout, firstItem, range);
updated = firstItem != first;
firstItem = first;
} else {
createItemsLayout();
updated = true;
}
if (!updated) {
updated = firstItem != range.getFirst()
|| itemsLayout.getChildCount() != range.getCount();
}
if (firstItem > range.getFirst() && firstItem <= range.getLast()) {
for (int i = firstItem - 1; i >= range.getFirst(); i--) {
if (!addViewItem(i, true)) {
break;
}
firstItem = i;
}
} else {
firstItem = range.getFirst();
}
int first = firstItem;
for (int i = itemsLayout.getChildCount(); i < range.getCount(); i++) {
if (!addViewItem(firstItem + i, false)
&& itemsLayout.getChildCount() == 0) {
first++;
}
}
firstItem = first;
return updated;
}
/**
* Updates view. Rebuilds items and label if necessary, recalculate items
* sizes.
*/
private void updateView() {
if (rebuildItems()) {
calculateLayoutWidth(getWidth(), MeasureSpec.EXACTLY);
layout(getWidth(), getHeight());
}
}
/**
* Creates item layouts if necessary
*/
private void createItemsLayout() {
if (itemsLayout == null) {
itemsLayout = new LinearLayout(getContext());
itemsLayout.setOrientation(LinearLayout.VERTICAL);
}
}
/**
* Builds view for measuring
*/
private void buildViewForMeasuring() {
// clear all items
if (itemsLayout != null) {
recycle.recycleItems(itemsLayout, firstItem, new ItemsRange());
} else {
createItemsLayout();
}
// add views
int addItems = visibleItems / 2;
for (int i = currentItem + addItems; i >= currentItem - addItems; i--) {
if (addViewItem(i, true)) {
firstItem = i;
}
}
}
/**
* Adds view for item to items layout
*
* @param index
* the item index
* @param first
* the flag indicates if view should be first
* @return true if corresponding item exists and is added
*/
private boolean addViewItem(int index, boolean first) {
View view = getItemView(index);
if (view != null) {
if (first) {
itemsLayout.addView(view, 0);
} else {
itemsLayout.addView(view);
}
return true;
}
return false;
}
/**
* Checks whether intem index is valid
*
* @param index
* the item index
* @return true if item index is not out of bounds or the wheel is cyclic
*/
private boolean isValidItemIndex(int index) {
return viewAdapter != null
&& viewAdapter.getItemsCount() > 0
&& (isCyclic || index >= 0
&& index < viewAdapter.getItemsCount());
}
/**
* Returns view for specified item
*
* @param index
* the item index
* @return item view or empty view if index is out of bounds
*/
private View getItemView(int index) {
if (viewAdapter == null || viewAdapter.getItemsCount() == 0) {
return null;
}
int count = viewAdapter.getItemsCount();
if (!isValidItemIndex(index)) {
return viewAdapter
.getEmptyItem(recycle.getEmptyItem(), itemsLayout);
} else {
while (index < 0) {
index = count + index;
}
}
index %= count;
return viewAdapter.getItem(index, recycle.getItem(), itemsLayout);
}
/**
* Stops scrolling
*/
public void stopScrolling() {
scroller.stopScrolling();
}
}
\ No newline at end of file
package cn.garymb.ygomobile.widget.wheelview;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
/**
* Wheel items adapter interface
*/
public interface WheelViewAdapter {
/**
* Gets items count
*
* @return the count of wheel items
*/
public int getItemsCount();
/**
* Get a View that displays the data at the specified position in the data
* set
*
* @param index
* the item index
* @param convertView
* the old view to reuse if possible
* @param parent
* the parent that this view will eventually be attached to
* @return the wheel item View
*/
public View getItem(int index, View convertView, ViewGroup parent);
/**
* Get a View that displays an empty wheel item placed before the first or
* after the last wheel item.
*
* @param convertView
* the old view to reuse if possible
* @param parent
* the parent that this view will eventually be attached to
* @return the empty item View
*/
public View getEmptyItem(View convertView, ViewGroup parent);
/**
* Register an observer that is called when changes happen to the data used
* by this adapter.
*
* @param observer
* the observer to be registered
*/
public void registerDataSetObserver(DataSetObserver observer);
/**
* Unregister an observer that has previously been registered
*
* @param observer
* the observer to be unregistered
*/
void unregisterDataSetObserver(DataSetObserver observer);
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@android:color/white" />
<item android:state_activated="true" android:color="@android:color/white" />
<item android:color="#585858" />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="@color/navigator_dir_text_color_selected" />
<item android:color="@android:color/white" />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@drawable/mm_title_act_btn_focused" />
<item android:state_pressed="true" android:drawable="@drawable/mm_title_act_btn_pressed" />
<item android:state_selected="true" android:drawable="@drawable/mm_title_act_btn_pressed" />
<item android:drawable="@drawable/mm_title_act_btn_normal" />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@drawable/mm_title_btn_focused" />
<item android:state_pressed="true" android:drawable="@drawable/mm_title_btn_pressed" />
<item android:state_selected="true" android:drawable="@drawable/mm_title_btn_pressed" />
<item android:drawable="@drawable/mm_title_btn_normal" />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" android:drawable="@drawable/overlay_internal_pressed" />
<item android:drawable="@drawable/overlay_internal_normal" />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="@color/less_trasparent_dark_purple"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid
android:color="@color/dark_purple"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" android:drawable="@drawable/overlay_rect_state" />
<item android:state_selected="true" android:drawable="@drawable/overlay_rect_state" />
<item android:state_checked="true" android:drawable="@drawable/overlay_rect_state" />
<item android:drawable="@drawable/overlay_rect_normal" />
</selector>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="2dp"/>
<solid
android:color="@color/less_trasparent_dark_purple"/>
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="2dp"/>
<solid
android:color="@color/dark_purple"/>
</shape>
<?xml version="1.0" encoding="UTF-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<gradient
android:angle="90.0"
android:centerColor="#ffdddddd"
android:endColor="#ff333333"
android:startColor="#ff333333"/>
<stroke
android:width="1.0dip"
android:color="#ff333333"/>
</shape>
</item>
<item
android:bottom="1.0dip"
android:left="4.0dip"
android:right="4.0dip"
android:top="1.0dip">
<shape android:shape="rectangle">
<gradient
android:angle="90.0"
android:centerColor="#ffFCFCFC"
android:endColor="#ffFCFCFC"
android:startColor="#ffFCFCFC"/>
</shape>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/combobox_content_layout"
android:background="#ff424542"
android:orientation="horizontal" >
<TextView
android:id="@+id/combobox_category_label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:text="@string/combobox_choose_item"
android:textColor="@android:color/white" />
<Button
android:id="@+id/cancel"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:background="@drawable/mm_title_btn_right"
android:text="@android:string/cancel"
android:textColor="@android:color/white" />
<Button
android:id="@+id/submit"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="right"
android:background="@drawable/mm_title_act_btn"
android:text="@android:string/ok"
android:textColor="@android:color/white" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/combobox_content_layout"
android:layout_width="fill_parent"
android:layout_height="220dip"
android:layout_alignParentBottom="true"
android:gravity="center"
android:orientation="horizontal" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="220dip"
android:layout_marginLeft="15dip"
android:layout_marginRight="15dip"
android:gravity="center"
android:orientation="horizontal" >
<cn.garymb.ygomobile.widget.wheelview.WheelView
android:id="@+id/combobox_content"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
android:layout_weight="1" />
</LinearLayout>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="220.0dip"
android:layout_gravity="center"
android:background="@drawable/com_ttshrk_view_scroll_picker_background" >
</FrameLayout>
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp" >
<TextView
android:id="@+id/textview_info"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_centerInParent="true"
android:background="@drawable/overlay_internal"
android:clickable="false"
android:gravity="center"
android:text="@android:string/cancel"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="@color/text_color_list" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="0dp" >
<ToggleButton
android:id="@+id/overlay_ignore"
android:layout_width="@dimen/chain_control_button_width"
android:layout_height="@dimen/chain_control_button_height"
android:layout_alignParentTop="true"
android:background="@drawable/overlay_rect"
android:textSize="12sp"
android:clickable="true"
android:textOn="@string/ignore_chain"
android:textOff="@string/ignore_chain"
android:textColor="@color/text_color_list"
android:gravity="center" >
</ToggleButton>
<ToggleButton
android:id="@+id/overlay_react"
android:layout_width="@dimen/chain_control_button_width"
android:layout_height="@dimen/chain_control_button_height"
android:layout_marginTop="@dimen/chain_control_margin"
android:layout_below="@id/overlay_ignore"
android:background="@drawable/overlay_rect"
android:textSize="12sp"
android:clickable="true"
android:textOn="@string/react_chain"
android:textOff="@string/react_chain"
android:textColor="@color/text_color_list"
android:gravity="center" >
</ToggleButton>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scrollView1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:orientation="vertical" >
<EditText
android:id="@+id/global_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLength="128"
android:singleLine="true"
android:textColor="#FFFFFF"
android:textCursorDrawable="@null" >
</EditText>
</LinearLayout>
</ScrollView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="combobox_choose_item">请选择其中一个</string>
<string name="ignore_chain">忽略时点</string>
<string name="react_chain">显示时点</string>
<string name="refresh_textures">刷新界面</string>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="combobox_choose_item">请选择其中一个</string>
<string name="ignore_chain">忽略时点</string>
<string name="react_chain">显示时点</string>
<string name="refresh_textures">刷新界面</string>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="trasparent_dark_purple">#669933CC</color>
<color name="dark_purple">#9933CC</color>
<color name="navigator_dir_text_color">#666666</color>
<color name="navigator_dir_text_color_selected">#ff8a00</color>
<color name="less_trasparent_dark_purple">#339933CC</color>
<color name="dark_trasparent_dark_purple">#999933CC</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="chain_control_button_width">70dip</dimen>
<dimen name="chain_control_button_height">30dip</dimen>
<dimen name="chain_control_margin">10dip</dimen>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="combobox_choose_item">Select one item</string>
<string name="ignore_chain">Ignore All Timing</string>
<string name="react_chain">Show All Timing</string>
<string name="refresh_textures">Refresh Textures</string>
</resources>
\ No newline at end of file
...@@ -500,6 +500,7 @@ ...@@ -500,6 +500,7 @@
!system 1624 投掷骰子结果: !system 1624 投掷骰子结果:
#tips #tips
!system 1700 可以用鼠标右键%ls !system 1700 可以用鼠标右键%ls
!system 1800 问题反馈
#victory reason #victory reason
!victory 0x0 投降 !victory 0x0 投降
!victory 0x1 基本分变成0 !victory 0x1 基本分变成0
......
...@@ -8,8 +8,8 @@ android { ...@@ -8,8 +8,8 @@ android {
applicationId "cn.garymb.ygomobile" applicationId "cn.garymb.ygomobile"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 28
versionCode 352001017 versionCode 352001025
versionName "3.5.2" versionName "3.5.2.2"
flavorDimensions "versionCode" flavorDimensions "versionCode"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
ndk { ndk {
......
...@@ -195,7 +195,6 @@ ...@@ -195,7 +195,6 @@
<!--android:priority="1000"--> <!--android:priority="1000"-->
<service <service
android:name="cn.garymb.ygomobile.ui.plus.ServiceDuelAssistant" android:name="cn.garymb.ygomobile.ui.plus.ServiceDuelAssistant"
android:process=":game"
android:priority="1000" /> android:priority="1000" />
</application> </application>
......
...@@ -16,6 +16,7 @@ import com.yuyh.library.imgsel.common.ImageLoader; ...@@ -16,6 +16,7 @@ import com.yuyh.library.imgsel.common.ImageLoader;
import cn.garymb.ygomobile.interfaces.GameConfig; import cn.garymb.ygomobile.interfaces.GameConfig;
import cn.garymb.ygomobile.interfaces.GameHost; import cn.garymb.ygomobile.interfaces.GameHost;
import cn.garymb.ygomobile.utils.CrashHandler; import cn.garymb.ygomobile.utils.CrashHandler;
import cn.garymb.ygomobile.utils.ScreenUtil;
import libwindbot.windbot.WindBot; import libwindbot.windbot.WindBot;
public class App extends GameApplication { public class App extends GameApplication {
...@@ -30,12 +31,20 @@ public class App extends GameApplication { ...@@ -30,12 +31,20 @@ public class App extends GameApplication {
initImgsel(); initImgsel();
// QbSdk.initX5Environment(this, null); // QbSdk.initX5Environment(this, null);
// QbSdk.setCurrentID(""); // QbSdk.setCurrentID("");
AppsSettings.init(this);
} }
AppsSettings.init(this);
//初始化异常工具类 //初始化异常工具类
CrashHandler crashHandler = CrashHandler.getInstance(); CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(getApplicationContext()); crashHandler.init(getApplicationContext());
if(getPackageName().equals(getAppProcessName())){
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String args = intent.getStringExtra("args");
WindBot.runAndroid(args);
}
}, new IntentFilter(Constants.ACTION_WINDBOT));
}
} }
private void initImgsel() { private void initImgsel() {
...@@ -62,6 +71,7 @@ public class App extends GameApplication { ...@@ -62,6 +71,7 @@ public class App extends GameApplication {
public static GameConfig genConfig() { public static GameConfig genConfig() {
GameConfig config = new GameConfig(); GameConfig config = new GameConfig();
config.setNotchHeight(AppsSettings.get().getNotchHeight());
config.setNativeInitOptions(AppsSettings.get().getNativeInitOptions()); config.setNativeInitOptions(AppsSettings.get().getNativeInitOptions());
config.setLockScreenOrientation(AppsSettings.get().isLockScreenOrientation()); config.setLockScreenOrientation(AppsSettings.get().isLockScreenOrientation());
config.setSensorRefresh(AppsSettings.get().isSensorRefresh()); config.setSensorRefresh(AppsSettings.get().isSensorRefresh());
......
...@@ -5,6 +5,7 @@ import android.view.Gravity; ...@@ -5,6 +5,7 @@ import android.view.Gravity;
import cn.garymb.ygomobile.lite.BuildConfig; import cn.garymb.ygomobile.lite.BuildConfig;
public interface Constants { public interface Constants {
String ACTION_WINDBOT = "RUN_WINDBOT";
boolean DEBUG = BuildConfig.DEBUG; boolean DEBUG = BuildConfig.DEBUG;
String PREF_START = "game_pref_"; String PREF_START = "game_pref_";
String PREF_LAST_DECK_PATH = "pref_last_deck_path"; String PREF_LAST_DECK_PATH = "pref_last_deck_path";
......
...@@ -2,12 +2,19 @@ package cn.garymb.ygomobile; ...@@ -2,12 +2,19 @@ package cn.garymb.ygomobile;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.graphics.Point; import android.graphics.Point;
import android.os.Build;
import android.util.Log; import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import org.minidns.record.A;
import java.io.File; import java.io.File;
...@@ -16,6 +23,11 @@ import cn.garymb.ygomobile.interfaces.GameConfig; ...@@ -16,6 +23,11 @@ import cn.garymb.ygomobile.interfaces.GameConfig;
import cn.garymb.ygomobile.interfaces.GameHost; import cn.garymb.ygomobile.interfaces.GameHost;
import cn.garymb.ygomobile.interfaces.GameSize; import cn.garymb.ygomobile.interfaces.GameSize;
import cn.garymb.ygomobile.lite.BuildConfig; import cn.garymb.ygomobile.lite.BuildConfig;
import cn.garymb.ygomobile.lite.R;
import cn.garymb.ygomobile.ui.plus.DialogPlus;
import cn.garymb.ygomobile.ui.plus.VUiKit;
import cn.garymb.ygomobile.utils.ScreenUtil;
import cn.garymb.ygomobile.utils.rom.RomIdentifier;
import libwindbot.windbot.WindBot; import libwindbot.windbot.WindBot;
import static cn.garymb.ygomobile.Constants.CORE_BOT_CONF_PATH; import static cn.garymb.ygomobile.Constants.CORE_BOT_CONF_PATH;
...@@ -25,12 +37,13 @@ class LocalGameHost extends GameHost { ...@@ -25,12 +37,13 @@ class LocalGameHost extends GameHost {
private Context context; private Context context;
private SharedPreferences settings; private SharedPreferences settings;
private boolean mInitBot = false; private boolean mInitBot = false;
private GameSize mGameSize;
LocalGameHost(Context context) { LocalGameHost(Context context) {
super(context); super(context);
this.context = context; this.context = context;
settings = context.getSharedPreferences("ygo_settings", Context.MODE_PRIVATE); settings = context.getSharedPreferences("ygo_settings", Context.MODE_PRIVATE);
if(!GameApplication.isGameProcess()){ if (!GameApplication.isGameProcess()) {
Log.e("kk", "GameHost don't running in game process."); Log.e("kk", "GameHost don't running in game process.");
} }
} }
...@@ -69,30 +82,38 @@ class LocalGameHost extends GameHost { ...@@ -69,30 +82,38 @@ class LocalGameHost extends GameHost {
} }
} }
private void initWindBot() { @Override
synchronized (this){ public void initWindbot(NativeInitOptions options, GameConfig config) {
if(mInitBot){ // if (options.mDbList.size() == 0) {
return; // return;
} // }
mInitBot = true; // String cdb = options.mDbList.get(0);
} // Log.i("kk", "cdb=" + cdb);
Log.i("路径", context.getFilesDir().getPath()); // try {
Log.i("路径2", AppsSettings.get().getDataBasePath() + "/" + DATABASE_NAME); // WindBot.initAndroid(AppsSettings.get().getResourcePath(),
try { // cdb,
WindBot.initAndroid(AppsSettings.get().getResourcePath(), // options.mResDir + "/" + CORE_BOT_CONF_PATH);
AppsSettings.get().getDataBasePath() + "/" + DATABASE_NAME, // mInitBot = true;
AppsSettings.get().getResourcePath() + "/" + CORE_BOT_CONF_PATH); // } catch (Throwable e) {
} catch (Throwable e) { // e.printStackTrace();
e.printStackTrace(); // Log.i("kk", "initAndroid", e);
} // }
} }
@Override @Override
public void runWindbot(String cmd) { public void runWindbot(String cmd) {
initWindBot(); // if (mInitBot) {
WindBot.runAndroid(cmd); // WindBot.runAndroid(cmd);
// } else {
// VUiKit.show(context, "run bot error");
// }
Intent intent = new Intent(Constants.WINDBOT_ACTION);
intent.putExtra("args", cmd);
intent.setPackage(context.getPackageName());
context.sendBroadcast(intent);
} }
@Override @Override
public AssetManager getGameAsset() { public AssetManager getGameAsset() {
return context.getAssets(); return context.getAssets();
...@@ -115,12 +136,17 @@ class LocalGameHost extends GameHost { ...@@ -115,12 +136,17 @@ class LocalGameHost extends GameHost {
w1 = fullW; w1 = fullW;
h1 = fullH; h1 = fullH;
} else { } else {
//全面屏,非沉浸模式,自动隐藏虚拟键,需要适配
w1 = actW; w1 = actW;
h1 = actH; h1 = actH;
} }
maxW = Math.max(w1, h1); maxW = Math.max(w1, h1);
maxH = Math.min(w1, h1); maxH = Math.min(w1, h1);
Log.i("kk", "maxW=" + maxW + ",maxH=" + maxH); int notchHeight = config.getNotchHeight();
if(notchHeight > 0 && immerSiveMode){
maxW -= notchHeight;
}
Log.i("kk", "real=" + fullW + "x" + fullH + ",cur=" + actW + "x" + actH + ",use=" + maxW + "x" + maxH);
float sx, sy, scale; float sx, sy, scale;
int gw, gh; int gw, gh;
if (keepScale) { if (keepScale) {
...@@ -137,11 +163,19 @@ class LocalGameHost extends GameHost { ...@@ -137,11 +163,19 @@ class LocalGameHost extends GameHost {
//fix touch point //fix touch point
int left = (maxW - gw) / 2; int left = (maxW - gw) / 2;
int top = (maxH - gh) / 2; int top = (maxH - gh) / 2;
if (notchHeight > 0 && !immerSiveMode) {
//left += (fullW - actW) / 2;
//fix touch
//left = (maxW - gw - config.getNotchHeight()) / 2;
}
Log.i("kk", "touch fix=" + left + "x" + top); Log.i("kk", "touch fix=" + left + "x" + top);
//if(huawei and liuhai){ //if(huawei and liuhai){
// left-=liuhai // left-=liuhai
// } // }
return new GameSize(gw, gh, left, top); GameSize gameSize = new GameSize(gw, gh, left, top);
gameSize.setScreen(fullW, fullH, actW, actH);
mGameSize = gameSize;
return gameSize;
} }
@Override @Override
...@@ -165,4 +199,48 @@ class LocalGameHost extends GameHost { ...@@ -165,4 +199,48 @@ class LocalGameHost extends GameHost {
public void onGameExit(Activity activity) { public void onGameExit(Activity activity) {
} }
@Override
public void onGameReport(Activity activity, GameConfig config) {
DialogPlus dlg = new DialogPlus(activity);
dlg.setTitle("Report");
dlg.setMessage("You need to collect the data of your model and the settings of the full screen / screen, and send the screenshot of the current interface to the author.");
dlg.setLeftButtonListener((d, id) -> {
//
dlg.dismiss();
showDialog(activity, config);
});
dlg.setRightButtonListener((d, id) -> {
dlg.dismiss();
});
dlg.show();
}
@SuppressLint({"DefaultLocale", "SetTextI18n"})
private void showDialog(Activity activity, GameConfig config) {
DialogPlus dlg = new DialogPlus(activity);
dlg.setView(R.layout.dialog_report);
GameSize size = mGameSize;
if (size == null) {
size = getGameSize(activity, config);
Log.i("kk", "gen size " + size);
}
((TextView) dlg.findViewById(R.id.tv_version)).setText(BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_CODE);
((TextView) dlg.findViewById(R.id.tv_model)).setText(Build.MODEL + "/" + Build.PRODUCT);
((TextView) dlg.findViewById(R.id.tv_android)).setText(Build.VERSION.RELEASE + " (" + Build.VERSION.SDK_INT + ")");
((TextView) dlg.findViewById(R.id.tv_rom)).setText(String.valueOf(RomIdentifier.getRomInfo(activity)));
((TextView) dlg.findViewById(R.id.tv_cut_screen)).setText((config.getNotchHeight() > 0) ? "Yes/" + config.getNotchHeight() : "No");
if (ScreenUtil.hasNavigationBar(activity)) {
((TextView) dlg.findViewById(R.id.tv_nav_bar)).setText("Yes/" + (ScreenUtil.isNavigationBarShown(activity) ? "Show" : "Hide"));
} else {
((TextView) dlg.findViewById(R.id.tv_nav_bar)).setText("No");
}
((TextView) dlg.findViewById(R.id.tv_screen_size)).setText(String.format("r:%dx%d,a=%dx%d,k=%s, g=%dx%d,c=%dx%d",
size.getFullW(), size.getFullH(), size.getActW(), size.getActH(), config.isKeepScale()?"Y":"N", size.getWidth(), size.getHeight(), size.getTouchX(), size.getTouchY()));
dlg.findViewById(R.id.btn_ok).setOnClickListener((v) -> {
dlg.dismiss();
});
dlg.show();
}
} }
...@@ -23,10 +23,14 @@ import cn.garymb.ygomobile.ui.home.ResCheckTask; ...@@ -23,10 +23,14 @@ import cn.garymb.ygomobile.ui.home.ResCheckTask;
import cn.garymb.ygomobile.ui.plus.VUiKit; import cn.garymb.ygomobile.ui.plus.VUiKit;
import cn.garymb.ygomobile.utils.IOUtils; import cn.garymb.ygomobile.utils.IOUtils;
import cn.garymb.ygomobile.utils.SystemUtils; import cn.garymb.ygomobile.utils.SystemUtils;
import libwindbot.windbot.WindBot;
import ocgcore.CardManager; import ocgcore.CardManager;
import ocgcore.ConfigManager; import ocgcore.ConfigManager;
import ocgcore.DataManager; import ocgcore.DataManager;
import static cn.garymb.ygomobile.Constants.CORE_BOT_CONF_PATH;
import static cn.garymb.ygomobile.Constants.DATABASE_NAME;
public class LogoActivity extends BaseActivity { public class LogoActivity extends BaseActivity {
public static final String EXTRA_NEW_VERSION = "isNew"; public static final String EXTRA_NEW_VERSION = "isNew";
public static final String EXTRA_ERROR = "error"; public static final String EXTRA_ERROR = "error";
...@@ -90,6 +94,7 @@ public class LogoActivity extends BaseActivity { ...@@ -90,6 +94,7 @@ public class LogoActivity extends BaseActivity {
} }
private void onCheckCompleted(boolean isNew, int err) { private void onCheckCompleted(boolean isNew, int err) {
initWindBot();
Intent intent = new Intent(this, MainActivity.class); Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(EXTRA_NEW_VERSION, isNew); intent.putExtra(EXTRA_NEW_VERSION, isNew);
intent.putExtra(EXTRA_ERROR, err); intent.putExtra(EXTRA_ERROR, err);
...@@ -99,6 +104,18 @@ public class LogoActivity extends BaseActivity { ...@@ -99,6 +104,18 @@ public class LogoActivity extends BaseActivity {
finish(); finish();
} }
private void initWindBot() {
Log.i("kk", "files=" + getFilesDir().getPath());
Log.i("kk", "cdb=" + AppsSettings.get().getDataBasePath() + "/" + DATABASE_NAME);
try {
WindBot.initAndroid(AppsSettings.get().getResourcePath(),
AppsSettings.get().getDataBasePath() + "/" + DATABASE_NAME,
AppsSettings.get().getResourcePath() + "/" + CORE_BOT_CONF_PATH);
} catch (Throwable e) {
Log.e("kk", "init windbot", e);
}
}
@Override @Override
public void finish() { public void finish() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
......
package cn.garymb.ygomobile.ui.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class SquareFrameLayout extends FrameLayout {
public SquareFrameLayout(@NonNull Context context) {
super(context);
}
public SquareFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SquareFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
...@@ -7,6 +7,7 @@ import android.os.Build; ...@@ -7,6 +7,7 @@ import android.os.Build;
import android.util.Log; import android.util.Log;
import android.view.DisplayCutout; import android.view.DisplayCutout;
import android.view.View; import android.view.View;
import android.view.WindowInsets;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
...@@ -34,10 +35,8 @@ public class ScreenUtil { ...@@ -34,10 +35,8 @@ public class ScreenUtil {
void onNotchInformation(boolean isNotch, int notchHeight, int phoneType); void onNotchInformation(boolean isNotch, int notchHeight, int phoneType);
} }
//是否是刘海屏 //是否是刘海屏
public static void findNotchInformation(Activity activity, FindNotchInformation findNotchInformation) { public static void findNotchInformation(Activity activity, FindNotchInformation findNotchInformation) {
if (isNotchVivo(activity)) { if (isNotchVivo(activity)) {
findNotchInformation.onNotchInformation(true, getNotchHeightVivo(activity), NOTCH_TYPE_PHONE_VIVO); findNotchInformation.onNotchInformation(true, getNotchHeightVivo(activity), NOTCH_TYPE_PHONE_VIVO);
} else if (isNotchOPPO(activity)) { } else if (isNotchOPPO(activity)) {
...@@ -77,14 +76,14 @@ public class ScreenUtil { ...@@ -77,14 +76,14 @@ public class ScreenUtil {
List<Rect> rects = cutout.getBoundingRects(); List<Rect> rects = cutout.getBoundingRects();
if (rects == null || rects.size() == 0) { if (rects == null || rects.size() == 0) {
try { try {
FileLogUtil.writeAndTime("Android P刘海: rects:"+(rects==null)); FileLogUtil.writeAndTime("Android P刘海: rects:" + (rects == null));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
findNotchPInformation.onNotchInformation(false, 0, NOTCH_TYPE_PHONE_ANDROID_P); findNotchPInformation.onNotchInformation(false, 0, NOTCH_TYPE_PHONE_ANDROID_P);
} else { } else {
try { try {
FileLogUtil.writeAndTime("Android P刘海: 刘海存在,个数为"+rects.size()); FileLogUtil.writeAndTime("Android P刘海: 刘海存在,个数为" + rects.size());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
...@@ -283,17 +282,22 @@ public class ScreenUtil { ...@@ -283,17 +282,22 @@ public class ScreenUtil {
} }
public static int getCurrentNavigationBarHeight(Activity activity){ public static int getCurrentNavigationBarHeight(Activity activity) {
if(isNavigationBarShown(activity)){ if (isNavigationBarShown(activity)) {
return getNavigationBarHeight(activity); return getNavigationBarHeight(activity);
} else{ } else {
return 0; return 0;
} }
} }
public static boolean hasNavigationBar(Activity activity) {
//虚拟键的view,为空或者不可见时是隐藏状态
View view = activity.findViewById(android.R.id.navigationBarBackground);
return view != null;
}
/** /**
* 非全面屏下 虚拟按键是否打开 * 非全面屏下 虚拟按键是否打开
* @param activity
*/ */
public static boolean isNavigationBarShown(Activity activity) { public static boolean isNavigationBarShown(Activity activity) {
//虚拟键的view,为空或者不可见时是隐藏状态 //虚拟键的view,为空或者不可见时是隐藏状态
...@@ -303,6 +307,7 @@ public class ScreenUtil { ...@@ -303,6 +307,7 @@ public class ScreenUtil {
/** /**
* 非全面屏下 虚拟键高度(无论是否隐藏) * 非全面屏下 虚拟键高度(无论是否隐藏)
*
* @param context * @param context
*/ */
public static int getNavigationBarHeight(Context context) { public static int getNavigationBarHeight(Context context) {
......
package cn.garymb.ygomobile.utils.rom;
import android.text.TextUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/09
* desc :
* </pre>
*/
public class AmigoOsChecker extends Checker {
@Override
protected String getManufacturer() {
return ManufacturerList.AMIGO;
}
@Override
protected String[] getAppList() {
return AppList.AMIGO_OS_APPS;
}
@Override
public ROM getRom() {
return ROM.AmigoOS;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = null;
String versionStr = properties.getProperty(BuildPropKeyList.AMIGO_DISPLAY_ID);
if (!TextUtils.isEmpty(versionStr)) {
Matcher matcher = Pattern.compile("amigo([\\d.]+)[a-zA-Z]*").matcher(versionStr); // "amigo3.5.1"
if (matcher.find()) {
try {
String version = matcher.group(1);
info = new ROMInfo(getRom());
info.setVersion(version);
info.setBaseVersion(Integer.parseInt(version.split("\\.")[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/03
* desc :
* </pre>
*/
interface AppList {
String[] MIUI_APPS = {
"com.miui.home" // 系统桌面
, "com.miui.core" // MIUI SDK
, "com.miui.rom" // com.miui.rom
, "com.miui.system" // com.miui.system
, "com.xiaomi.bluetooth" // MIUI Bluetooth
, "com.miui.securitycenter" // 安全中心
, "com.miui.cloudservice" // 小米云服务
, "com.miui.backup" // 备份
, "com.android.camera" // 相机
, "com.miui.gallery" // 相册
, "com.miui.player" // 音乐
};
String[] FLYME_APPS = {
"com.meizu.flyme.launcher" // Flyme桌面
, "com.meizu.filemanager" // 文件管理
, "com.meizu.backup" // 备份与恢复
, "com.meizu.flyme.update" // 系统更新
, "com.meizu.media.camera" // 相机
, "com.meizu.mstore" // 应用商店
, "com.meizu.safe" // 手机管家
, "com.meizu.setup" // 开机引导
, "com.android.settings" // 设置
, "com.meizu.media.music" // 音乐
, "com.meizu.media.video" // 视频
, "com.meizu.media.gallery" // 图库
, "com.meizu.flyme.input" // 系统输入法
};
String[] EMUI_APPS = {};
String[] COLOR_OS_APPS = {};
String[] FUNTOUCH_OS_APPS = {};
String[] SMARTISAN_OS_APPS = {};
String[] EUI_APPS = {};
String[] SENSE_APPS = {};
String[] AMIGO_OS_APPS = {};
String[] _360_OS_APPS = {};
String[] NUBIA_UI_APPS = {};
String[] H2OS_APPS = {};
String[] YUN_OS_APPS = {};
String[] YULONG_APPS = {};
String[] SAMSUNG_APPS = {};
String[] SONY_APPS = {
"com.sonyericsson.settings" // 设定
, "com.sonymobile.android.contacts" // 通讯录
, "com.sonymobile.support" // 支持
, "com.sonymobile.synchub" // 备份和恢复
, "com.sonyericsson.android.camera" // 相机
, "com.sonyericsson.album" // 相册
, "com.sonyericsson.music" // 音乐
, "com.sonymobile.calendar" // 日历
, "com.sonymobile.android.dialer" // 电话
};
String[] LENOVO_APPS = {};
String[] LG_APPS = {};
String[] GOOGLE_APPS = {};
}
package cn.garymb.ygomobile.utils.rom;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/03
* desc :
* </pre>
*/
interface BuildPropKeyList {
String KEY_DISPLAY_ID = "ro.build.display.id";
String KEY_BASE_OS_VERSION = "ro.build.version.base_os";
// 小米 : MIUI
String MIUI_VERSION = "ro.build.version.incremental"; // "7.6.15"
String MIUI_VERSION_NANE = "ro.miui.ui.version.name"; // "V8"
// 华为 : EMUI
String EMUI_VERSION = "ro.build.version.emui"; // "EmotionUI_3.0"
// 魅族 : Flyme
String FLYME_DISPLAY_ID = KEY_DISPLAY_ID; // "Flyme OS 4.5.4.2U"
// OPPO : ColorOS
String COLOROS_ROM_VERSION = "ro.rom.different.version"; // "ColorOS2.1"
// vivo : FuntouchOS
String FUNTOUCHOS_OS_VERSION = "ro.vivo.os.version"; // "3.0"
String FUNTOUCHOS_DISPLAY_ID = "ro.vivo.os.build.display.id"; // "FuntouchOS_3.0"
String FUNTOUCHOS_ROM_VERSION = "ro.vivo.rom.version"; // "rom_3.1"
// Samsung
// Sony
// 乐视 : eui
String EUI_VERSION = "ro.letv.release.version"; // "5.9.023S"
String EUI_VERSION_DATE = "ro.letv.release.version_date"; // "5.9.023S_03111"
// 金立 : amigo
String AMIGO_ROM_VERSION = "ro.gn.gnromvernumber"; // "GIONEE ROM5.0.16"
String AMIGO_DISPLAY_ID = KEY_DISPLAY_ID;
// 酷派 : yulong
// HTC : Sense
// LG : LG
// 联想
}
package cn.garymb.ygomobile.utils.rom;
import android.content.Context;
import android.content.pm.PackageManager;
import java.util.Set;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/09
* desc :
* </pre>
*/
public abstract class Checker implements IChecker {
protected abstract String getManufacturer();
protected abstract String[] getAppList();
@Override
public boolean checkManufacturer(String manufacturer) {
return manufacturer.equalsIgnoreCase(getManufacturer());
}
@Override
public boolean checkApplication(Context context) {
PackageManager manager = context.getPackageManager();
for (String pkg : getAppList()) {
try {
manager.getPackageInfo(pkg, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
return false;
}
@Override
public boolean checkApplication(Set<String> installedPackages) {
int count = 0;
String[] list = getAppList();
int aim = (list.length + 1) / 2;
for (String pkg : list) {
if (installedPackages.contains(pkg)) {
count++;
if (count >= aim)
return true;
}
}
return false;
}
}
package cn.garymb.ygomobile.utils.rom;
import android.text.TextUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/09
* desc :
* </pre>
*/
public class ColorOsChecker extends Checker {
@Override
protected String getManufacturer() {
return ManufacturerList.OPPO;
}
@Override
protected String[] getAppList() {
return AppList.COLOR_OS_APPS;
}
@Override
public ROM getRom() {
return ROM.ColorOS;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = null;
String versionStr = properties.getProperty(BuildPropKeyList.COLOROS_ROM_VERSION);
if (!TextUtils.isEmpty(versionStr)) {
Matcher matcher = Pattern.compile("ColorOS([\\d.]+)").matcher(versionStr); // ColorOS2.1
if (matcher.find()) {
try {
String version = matcher.group(1);
info = new ROMInfo(getRom());
info.setVersion(version);
info.setBaseVersion(Integer.parseInt(version.split("\\.")[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
import android.text.TextUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/09
* desc :
* </pre>
*/
public class EmuiChecker extends Checker {
@Override
protected String getManufacturer() {
return ManufacturerList.HUAWEI;
}
@Override
protected String[] getAppList() {
return AppList.EMUI_APPS;
}
@Override
public ROM getRom() {
return ROM.EMUI;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = null;
String versionStr = properties.getProperty(BuildPropKeyList.EMUI_VERSION);
if (!TextUtils.isEmpty(versionStr)) {
Matcher matcher = Pattern.compile("EmotionUI_([\\d.]+)").matcher(versionStr); // EmotionUI_3.0
if (matcher.find()) {
try {
String version = matcher.group(1);
info = new ROMInfo(getRom());
info.setVersion(version);
info.setBaseVersion(Integer.parseInt(version.split("\\.")[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
import android.text.TextUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/09
* desc :
* </pre>
*/
public class EuiChecker extends Checker {
@Override
protected String getManufacturer() {
return ManufacturerList.LETV;
}
@Override
protected String[] getAppList() {
return AppList.EUI_APPS;
}
@Override
public ROM getRom() {
return ROM.EUI;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = null;
String versionStr = properties.getProperty(BuildPropKeyList.EUI_VERSION);
if (!TextUtils.isEmpty(versionStr)) {
Matcher matcher = Pattern.compile("([\\d.]+)[^\\d]*").matcher(versionStr); // 5.9.023S
if (matcher.find()) {
try {
String version = matcher.group(1);
info.setVersion(version);
info.setBaseVersion(Integer.parseInt(version.split("\\.")[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
import android.text.TextUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/09
* desc :
* </pre>
*/
public class FlymeChecker extends Checker {
@Override
public ROM getRom() {
return ROM.Flyme;
}
@Override
protected String getManufacturer() {
return ManufacturerList.MEIZU;
}
@Override
protected String[] getAppList() {
return AppList.FLYME_APPS;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = new ROMInfo(getRom());
String versionStr = properties.getProperty(BuildPropKeyList.FLYME_DISPLAY_ID);
if (!TextUtils.isEmpty(versionStr)) {
Matcher matcher = Pattern.compile("Flyme[^\\d]*([\\d.]+)[^\\d]*").matcher(versionStr); // Flyme OS 4.5.4.2U
if (matcher.find()) {
try {
String version = matcher.group(1);
info.setVersion(version);
info.setBaseVersion(Integer.parseInt(version.split("\\.")[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
import android.text.TextUtils;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/09
* desc :
* </pre>
*/
public class FuntouchOsChecker extends Checker {
@Override
protected String getManufacturer() {
return ManufacturerList.VIVO;
}
@Override
protected String[] getAppList() {
return AppList.FUNTOUCH_OS_APPS;
}
@Override
public ROM getRom() {
return ROM.FuntouchOS;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = null;
String versionStr = properties.getProperty(BuildPropKeyList.FUNTOUCHOS_OS_VERSION);
if (!TextUtils.isEmpty(versionStr) && versionStr.matches("[\\d.]+")) { // 3.0
try {
info = new ROMInfo(getRom());
info.setVersion(versionStr);
info.setBaseVersion(Integer.parseInt(versionStr.split("\\.")[0]));
} catch (Exception e) {
e.printStackTrace();
}
}
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
import android.os.Build;
import android.text.TextUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GoogleChecker extends Checker {
@Override
protected String getManufacturer() {
return ManufacturerList.GOOGLE;
}
@Override
protected String[] getAppList() {
return new String[]{"com.google.android.apps.nexuslauncher"};
}
@Override
public ROM getRom() {
return ROM.Google;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = new ROMInfo(getRom());
info.setVersion(Build.VERSION.RELEASE);
info.setBaseVersion(Build.VERSION.SDK_INT);
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
import android.content.Context;
import java.util.Set;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/03
* desc :
* </pre>
*/
public interface IChecker {
ROM getRom();
boolean checkManufacturer(String manufacturer);
boolean checkApplication(Context context);
boolean checkApplication(Set<String> installedPackages);
ROMInfo checkBuildProp(RomProperties properties) throws Exception;
}
package cn.garymb.ygomobile.utils.rom;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/03
* desc :
* </pre>
*/
interface ManufacturerList {
String HUAWEI = "huawei"; // 华为
String MEIZU = "meizu"; // 魅族
String XIAOMI = "xiaomi"; // 小米
String SONY = "sony"; // 索尼
String SAMSUNG = "samsung"; // 三星
String LETV = "letv"; // 乐视
String ZTE = "zte"; // 中兴
String YULONG = "yulong"; // 酷派
String LENOVO = "lenovo"; // 联想
String LG = "lg"; // LG
String OPPO = "oppo"; // oppo
String VIVO = "vivo"; // vivo
String AMIGO = "amigo"; // 金立 // todo
String GOOGLE = "Google";
}
package cn.garymb.ygomobile.utils.rom;
import android.text.TextUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/01
* desc :
* </pre>
*/
class MiuiChecker extends Checker {
@Override
public ROM getRom() {
return ROM.MIUI;
}
@Override
protected String getManufacturer() {
return ManufacturerList.XIAOMI;
}
@Override
protected String[] getAppList() {
return AppList.MIUI_APPS;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
ROMInfo info = null;
String versionName = properties.getProperty(BuildPropKeyList.MIUI_VERSION_NANE);
if (!TextUtils.isEmpty(versionName) && versionName.matches("[Vv]\\d+")) { // V9
try {
info = new ROMInfo(getRom());
info.setBaseVersion(Integer.parseInt(versionName.substring(1)));
String versionStr = properties.getProperty(BuildPropKeyList.MIUI_VERSION);
if (!TextUtils.isEmpty(versionStr)) {
// 参考: 8.1.25 & V9.6.2.0.ODECNFD & V10.0.1.0.OAACNFH
Matcher matcher = Pattern.compile("[Vv]?(\\d+(\\.\\d+)*)[.A-Za-z]*").matcher(versionStr);
if (matcher.matches()) {
info.setVersion(matcher.group(1));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return info;
}
}
package cn.garymb.ygomobile.utils.rom;
public enum ROM {
MIUI, // 小米
Flyme, // 魅族
EMUI, // 华为
ColorOS, // OPPO
FuntouchOS, // vivo
SmartisanOS, // 锤子
EUI, // 乐视
Sense, // HTC
AmigoOS, // 金立
_360OS, // 奇酷360
NubiaUI, // 努比亚
H2OS, // 一加
YunOS, // 阿里巴巴
YuLong, // 酷派
SamSung, // 三星
Sony, // 索尼
Lenovo, // 联想
LG, // LG
Google, // 原生
Other // CyanogenMod, Lewa OS, 百度云OS, Tencent OS, 深度OS, IUNI OS, Tapas OS, Mokee
}
\ No newline at end of file
package cn.garymb.ygomobile.utils.rom;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/03
* desc :
* </pre>
*/
public class ROMInfo {
private ROM rom;
private int baseVersion;
private String version;
public ROMInfo(ROM rom) {
this.rom = rom;
}
public ROMInfo(ROM rom, int baseVersion, String version) {
this.rom = rom;
this.baseVersion = baseVersion;
this.version = version;
}
public void setRom(ROM rom) {
this.rom = rom;
}
public void setBaseVersion(int baseVersion) {
this.baseVersion = baseVersion;
}
public void setVersion(String version) {
this.version = version;
}
public ROM getRom() {
return rom;
}
public int getBaseVersion() {
return baseVersion;
}
public String getVersion() {
return version;
}
@Override
public String toString() {
if(version != null){
return rom+"/"+version;
}
return String.valueOf(rom);
}
}
package cn.garymb.ygomobile.utils.rom;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/01
* desc :
* </pre>
*/
public class RomIdentifier {
private static ROM sRomType;
private static ROMInfo sRomInfo;
private RomIdentifier() {
}
private static IChecker[] getICheckers() {
return new IChecker[]{
new MiuiChecker(),
new EmuiChecker(),
new ColorOsChecker(),
new FuntouchOsChecker(),
new FlymeChecker(),
new EuiChecker(),
new AmigoOsChecker(),
new GoogleChecker()
};
}
/**
* 获取 ROM 类型
*
* @param context Context
* @return ROM 类型
*/
public static ROM getRomType(Context context) {
if (sRomType == null)
sRomType = doGetRomType(context);
return sRomType;
}
private static ROM doGetRomType(Context context) {
IChecker[] checkers = getICheckers();
// 优先检查 Manufacturer
String manufacturer = Build.MANUFACTURER;
Log.i("kk", "manufacturer="+manufacturer);
for (IChecker checker : checkers) {
if (checker.checkManufacturer(manufacturer)) {
// 检查完 Manufacturer 后, 再核对一遍应用列表
if (checker.checkApplication(context))
return checker.getRom();
}
}
// 如果 Manufacturer 和 应用列表对不上, 则以应用列表为准, 重新检查一遍应用列表
List<ApplicationInfo> appInfos = context.getPackageManager().getInstalledApplications(0);
HashSet<String> installPkgs = new HashSet<>();
for (ApplicationInfo appInfo : appInfos) {
installPkgs.add(appInfo.packageName);
}
for (IChecker checker : checkers) {
if (checker.checkApplication(installPkgs))
return checker.getRom();
}
return ROM.Other;
}
/**
* 获取 ROM 信息 (包括 ROM 类型和版本信息)
*
* @param context Context
* @return ROM 信息
*/
public static ROMInfo getRomInfo(Context context) {
if (sRomInfo == null)
sRomInfo = doGetRomInfo(context);
return sRomInfo;
}
private static ROMInfo doGetRomInfo(Context context) {
ROM rom = getRomType(context);
IChecker[] checkers = getICheckers();
RomProperties properties = new RomProperties();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
FileInputStream is = null;
try {
// 获取 build.prop 配置
Properties buildProperties = new Properties();
is = new FileInputStream(new File(Environment.getRootDirectory(), "build.prop"));
buildProperties.load(is);
properties.setBuildProp(buildProperties);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 检查配置
for (int i = 0; i < checkers.length; i++) {
if (rom == checkers[i].getRom()) {
if (i != 0) {
IChecker temp = checkers[0];
checkers[0] = checkers[i];
checkers[i] = temp;
}
break;
}
}
try {
ROMInfo temp;
for (IChecker checker : checkers) {
if ((temp = checker.checkBuildProp(properties)) != null) {
return temp;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return new ROMInfo(rom);
}
}
package cn.garymb.ygomobile.utils.rom;
import android.util.Log;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/10
* desc :
* </pre>
*/
class RomProperties {
Properties properties;
public RomProperties() {
}
public void setBuildProp(Properties properties) {
this.properties = properties;
}
public String getProperty(String key) throws Exception {
if (properties != null) {
return properties.getProperty(key);
} else {
return getSystemProperty(key);
}
}
private static String getSystemProperty(String key) throws Exception {
try {
Class<?> clz = Class.forName("android.os.SystemProperties");
Method get = clz.getMethod("get", String.class, String.class);
return (String) get.invoke(clz, key, null);
} catch (Exception e) {
Log.e(RomProperties.class.getSimpleName(),
"反射获取 build.prop 属性 " + key + " 失败", e);
}
return null;
}
}
package cn.garymb.ygomobile.utils.rom;
/**
* <pre>
* author : Senh Linsh
* github : https://github.com/SenhLinsh
* date : 2018/07/17
* desc :
* </pre>
*/
public class SonyChecker extends Checker {
@Override
protected String getManufacturer() {
return ManufacturerList.SONY;
}
@Override
protected String[] getAppList() {
return AppList.SONY_APPS;
}
@Override
public ROM getRom() {
return ROM.Sony;
}
@Override
public ROMInfo checkBuildProp(RomProperties properties) throws Exception {
return null;
}
}
...@@ -7,18 +7,23 @@ ...@@ -7,18 +7,23 @@
android:background="#38000000" android:background="#38000000"
android:gravity="center"> android:gravity="center">
<FrameLayout <cn.garymb.ygomobile.ui.widget.SquareFrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="400dp" android:layout_height="wrap_content"
android:gravity="center"> android:gravity="center">
<ImageView <cn.garymb.ygomobile.ui.widget.SquareFrameLayout
android:id="@+id/logo" android:layout_width="match_parent"
android:layout_width="380dp" android:layout_height="wrap_content">
android:layout_height="380dp" <ImageView
android:layout_gravity="top|center_horizontal" android:id="@+id/logo"
android:scaleType="fitXY" android:layout_width="match_parent"
android:src="@drawable/ic_launcher3" /> android:layout_height="match_parent"
android:padding="10dp"
android:layout_gravity="top|center_horizontal"
android:scaleType="fitXY"
android:src="@drawable/ic_launcher3" />
</cn.garymb.ygomobile.ui.widget.SquareFrameLayout>
<LinearLayout <LinearLayout
android:id="@+id/ly_loading" android:id="@+id/ly_loading"
...@@ -46,5 +51,5 @@ ...@@ -46,5 +51,5 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" /> android:layout_gravity="center_horizontal" />
</LinearLayout> </LinearLayout>
</FrameLayout> </cn.garymb.ygomobile.ui.widget.SquareFrameLayout>
</LinearLayout> </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#88000000"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/white"
android:text="@string/lb_version"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_version"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textColor="@color/white"
android:textSize="12sp"
tools:text="xxxx" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/white"
android:text="@string/lb_model"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_model"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textColor="@color/white"
android:textSize="12sp"
tools:text="xxxx" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/lb_android"
android:textColor="@color/white"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_android"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:layout_weight="2"
android:textSize="12sp"
tools:text="9.0.0" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/white"
android:text="@string/lb_rom"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_rom"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textColor="@color/white"
android:textSize="12sp"
tools:text="9.0.0" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/white"
android:text="@string/lb_cut_screen"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_cut_screen"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textColor="@color/white"
android:textSize="12sp"
tools:text="9.0.0" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="@color/white"
android:text="@string/lb_nav_bar"
android:textSize="12sp" />
<TextView
android:id="@+id/tv_nav_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textSize="12sp"
android:textColor="@color/white"
tools:text="9.0.0" />
</LinearLayout>
<TextView
android:id="@+id/tv_screen_size"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:textColor="@color/white"
android:padding="5dp"
tools:text="120x450" />
<Button
android:layout_width="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:id="@+id/btn_ok"
android:text="@android:string/ok"/>
</LinearLayout>
\ No newline at end of file
...@@ -274,4 +274,12 @@ ...@@ -274,4 +274,12 @@
<string name="about_pref_key_hw_hide_notouch">Hide Hw notouch</string> <string name="about_pref_key_hw_hide_notouch">Hide Hw notouch</string>
<string name="about_pref_key_hw_hide_notouch_desc">If the click has problem in your game, turn on this.</string> <string name="about_pref_key_hw_hide_notouch_desc">If the click has problem in your game, turn on this.</string>
<string name="tip_load_ok">load completed.</string> <string name="tip_load_ok">load completed.</string>
<string name="lb_nav_bar">Show Nav Bar:</string>
<string name="lb_model">Model:</string>
<string name="lb_android">Android:</string>
<string name="lb_rom">ROM:</string>
<string name="lb_rom_version">ROM Version:</string>
<string name="lb_cut_screen">Cut Screen:</string>
<string name="lb_cut_screen_state">Cut Screen State:</string>
<string name="lb_version">Version:</string>
</resources> </resources>
...@@ -273,4 +273,12 @@ ...@@ -273,4 +273,12 @@
<string name="about_pref_key_hw_hide_notouch">隐藏HW刘海区域</string> <string name="about_pref_key_hw_hide_notouch">隐藏HW刘海区域</string>
<string name="about_pref_key_hw_hide_notouch_desc">如果你游戏点击不准,请启用这个功能</string> <string name="about_pref_key_hw_hide_notouch_desc">如果你游戏点击不准,请启用这个功能</string>
<string name="tip_load_ok">加载完成</string> <string name="tip_load_ok">加载完成</string>
<string name="lb_nav_bar">Show Nav Bar:</string>
<string name="lb_model">Model:</string>
<string name="lb_android">Android:</string>
<string name="lb_rom">ROM:</string>
<string name="lb_rom_version">ROM Version:</string>
<string name="lb_cut_screen">Cut Screen:</string>
<string name="lb_cut_screen_state">Cut Screen State:</string>
<string name="lb_version">Version:</string>
</resources> </resources>
...@@ -276,4 +276,12 @@ ...@@ -276,4 +276,12 @@
<string name="about_pref_key_hw_hide_notouch">Hide Hw notouch</string> <string name="about_pref_key_hw_hide_notouch">Hide Hw notouch</string>
<string name="about_pref_key_hw_hide_notouch_desc">If the click has problem in your game, turn on this.</string> <string name="about_pref_key_hw_hide_notouch_desc">If the click has problem in your game, turn on this.</string>
<string name="tip_load_ok">load completed.</string> <string name="tip_load_ok">load completed.</string>
<string name="lb_nav_bar">Show Nav Bar:</string>
<string name="lb_model">Model:</string>
<string name="lb_android">Android:</string>
<string name="lb_rom">ROM:</string>
<string name="lb_rom_version">ROM Version:</string>
<string name="lb_cut_screen">Cut Screen:</string>
<string name="lb_cut_screen_state">Cut Screen State:</string>
<string name="lb_version">Version:</string>
</resources> </resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment