1 package org.coolreader.crengine;
2 
3 import android.app.Application;
4 import android.graphics.PixelFormat;
5 import android.os.Build;
6 import android.util.Log;
7 
8 import com.onyx.android.sdk.api.device.epd.EpdController;
9 import com.onyx.android.sdk.device.Device;
10 import com.onyx.android.sdk.utils.ReflectUtil;
11 
12 import java.lang.reflect.Field;
13 import java.lang.reflect.Method;
14 import java.util.List;
15 
16 public class DeviceInfo {
17 
18 	public final static String MANUFACTURER;
19 	public final static String MODEL;
20 	public final static String DEVICE;
21 	public final static String PRODUCT;
22 	public final static String BRAND;
23 	public final static int MIN_SCREEN_BRIGHTNESS_VALUE;
24 	public final static int MAX_SCREEN_BRIGHTNESS_VALUE;
25 	public final static int MAX_SCREEN_BRIGHTNESS_WARM_VALUE;
26 	public final static boolean SAMSUNG_BUTTONS_HIGHLIGHT_PATCH;
27 	public final static boolean EINK_SCREEN;
28 	public final static boolean EINK_SCREEN_REGAL;
29 	public final static boolean EINK_HAVE_FRONTLIGHT;
30 	public final static boolean EINK_HAVE_NATURAL_BACKLIGHT;
31 	public final static boolean EINK_SCREEN_UPDATE_MODES_SUPPORTED;
32 	public final static boolean NOOK_NAVIGATION_KEYS;
33 	public final static boolean EINK_NOOK;
34 	public final static boolean EINK_NOOK_120;
35 	public final static boolean EINK_ONYX;
36 	public final static boolean EINK_DNS;
37 	public final static boolean EINK_TOLINO;
38 	public final static boolean FORCE_HC_THEME;
39 	public final static boolean EINK_SONY;
40 	public final static boolean EINK_ENERGYSYSTEM;
41 	public final static boolean SONY_NAVIGATION_KEYS;
42 	public final static boolean USE_CUSTOM_TOAST;
43 	public final static boolean AMOLED_SCREEN;
44 	public final static boolean POCKETBOOK;
45 	public final static boolean ONYX_BUTTONS_LONG_PRESS_NOT_AVAILABLE;
46 	public final static boolean ONYX_HAVE_FRONTLIGHT;
47 	public final static boolean ONYX_HAVE_NATURAL_BACKLIGHT;
48 	public final static boolean ONYX_HAVE_BRIGHTNESS_SYSTEM_DIALOG;
49 	public final static boolean NOFLIBUSTA;
50 	public final static boolean NAVIGATE_LEFTRIGHT; // map left/right keys to single page flip
51 	public final static boolean REVERT_LANDSCAPE_VOLUME_KEYS; // revert volume keys in landscape mode
52 	public final static android.graphics.Bitmap.Config BUFFER_COLOR_FORMAT;
53 	public final static boolean USE_OPENGL = true;
54 	public final static int PIXEL_FORMAT;
55 	public final static String  DEF_FONT_FACE;
56 	public final static boolean USE_BITMAP_MEMORY_HACK; // revert volume keys in landscape mode
57 	public final static Integer DEF_FONT_SIZE;
58 	public final static boolean ONE_COLUMN_IN_LANDSCAPE;
59 
60 	// minimal screen backlight level percent for different devices
61 	private static final String[] MIN_SCREEN_BRIGHTNESS_DB = {
62 		"LGE;LG-P500",       "6", // LG Optimus One
63 		"samsung;GT-I9003",  "6", // Samsung i9003
64 		"Samsung;GT-I9000",  "1", // Samsung Galaxy S
65 		"Samsung;GT-I9100",  "1", // Samsung Galaxy S2
66 		"Samsung;GT-I9300",  "1", // Samsung Galaxy S3
67 		"Samsung;GT-I9500",  "1", // Samsung Galaxy S4
68 		"HTC;HTC EVO 3D*",   "1", // HTC EVO
69 		"Archos;A70S",       "1", // Archos
70 		"HTC;HTC Desire",    "6", // HTC Desire
71 		"HTC;HTC Desire S",  "6",
72 		"HTC;HTC Incredible*","6",// HTC Incredible, HTC Incredible S
73 		"HTC;Legend",        "6",
74 		"LGE;LG-E510",       "6",
75 		"*;Kindle Fire",     "6",
76 		"Samsung;GT-S5830",  "6",
77 		"HUAWEI;U8800",      "6",
78 		"Motorola;Milestone XT720", "6",
79 		"Foxconn;PocketBook A10", "3",
80 		"*;*;*;tolino",	     "1",
81 		// TODO: more devices here
82 	};
83 
84 	public final static int ICE_CREAM_SANDWICH = 14;
85 	public final static int HONEYCOMB = 11;
86 
87 	private static int sdkInt = 0;
getSDKLevel()88 	public static int getSDKLevel() {
89 		if (sdkInt > 0)
90 			return sdkInt;
91 		// hack for Android 1.5
92 		sdkInt = 3;
93 		Field fld;
94 		try {
95 			Class<?> cl = android.os.Build.VERSION.class;
96 			fld = cl.getField("SDK_INT");
97 			sdkInt = fld.getInt(cl);
98 			Log.i("cr3", "API LEVEL " + sdkInt + " detected");
99 		} catch (SecurityException e) {
100 			// ignore
101 		} catch (NoSuchFieldException e) {
102 			// ignore
103 		} catch (IllegalArgumentException e) {
104 			// ignore
105 		} catch (IllegalAccessException e) {
106 			// ignore
107 		}
108 		return sdkInt;
109 	}
110 
supportsActionBar()111 	public static boolean supportsActionBar() {
112 		return getSDKLevel() >= HONEYCOMB;
113 	}
114 
115 	static {
116 		MANUFACTURER = getBuildField("MANUFACTURER");
117 		MODEL = getBuildField("MODEL");
118 		DEVICE = getBuildField("DEVICE");
119 		PRODUCT = getBuildField("PRODUCT");
120 		BRAND = getBuildField("BRAND");
121 		SAMSUNG_BUTTONS_HIGHLIGHT_PATCH = MANUFACTURER.toLowerCase().contentEquals("samsung") &&
122 		        (MODEL.contentEquals("GT-S5830") || MODEL.contentEquals("GT-S5660")); // More models?
123 		AMOLED_SCREEN = MANUFACTURER.toLowerCase().contentEquals("samsung") &&
124         		(MODEL.toLowerCase().startsWith("gt-i")); // AMOLED screens: GT-IXXXX
125 		EINK_NOOK = MANUFACTURER.toLowerCase().contentEquals("barnesandnoble") &&
126 				(PRODUCT.contentEquals("NOOK") || MODEL.contentEquals("NOOK") || MODEL.contentEquals("BNRV350") || MODEL.contentEquals("BNRV300") || MODEL.contentEquals("BNRV500")) &&
127 				DEVICE.toLowerCase().contentEquals("zoom2");
128 		EINK_NOOK_120 = EINK_NOOK && (MODEL.contentEquals("BNRV350") || MODEL.contentEquals("BNRV300") || MODEL.contentEquals("BNRV500"));
129 		EINK_SONY = MANUFACTURER.toLowerCase().contentEquals("sony") && MODEL.startsWith("PRS-T");
130 		//MANUFACTURER=Onyx, MODEL=*; All ONYX BOOX Readers have e-ink screen
131 		EINK_ONYX = (MANUFACTURER.toLowerCase().contentEquals("onyx") || MANUFACTURER.toLowerCase().contentEquals("onyx-intl")) &&
132 				(BRAND.toLowerCase().contentEquals("onyx") || BRAND.toLowerCase().contentEquals("maccentre") || BRAND.toLowerCase().contentEquals("maccenter")) &&
133 				MODEL.length() > 0;
134 		EINK_ENERGYSYSTEM = (
135 			(BRAND.toLowerCase().contentEquals("energysistem")||BRAND.toLowerCase().contentEquals("energysystem")) &&  MODEL.toLowerCase().startsWith("ereader"));
136 		//MANUFACTURER -DNS, DEVICE -BK6004C, MODEL - DNS Airbook EGH602, PRODUCT - BK6004C
137 		EINK_DNS = MANUFACTURER.toLowerCase().contentEquals("dns") && MODEL.startsWith("DNS Airbook EGH");
138 
139 		EINK_TOLINO = (BRAND.toLowerCase().contentEquals("tolino") && (MODEL.toLowerCase().contentEquals("imx50_rdp")) ) || 		// SHINE
140 				(MODEL.toLowerCase().contentEquals("tolino") && DEVICE.toLowerCase().contentEquals("tolino_vision2")); //Tolino Vision HD4 doesn't show any Brand, only Model=tolino and  DEVICE=tolino_vision2)
141 
142 
143 		EINK_SCREEN = EINK_SONY || EINK_NOOK || EINK_ONYX || EINK_ENERGYSYSTEM || EINK_DNS || EINK_TOLINO; // TODO: set to true for eink devices like Nook Touch
144 
145 		// On Onyx Boox Monte Cristo 3 (and possible Monte Cristo, Monte Cristo 2) long press action on buttons are catch by system and not available for application
146 		// TODO: check this on other ONYX BOOX Readers
147 		ONYX_BUTTONS_LONG_PRESS_NOT_AVAILABLE = EINK_ONYX && MODEL.toLowerCase().startsWith("mc_kepler");
148 		boolean onyx_have_frontlight = false;
149 		boolean onyx_have_natural_backlight = false;
150 		int onyx_max_screen_brightness_value = 100;
151 		int onyx_max_screen_brightness_warm_value = 100;
152 		boolean onyx_support_regal = false;
153 		boolean onyx_have_brightness_system_dialog = false;
154 		if (EINK_ONYX) {
155 			onyx_support_regal = EpdController.supportRegal();
156 			Class<?> clazz = ReflectUtil.classForName("android.app.ActivityThread");
157 			Method method = ReflectUtil.getMethodSafely(clazz, "currentApplication");
158 			Application app = (Application) ReflectUtil.invokeMethodSafely(method, null);
159 			if (null != app) {
160 				onyx_have_frontlight = Device.currentDevice().hasFLBrightness(app);
161 				List<Integer> list = null;
162 				try {
163 					list = Device.currentDevice().getFrontLightValueList(app);
164 				} catch (Exception ignored) {}
165 				if (list != null && list.size() > 0) {
166 					onyx_max_screen_brightness_value = list.get(list.size() - 1);
167 					if (!onyx_have_frontlight) {
168 						// For ONYX BOOX MC3 and may be other too...
169 						onyx_have_frontlight = true;
170 					}
171 				}
172 				// natural (cold & warm) backlight support
173 				onyx_have_natural_backlight = Device.currentDevice().hasCTMBrightness(app);
174 				if (onyx_have_natural_backlight) {
175 					Integer[] values = Device.currentDevice().getWarmLightValues(app);
176 					if (values != null && values.length > 0) {
177 						onyx_max_screen_brightness_warm_value = values[values.length - 1];
178 					}
179 				}
180 				if (!onyx_have_frontlight && onyx_have_natural_backlight) {
181 					onyx_have_frontlight = true;
182 					Integer[] values = Device.currentDevice().getColdLightValues(app);
183 					if (values != null && values.length > 0) {
184 						onyx_max_screen_brightness_value = values[values.length - 1];
185 					}
186 				}
187 			}
188 			switch (Device.currentDeviceIndex()) {
189 				case Rk31xx:
190 				case Rk32xx:
191 				case Rk33xx:
192 				case SDM:
193 					onyx_have_brightness_system_dialog = true;
194 					break;
195 			}
196 		}
197 		ONYX_HAVE_BRIGHTNESS_SYSTEM_DIALOG = onyx_have_brightness_system_dialog;
198 		ONYX_HAVE_FRONTLIGHT = onyx_have_frontlight;
199 		ONYX_HAVE_NATURAL_BACKLIGHT = onyx_have_natural_backlight;
200 		MAX_SCREEN_BRIGHTNESS_VALUE = onyx_max_screen_brightness_value;
201 		MAX_SCREEN_BRIGHTNESS_WARM_VALUE = onyx_max_screen_brightness_warm_value;
202 
203 		EINK_SCREEN_REGAL = onyx_support_regal;		// TODO: add other e-ink devices with regal support
204 
205 		EINK_HAVE_FRONTLIGHT = ONYX_HAVE_FRONTLIGHT; // TODO: add other e-ink devices with frontlight support
206 		EINK_HAVE_NATURAL_BACKLIGHT = ONYX_HAVE_NATURAL_BACKLIGHT;	// TODO: add other e-ink devices with natural backlight support
207 
208 		POCKETBOOK = MODEL.toLowerCase().startsWith("pocketbook") || MODEL.toLowerCase().startsWith("obreey");
209 
210 		NOOK_NAVIGATION_KEYS = EINK_NOOK; // TODO: add autodetect
211 		SONY_NAVIGATION_KEYS = EINK_SONY;
212 		EINK_SCREEN_UPDATE_MODES_SUPPORTED = EINK_SCREEN && ( EINK_NOOK || EINK_TOLINO || EINK_ONYX ); // TODO: add autodetect
213 		FORCE_HC_THEME = EINK_SCREEN || MODEL.equalsIgnoreCase("pocketbook vision");
214 		USE_CUSTOM_TOAST = EINK_SCREEN;
215 		NOFLIBUSTA = POCKETBOOK;
216 		NAVIGATE_LEFTRIGHT = POCKETBOOK && DEVICE.startsWith("EP10");
217 		REVERT_LANDSCAPE_VOLUME_KEYS = POCKETBOOK && DEVICE.startsWith("EP5A");
218 		MIN_SCREEN_BRIGHTNESS_VALUE = getMinBrightness(AMOLED_SCREEN ? 2 : (getSDKLevel() >= ICE_CREAM_SANDWICH ? 8 : 16));
219 		//BUFFER_COLOR_FORMAT = getSDKLevel() >= HONEYCOMB ? android.graphics.Bitmap.Config.ARGB_8888 : android.graphics.Bitmap.Config.RGB_565;
220 		//BUFFER_COLOR_FORMAT = android.graphics.Bitmap.Config.ARGB_8888;
221 		BUFFER_COLOR_FORMAT = EINK_SCREEN || USE_OPENGL ? android.graphics.Bitmap.Config.ARGB_8888 : android.graphics.Bitmap.Config.RGB_565;
222 		PIXEL_FORMAT = (DeviceInfo.BUFFER_COLOR_FORMAT == android.graphics.Bitmap.Config.RGB_565) ? PixelFormat.RGB_565 : PixelFormat.RGBA_8888;
223 
224 		DEF_FONT_FACE = getSDKLevel() >= ICE_CREAM_SANDWICH ? "Roboto" : "Droid Sans";
225 
226 		USE_BITMAP_MEMORY_HACK = getSDKLevel() < ICE_CREAM_SANDWICH;
227 		ONE_COLUMN_IN_LANDSCAPE = POCKETBOOK && DEVICE.endsWith("SURFPAD");
228 		DEF_FONT_SIZE = POCKETBOOK && DEVICE.endsWith("SURFPAD") ? 18 : null;
229 	}
230 
231 	private static String getBuildField(String fieldName) {
232 
233 		try {
234 			return (String)Build.class.getField(fieldName).get(null);
235 		} catch (Exception e) {
236 			Log.d("cr3", "Exception while trying to check Build." + fieldName);
237 			return "";
238 		}
239 	}
240 
241 
242 	static {
243 		Log.i("cr3", "DeviceInfo: MANUFACTURER=" + MANUFACTURER + ", MODEL=" + MODEL + ", DEVICE=" + DEVICE + ", PRODUCT=" + PRODUCT + ", BRAND=" + BRAND);
244 		Log.i("cr3", "DeviceInfo: MIN_SCREEN_BRIGHTNESS_VALUE=" + MIN_SCREEN_BRIGHTNESS_VALUE + "; MAX_SCREEN_BRIGHTNESS_VALUE=" + MAX_SCREEN_BRIGHTNESS_VALUE + "; EINK_SCREEN=" + EINK_SCREEN + "; EINK_SCREEN_REGAL=" + EINK_SCREEN_REGAL + ", AMOLED_SCREEN=" + AMOLED_SCREEN + ", POCKETBOOK=" + POCKETBOOK);
245 	}
246 
247 	// multiple patterns divided by |, * wildcard can be placed at beginning and/or end of pattern
248 	// samples: "samsung", "p500|p510", "sgs*|sgh*"
249 	private static boolean match(String value, String pattern) {
250 		if (pattern == null || pattern.length() == 0 || "*".equals(pattern))
251 			return true; // matches any value
252 		if (value == null || value.length() == 0)
253 			return false;
254 		value = value.toLowerCase();
255 		pattern = pattern.toLowerCase();
256 		String[] patterns = pattern.split("\\|");
257 		for (String p : patterns) {
258 			boolean startingWildcard = false;
259 			boolean endingWildcard = false;
260 			if (p.startsWith("*")) {
261 				startingWildcard = true;
262 				p = p.substring(1);
263 			}
264 			if (p.endsWith("*")) {
265 				endingWildcard = true;
266 				p = p.substring(0, p.length()-1);
267 			}
268 			if (startingWildcard && endingWildcard) {
269 				if (value.indexOf(p) < 0)
270 					return false;
271 			} else if (startingWildcard) {
272 				if (!value.endsWith(p))
273 					return false;
274 			} else if (endingWildcard) {
275 				if (!value.startsWith(p))
276 					return false;
277 			} else {
278 				if (!value.equals(p))
279 					return false;
280 			}
281 		}
282 		return true;
283 	}
284 
285 	// delimited by ;
286 	// "manufacturer;model;device;brand", "manufacturer;model;device" or "manufacturer;model" or "manufacturer"
287 	private static boolean matchDevice(String pattern) {
288 		String[] patterns = pattern.split(";");
289 		if (patterns.length >= 1)
290 			if (!match(MANUFACTURER, patterns[0]))
291 				return false;
292 		if (patterns.length >= 2)
293 			if (!match(MODEL, patterns[1]))
294 				return false;
295 		if (patterns.length >= 3)
296 			if (!match(DEVICE, patterns[2]))
297 				return false;
298 		if (patterns.length >= 4)
299 			if (!match(BRAND, patterns[3]))
300 				return false;
301 		return true;
302 	}
303 
304 //	// TEST
305 //	private static boolean testMatchDevice(String manufacturer, String model, String device, String pattern) {
306 //		String[] patterns = pattern.split(";");
307 //		if (patterns.length >= 1)
308 //			if (!match(manufacturer, patterns[0]))
309 //				return false;
310 //		if (patterns.length >= 2)
311 //			if (!match(model, patterns[1]))
312 //				return false;
313 //		if (patterns.length >= 3)
314 //			if (!match(device, patterns[2]))
315 //				return false;
316 //		Log.v("cr3", "matched : " + pattern + " == " + manufacturer + "," + model + "," + device);
317 //		return true;
318 //	}
319 //
320 //	static {
321 //		testMatchDevice("Archos", "A70S", "A70S", "Archos;A70S");
322 //		testMatchDevice("MegaMan", "A70S", "A70S", "mega*;A70*");
323 //		testMatchDevice("MegaMan", "A70", "A70S", "*man;A70*");
324 //	}
325 
getMinBrightness(int defValue)326 	private static int getMinBrightness(int defValue) {
327 		try {
328 			for (int i=0; i<MIN_SCREEN_BRIGHTNESS_DB.length - 1; i+=2) {
329 				if (matchDevice(MIN_SCREEN_BRIGHTNESS_DB[i])) {
330 					return Integer.valueOf(MIN_SCREEN_BRIGHTNESS_DB[i+1]);
331 				}
332 			}
333 		} catch (NumberFormatException e) {
334 			// ignore
335 		}
336 		return defValue;
337 	}
338 
339 }
340