1 package org.coolreader.crengine;
2 
3 import android.content.Context;
4 import android.util.Log;
5 import android.view.View;
6 
7 import com.onyx.android.sdk.api.device.epd.EpdController;
8 import com.onyx.android.sdk.api.device.epd.UpdateMode;
9 import com.onyx.android.sdk.device.Device;
10 
11 import org.coolreader.CoolReader;
12 
13 import java.util.Arrays;
14 import java.util.List;
15 
16 public class EinkScreenOnyx implements EinkScreen {
17 
18 	public static final Logger log = L.create("onyx", Log.VERBOSE);
19 
20 	private EinkUpdateMode mUpdateMode = EinkUpdateMode.Unspecified;
21 	private int mUpdateInterval;
22 	private int mRefreshNumber = -1;
23 	private boolean mInFastMode = false;
24 	private boolean mInA2Mode = false;
25 	// Front light levels
26 	private List<Integer> mFrontLineLevels = null;
27 	private List<Integer> mWarmLightLevels = null;
28 	private UpdateMode mOnyxUpdateMode = UpdateMode.None;
29 	private int mExtraDelayFullRefresh = 0;
30 
31 	@Override
setupController(EinkUpdateMode mode, int updateInterval, View view)32 	public void setupController(EinkUpdateMode mode, int updateInterval, View view) {
33 		mUpdateInterval = updateInterval;
34 		if (mUpdateMode.equals(mode))
35 			return;
36 		log.d("EinkScreenOnyx.setupController(): mode=" + mode);
37 		EpdController.enableScreenUpdate(view, true);
38 		mRefreshNumber = 0;
39 		EpdController.clearApplicationFastMode();
40 		UpdateMode onyxFastUpdateMode = UpdateMode.DU;
41 		switch (Device.currentDeviceIndex()) {
42 			case Rk32xx:
43 			case Rk33xx:
44 			case SDM:
45 				onyxFastUpdateMode = UpdateMode.DU_QUALITY;
46 				break;
47 		}
48 		switch (Device.currentDeviceIndex()) {
49 			// TODO: check other ONYX devices & platforms
50 			case SDM:
51 				// Hack, use additional delay before full screen update
52 				mExtraDelayFullRefresh = 20;
53 				break;
54 		}
55 		switch (mode) {
56 			case Regal:            // Regal
57 				if (mInA2Mode) {
58 					onyxEnableA2Mode(view, false);
59 					mInA2Mode = false;
60 				}
61 				if (mInFastMode) {
62 					EpdController.applyApplicationFastMode(CoolReader.class.getSimpleName(), false, true);
63 					mInFastMode = false;
64 				}
65 				mOnyxUpdateMode = UpdateMode.REGAL;
66 				break;
67 			case Clear:            // Quality
68 				if (mInA2Mode) {
69 					onyxEnableA2Mode(view, false);
70 					mInA2Mode = false;
71 				}
72 				if (mInFastMode) {
73 					EpdController.applyApplicationFastMode(CoolReader.class.getSimpleName(), false, true);
74 					mInFastMode = false;
75 				}
76 				mOnyxUpdateMode = UpdateMode.GU;
77 				break;
78 			case Fast:            // Fast
79 				if (mInA2Mode) {
80 					onyxEnableA2Mode(view, false);
81 					mInA2Mode = false;
82 				}
83 				// Enable fast mode (not implemented on RK3026)
84 				if (!mInFastMode) {
85 					EpdController.applyApplicationFastMode(CoolReader.class.getSimpleName(), true, true, UpdateMode.DU_QUALITY, Integer.MAX_VALUE);
86 					mInFastMode = true;
87 				}
88 				mOnyxUpdateMode = onyxFastUpdateMode;
89 				break;
90 			case A2:            // A2 mode
91 				if (mInFastMode) {
92 					EpdController.applyApplicationFastMode(CoolReader.class.getSimpleName(), false, true);
93 					mInFastMode = false;
94 				}
95 				if (!mInA2Mode) {
96 					onyxEnableA2Mode(view, true);
97 					mInA2Mode = true;
98 				}
99 				mOnyxUpdateMode = onyxFastUpdateMode;
100 				break;
101 			default:
102 				mOnyxUpdateMode = UpdateMode.GU;
103 		}
104 		if (null != view) {
105 			EpdController.setViewDefaultUpdateMode(view, mOnyxUpdateMode);
106 			BackgroundThread.instance().executeGUI(view::invalidate);
107 		}
108 		mUpdateMode = mode;
109 	}
110 
111 	@Override
prepareController(View view, boolean isPartially)112 	public void prepareController(View view, boolean isPartially) {
113 		if (mRefreshNumber == -1) {
114 			mRefreshNumber = 0;
115 			onyxRepaintEveryThing(view, false);
116 			return;
117 		}
118 		if (mUpdateInterval > 0) {
119 			mRefreshNumber++;
120 			if (mRefreshNumber >= mUpdateInterval) {
121 				mRefreshNumber = 0;
122 				return;
123 			}
124 		}
125 		if (mRefreshNumber > 0 || mUpdateInterval == 0) {
126 			EpdController.setViewDefaultUpdateMode(view, mOnyxUpdateMode);
127 			if (Device.DeviceIndex.Rk32xx == Device.currentDeviceIndex()) {
128 				// Hack, without it, the image on rk3288 will not updated.
129 				// Found by brute force.
130 				EpdController.byPass(0);
131 			}
132 		}
133 	}
134 
135 	@Override
updateController(View view, boolean isPartially)136 	public void updateController(View view, boolean isPartially) {
137 		if (0 == mRefreshNumber && mUpdateInterval > 0) {
138 			if (mExtraDelayFullRefresh > 0) {
139 				// Hack, on ONYX devices with SDM platform without this delay full screen refresh runs too early
140 				// (before new page appears on screen)
141 				// This functions called after android.view.SurfaceHolder.unlockCanvasAndPost()
142 				//   See https://developer.android.com/reference/android/view/SurfaceHolder#unlockCanvasAndPost(android.graphics.Canvas)
143 				// which guarantees that by this time the new image will be on the screen
144 				// But in fact on com.onyx.android.sdk.device.Device.DeviceIndex.SDM need extra delay.
145 				try {
146 					Thread.sleep(mExtraDelayFullRefresh);
147 				} catch (InterruptedException ignored) {
148 				}
149 			}
150 			onyxRepaintEveryThing(view, false);
151 		}
152 	}
153 
154 	@Override
refreshScreen(View view)155 	public void refreshScreen(View view) {
156 		onyxRepaintEveryThing(view, true);
157 		mRefreshNumber = 0;
158 	}
159 
160 	@Override
getUpdateMode()161 	public EinkUpdateMode getUpdateMode() {
162 		return mUpdateMode;
163 	}
164 
165 	@Override
getUpdateInterval()166 	public int getUpdateInterval() {
167 		return mUpdateInterval;
168 	}
169 
170 	@Override
getFrontLightValue(Context context)171 	public int getFrontLightValue(Context context) {
172 		int res = 0;
173 		try {
174 			if (DeviceInfo.ONYX_HAVE_NATURAL_BACKLIGHT) {
175 				res = Device.currentDevice().getColdLightConfigValue(context);
176 			} else {
177 				res = Device.currentDevice().getFrontLightDeviceValue(context);
178 			}
179 		} catch (Exception ignored) {}
180 		return res;
181 	}
182 
183 	@Override
setFrontLightValue(Context context, int value)184 	public boolean setFrontLightValue(Context context, int value) {
185 		boolean res = false;
186 		if (DeviceInfo.ONYX_HAVE_FRONTLIGHT) {
187 			if (value >= 0) {
188 				Integer alignedValue = Utils.findNearestValue(getFrontLightLevels(context), value);
189 				if (null != alignedValue) {
190 					if (DeviceInfo.ONYX_HAVE_NATURAL_BACKLIGHT) {
191 						res = Device.currentDevice().setColdLightDeviceValue(context, alignedValue);
192 					} else {
193 						if (Device.currentDevice().setFrontLightDeviceValue(context, alignedValue))
194 							res = Device.currentDevice().setFrontLightConfigValue(context, alignedValue);
195 					}
196 				}
197 			} else {
198 				// system default, just ignore
199 			}
200 		}
201 		return res;
202 	}
203 
204 	@Override
getWarmLightValue(Context context)205 	public int getWarmLightValue(Context context) {
206 		int res = 0;
207 		try {
208 			if (DeviceInfo.ONYX_HAVE_NATURAL_BACKLIGHT) {
209 				res = Device.currentDevice().getWarmLightConfigValue(context);
210 			}
211 		} catch (Exception ignored) {}
212 		return res;
213 	}
214 
215 	@Override
setWarmLightValue(Context context, int value)216 	public boolean setWarmLightValue(Context context, int value) {
217 		boolean res = false;
218 		if (DeviceInfo.ONYX_HAVE_NATURAL_BACKLIGHT) {
219 			if (value >= 0) {
220 				Integer alignedValue = Utils.findNearestValue(getWarmLightLevels(context), value);
221 				if (null != alignedValue) {
222 					res = Device.currentDevice().setWarmLightDeviceValue(context, alignedValue);
223 				}
224 			} else {
225 				// system default, just ignore
226 			}
227 		}
228 		return res;
229 	}
230 
231 	@Override
getFrontLightLevels(Context context)232 	public List<Integer> getFrontLightLevels(Context context) {
233 		if (DeviceInfo.ONYX_HAVE_FRONTLIGHT || DeviceInfo.ONYX_HAVE_NATURAL_BACKLIGHT) {
234 			if (null == mFrontLineLevels) {
235 				try {
236 					mFrontLineLevels = Device.currentDevice().getFrontLightValueList(context);
237 				} catch (Exception ignored) { }
238 				if (null == mFrontLineLevels || mFrontLineLevels.size() == 0) {
239 					Integer[] values = Device.currentDevice().getColdLightValues(context);
240 					if (null != values) {
241 						mFrontLineLevels = Arrays.asList(values);
242 					}
243 				}
244 			}
245 		}
246 		return mFrontLineLevels;
247 	}
248 
249 	@Override
getWarmLightLevels(Context context)250 	public List<Integer> getWarmLightLevels(Context context) {
251 		if (DeviceInfo.EINK_HAVE_NATURAL_BACKLIGHT) {
252 			if (null == mWarmLightLevels) {
253 				if (DeviceInfo.ONYX_HAVE_NATURAL_BACKLIGHT) {
254 					Integer[] values = Device.currentDevice().getWarmLightValues(context);
255 					if (null != values) {
256 						mWarmLightLevels = Arrays.asList(values);
257 					}
258 				}
259 			}
260 		}
261 		return mWarmLightLevels;
262 	}
263 
264 
265 	// private methods
onyxRepaintEveryThing(View view, boolean invalidate)266 	private void onyxRepaintEveryThing(View view, boolean invalidate) {
267 		switch (Device.currentDeviceIndex()) {
268 			case Rk31xx:
269 			case Rk32xx:
270 			case Rk33xx:
271 			case SDM:
272 				EpdController.repaintEveryThing(UpdateMode.GC);
273 				break;
274 			default:
275 				if (null != view) {
276 					EpdController.setViewDefaultUpdateMode(view, UpdateMode.GC);
277 					if (invalidate)
278 						view.postInvalidate();
279 				}
280 				break;
281 		}
282 	}
283 
onyxEnableA2Mode(View view, boolean enable)284 	private void onyxEnableA2Mode(View view, boolean enable) {
285 		switch (Device.currentDeviceIndex()) {
286 			case Rk3026:
287 			case imx6:
288 			case imx7:
289 				if (enable)
290 					EpdController.enableA2ForSpecificView(view);
291 				else
292 					EpdController.disableA2ForSpecificView(view);
293 				break;
294 			default:
295 				EpdController.clearApplicationFastMode();
296 				if (enable)
297 					EpdController.applyApplicationFastMode(CoolReader.class.getSimpleName(), true, true, UpdateMode.ANIMATION_QUALITY, Integer.MAX_VALUE);
298 				else
299 					EpdController.applyApplicationFastMode(CoolReader.class.getSimpleName(), false, true);
300 				break;
301 		}
302 	}
303 }
304