修改换行符为 LF
This commit is contained in:
1 parent
ed0702068c
commit
f20bc2c954
51 files changed
+6729
-6729
No files matched your search
+9
-9
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
+120
-120
@@ -1,120 +1,120 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.hippo.ehviewer"
|
||||
android:versionCode="15"
|
||||
android:versionName="0.5.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:name=".AppContext"
|
||||
android:allowBackup="true"
|
||||
android:largeHeap="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" >
|
||||
|
||||
<activity
|
||||
android:name=".StartActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme.NoActionBar.Transparent"
|
||||
android:screenOrientation="portrait" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaListActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:theme="@style/AppTheme.Main.ListActivity"
|
||||
android:launchMode="singleTop" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaDetailActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|adjustUnspecified|stateHidden"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTask" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http" />
|
||||
<data android:host="exhentai.org" />
|
||||
<data android:host="g.e-hentai.org" />
|
||||
<data android:host="lofi.e-hentai.org" />
|
||||
<data android:pathPrefix="/g/" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:label="@string/preference_settings_title"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.FavouriteActivity"
|
||||
android:label="@string/favourite"
|
||||
android:icon="@drawable/ic_action_favorite"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:theme="@style/AppTheme.ListActivity" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.DownloadActivity"
|
||||
android:label="@string/download"
|
||||
android:icon="@drawable/ic_action_download"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTask"
|
||||
android:parentActivityName="com.hippo.ehviewer.activity.MangaListActivity"
|
||||
android:theme="@style/AppTheme.ListActivity" >
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.hippo.ehviewer.activity.MangaListActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaDownloadActivity"
|
||||
android:label="@string/download"
|
||||
android:icon="@drawable/ic_action_download"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize" >
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.DownloadService"
|
||||
android:label="DownloadService" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.hippo.ehviewer"
|
||||
android:versionCode="15"
|
||||
android:versionName="0.5.0" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="19" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:name=".AppContext"
|
||||
android:allowBackup="true"
|
||||
android:largeHeap="false"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme" >
|
||||
|
||||
<activity
|
||||
android:name=".StartActivity"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme.NoActionBar.Transparent"
|
||||
android:screenOrientation="portrait" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaListActivity"
|
||||
android:label="@string/app_name"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:theme="@style/AppTheme.Main.ListActivity"
|
||||
android:launchMode="singleTop" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEARCH" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaDetailActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="adjustPan|adjustUnspecified|stateHidden"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTask" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="http" />
|
||||
<data android:host="exhentai.org" />
|
||||
<data android:host="g.e-hentai.org" />
|
||||
<data android:host="lofi.e-hentai.org" />
|
||||
<data android:pathPrefix="/g/" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.SettingsActivity"
|
||||
android:label="@string/preference_settings_title"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.FavouriteActivity"
|
||||
android:label="@string/favourite"
|
||||
android:icon="@drawable/ic_action_favorite"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:theme="@style/AppTheme.ListActivity" >
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.DownloadActivity"
|
||||
android:label="@string/download"
|
||||
android:icon="@drawable/ic_action_download"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:launchMode="singleTask"
|
||||
android:parentActivityName="com.hippo.ehviewer.activity.MangaListActivity"
|
||||
android:theme="@style/AppTheme.ListActivity" >
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.hippo.ehviewer.activity.MangaListActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".activity.MangaDownloadActivity"
|
||||
android:label="@string/download"
|
||||
android:icon="@drawable/ic_action_download"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize" >
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.DownloadService"
|
||||
android:label="DownloadService" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,18 +1,18 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<com.hippo.ehviewer.gallery.ui.GLRootView
|
||||
android:id="@+id/gl_root_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
<!--
|
||||
View android:id="@+id/gl_root_cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#ff888888"/
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<com.hippo.ehviewer.gallery.ui.GLRootView
|
||||
android:id="@+id/gl_root_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
<!--
|
||||
View android:id="@+id/gl_root_cover"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#ff888888"/
|
||||
-->
|
||||
|
||||
</RelativeLayout>
|
||||
+49
-49
@@ -1,50 +1,50 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" >
|
||||
|
||||
<com.hippo.ehviewer.widget.FswView
|
||||
android:id="@+id/alignment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true" />
|
||||
|
||||
<com.hippo.ehviewer.widget.HfListView
|
||||
android:id="@+id/list_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/list_wait_first"
|
||||
style="@style/ProgressBar.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/list_refresh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:onClick="buttonRefresh"
|
||||
android:text="@string/refresh"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sadpanda"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/sadpanda"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_no_found"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="@string/no_found"
|
||||
android:textSize="24sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" >
|
||||
|
||||
<com.hippo.ehviewer.widget.FswView
|
||||
android:id="@+id/alignment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true" />
|
||||
|
||||
<com.hippo.ehviewer.widget.HfListView
|
||||
android:id="@+id/list_list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/list_wait_first"
|
||||
style="@style/ProgressBar.Large"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/list_refresh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:onClick="buttonRefresh"
|
||||
android:text="@string/refresh"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sadpanda"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:src="@drawable/sadpanda"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/list_no_found"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:text="@string/no_found"
|
||||
android:textSize="24sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
||||
+73
-73
@@ -1,74 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/list_thumb_height"
|
||||
android:background="@drawable/list_item_bg" >
|
||||
|
||||
<com.hippo.ehviewer.widget.LoadImageView
|
||||
android:id="@+id/cover"
|
||||
android:layout_width="@dimen/list_thumb_width"
|
||||
android:layout_height="@dimen/list_thumb_height"
|
||||
android:scaleType="fitCenter" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
style="@style/CardTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@id/cover"
|
||||
android:layout_marginLeft="8.0dip"
|
||||
android:layout_marginTop="1.0dip"
|
||||
android:layout_marginRight="4.0dip" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uploader"
|
||||
style="@style/CardSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/name"
|
||||
android:layout_alignLeft="@id/name"
|
||||
android:layout_marginRight="4.0dip" />
|
||||
|
||||
<RatingBar
|
||||
android:id="@+id/rate"
|
||||
style="@style/RatingBarSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/uploader"
|
||||
android:layout_alignLeft="@id/name"
|
||||
android:layout_marginTop="2.0dip"
|
||||
android:layout_marginRight="4.0dip" />
|
||||
|
||||
<!-- RelativeLayout layout_marginBottom layout_below -->
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignLeft="@id/name" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category"
|
||||
style="@style/CardSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5.0dp"
|
||||
android:paddingTop="2.0dp"
|
||||
android:paddingBottom="2.0dp"
|
||||
android:paddingLeft="8.0dp"
|
||||
android:paddingRight="8.0dp"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/posted"
|
||||
style="@style/CardSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginRight="4.0dip"
|
||||
android:paddingBottom="1.0dp" />
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="@dimen/list_thumb_height"
|
||||
android:background="@drawable/list_item_bg" >
|
||||
|
||||
<com.hippo.ehviewer.widget.LoadImageView
|
||||
android:id="@+id/cover"
|
||||
android:layout_width="@dimen/list_thumb_width"
|
||||
android:layout_height="@dimen/list_thumb_height"
|
||||
android:scaleType="fitCenter" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
style="@style/CardTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@id/cover"
|
||||
android:layout_marginLeft="8.0dip"
|
||||
android:layout_marginTop="1.0dip"
|
||||
android:layout_marginRight="4.0dip" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/uploader"
|
||||
style="@style/CardSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/name"
|
||||
android:layout_alignLeft="@id/name"
|
||||
android:layout_marginRight="4.0dip" />
|
||||
|
||||
<RatingBar
|
||||
android:id="@+id/rate"
|
||||
style="@style/RatingBarSmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/uploader"
|
||||
android:layout_alignLeft="@id/name"
|
||||
android:layout_marginTop="2.0dip"
|
||||
android:layout_marginRight="4.0dip" />
|
||||
|
||||
<!-- RelativeLayout layout_marginBottom layout_below -->
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignLeft="@id/name" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/category"
|
||||
style="@style/CardSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="5.0dp"
|
||||
android:paddingTop="2.0dp"
|
||||
android:paddingBottom="2.0dp"
|
||||
android:paddingLeft="8.0dp"
|
||||
android:paddingRight="8.0dp"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/posted"
|
||||
style="@style/CardSubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginRight="4.0dip"
|
||||
android:paddingBottom="1.0dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
+43
-43
@@ -1,44 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<integer-array name="refresh_colors">
|
||||
<item>@color/refresh_color_1</item>
|
||||
<item>@color/refresh_color_2</item>
|
||||
<item>@color/refresh_color_3</item>
|
||||
<item>@color/refresh_color_4</item>
|
||||
</integer-array>
|
||||
|
||||
<color name="refresh_color_1">#f4b400</color>
|
||||
<color name="refresh_color_2">#3e802f</color>
|
||||
<color name="refresh_color_3">#427fed</color>
|
||||
<color name="refresh_color_4">#b23424</color>
|
||||
|
||||
<color name="ab_bg">@android:color/holo_blue_dark</color>
|
||||
<color name="main_background">#ffe5e5e5</color>
|
||||
<color name="holo_blue_light_light">#ff81d3f7</color>
|
||||
|
||||
<color name="light_gray">#aaa</color>
|
||||
<color name="lighter_gray">#ddd</color>
|
||||
|
||||
<color name="doujinshi_bg">#fff02d2d</color>
|
||||
<color name="manga_bg">#ffffa037</color>
|
||||
<color name="artist_cg_bg">#ffffd84f</color>
|
||||
<color name="game_cg_bg">#ff00b26f</color>
|
||||
<color name="western_bg">#ffa2df86</color>
|
||||
<color name="non_h_bg">#ff4ec8f7</color>
|
||||
<color name="image_set_bg">#ff4272d7</color>
|
||||
<color name="cosplay_bg">#ff8058bd</color>
|
||||
<color name="asian_porn_bg">#ffdda0f6</color>
|
||||
<color name="misc_bg">#ffeaccf3</color>
|
||||
<color name="unknown_bg">@android:color/black</color>
|
||||
|
||||
<color name="title_primary">#ff333333</color>
|
||||
<color name="title_secondary">#ffaaaaaa</color>
|
||||
|
||||
<color name="detail_title_secondary">#ff777777</color>
|
||||
|
||||
<color name="detail_divider">#22000000</color>
|
||||
|
||||
|
||||
<color name="btn_white_default">#fff0f0f0</color>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<integer-array name="refresh_colors">
|
||||
<item>@color/refresh_color_1</item>
|
||||
<item>@color/refresh_color_2</item>
|
||||
<item>@color/refresh_color_3</item>
|
||||
<item>@color/refresh_color_4</item>
|
||||
</integer-array>
|
||||
|
||||
<color name="refresh_color_1">#f4b400</color>
|
||||
<color name="refresh_color_2">#3e802f</color>
|
||||
<color name="refresh_color_3">#427fed</color>
|
||||
<color name="refresh_color_4">#b23424</color>
|
||||
|
||||
<color name="ab_bg">@android:color/holo_blue_dark</color>
|
||||
<color name="main_background">#ffe5e5e5</color>
|
||||
<color name="holo_blue_light_light">#ff81d3f7</color>
|
||||
|
||||
<color name="light_gray">#aaa</color>
|
||||
<color name="lighter_gray">#ddd</color>
|
||||
|
||||
<color name="doujinshi_bg">#fff02d2d</color>
|
||||
<color name="manga_bg">#ffffa037</color>
|
||||
<color name="artist_cg_bg">#ffffd84f</color>
|
||||
<color name="game_cg_bg">#ff00b26f</color>
|
||||
<color name="western_bg">#ffa2df86</color>
|
||||
<color name="non_h_bg">#ff4ec8f7</color>
|
||||
<color name="image_set_bg">#ff4272d7</color>
|
||||
<color name="cosplay_bg">#ff8058bd</color>
|
||||
<color name="asian_porn_bg">#ffdda0f6</color>
|
||||
<color name="misc_bg">#ffeaccf3</color>
|
||||
<color name="unknown_bg">@android:color/black</color>
|
||||
|
||||
<color name="title_primary">#ff333333</color>
|
||||
<color name="title_secondary">#ffaaaaaa</color>
|
||||
|
||||
<color name="detail_title_secondary">#ff777777</color>
|
||||
|
||||
<color name="detail_divider">#22000000</color>
|
||||
|
||||
|
||||
<color name="btn_white_default">#fff0f0f0</color>
|
||||
</resources>
|
||||
+158
-158
@@ -1,158 +1,158 @@
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="ActionBar" parent="@android:style/Widget.Holo.ActionBar">
|
||||
<item name="android:background">@drawable/ab_bg</item>
|
||||
<item name="android:backgroundStacked">@drawable/ab_bg_stacked</item>
|
||||
<item name="android:backgroundSplit">@drawable/ab_bg_bottom</item>
|
||||
<item name="android:titleTextStyle">@style/ActionBarTitleFont</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBarTitleFont" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBarTabStyle" parent="@android:style/Widget.Holo.Light.ActionBar.TabView">
|
||||
<item name="android:background">@drawable/ab_tab_indicator</item>
|
||||
</style>
|
||||
|
||||
<style name="PopupMenu" parent="@android:style/Widget.Holo.Light.ListPopupWindow">
|
||||
</style>
|
||||
|
||||
|
||||
<!-- Button -->
|
||||
<style name="Button" parent="@android:style/Widget.Button">
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:paddingLeft">12dp</item>
|
||||
<item name="android:paddingTop">12dp</item>
|
||||
<item name="android:paddingRight">12dp</item>
|
||||
<item name="android:paddingBottom">12dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Blue">
|
||||
<item name="android:background">@drawable/blue_bn_bg</item>
|
||||
<item name="android:textColor">@color/blue_bn_text</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.White">
|
||||
<item name="android:background">@drawable/white_bn_bg</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:paddingLeft">12dp</item>
|
||||
<item name="android:paddingTop">2dp</item>
|
||||
<item name="android:paddingRight">12dp</item>
|
||||
<item name="android:paddingBottom">2dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Blue">
|
||||
<item name="android:background">@drawable/blue_bn_bg</item>
|
||||
<item name="android:textColor">@color/blue_bn_text</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.White">
|
||||
<item name="android:background">@drawable/white_bn_bg</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Flat">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:paddingLeft">18dp</item>
|
||||
<item name="android:paddingTop">2dp</item>
|
||||
<item name="android:paddingRight">18dp</item>
|
||||
<item name="android:paddingBottom">2dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Flat.Blue">
|
||||
<item name="android:background">@drawable/blue_bn_bg</item>
|
||||
<item name="android:textColor">@color/blue_bn_text</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Flat.White">
|
||||
<item name="android:background">@drawable/white_bn_bg</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="ProgressBar" parent="@android:Widget.Holo.ProgressBar">
|
||||
</style>
|
||||
|
||||
<style name="ProgressBar.Small">
|
||||
<item name="android:indeterminateDrawable">@drawable/progress_small</item>
|
||||
<item name="android:minWidth">16dip</item>
|
||||
<item name="android:maxWidth">16dip</item>
|
||||
<item name="android:minHeight">16dip</item>
|
||||
<item name="android:maxHeight">16dip</item>
|
||||
</style>
|
||||
|
||||
<style name="ProgressBar.Large">
|
||||
<item name="android:indeterminateDrawable">@drawable/progress_large</item>
|
||||
<item name="android:minWidth">76dip</item>
|
||||
<item name="android:maxWidth">76dip</item>
|
||||
<item name="android:minHeight">76dip</item>
|
||||
<item name="android:maxHeight">76dip</item>
|
||||
</style>
|
||||
|
||||
<!-- Text -->
|
||||
<style name="CardTitle">
|
||||
<item name="android:textColor">@color/title_primary</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:lineSpacingExtra">0.0sp</item>
|
||||
</style>
|
||||
|
||||
<style name="CardSubtitle">
|
||||
<item name="android:textSize">@dimen/text_medium</item>
|
||||
<item name="android:textColor">@color/title_secondary</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:drawablePadding">4.0dip</item>
|
||||
<item name="android:lineSpacingExtra">0.0sp</item>
|
||||
</style>
|
||||
|
||||
<style name="DetailTitle">
|
||||
<item name="android:textSize">@dimen/detail_title_size</item>
|
||||
<item name="android:textColor">@color/title_primary</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:lineSpacingExtra">0.0sp</item>
|
||||
</style>
|
||||
|
||||
<style name="DetailSubtitle">
|
||||
<item name="android:textSize">@dimen/detail_subtitle_size</item>
|
||||
<item name="android:textColor">@color/detail_title_secondary</item>
|
||||
<item name="android:ellipsize">marquee</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextTag">
|
||||
<item name="android:textSize">@dimen/detail_subtitle_size</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
<item name="android:background">@android:color/black</item>
|
||||
<item name="android:ellipsize">marquee</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:paddingTop">2.0dp</item>
|
||||
<item name="android:paddingBottom">2.0dp</item>
|
||||
<item name="android:paddingLeft">8.0dp</item>
|
||||
<item name="android:paddingRight">8.0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="RatingBarSmall">
|
||||
<item name="android:background">@null</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:button">@null</item>
|
||||
<item name="android:maxHeight">@dimen/rating_bar_small_height</item>
|
||||
<item name="android:progressDrawable">@drawable/ratingbar_small</item>
|
||||
<item name="android:minHeight">@dimen/rating_bar_small_height</item>
|
||||
<item name="android:thumb">@null</item>
|
||||
<item name="android:numStars">5</item>
|
||||
<item name="android:stepSize">0.5</item>
|
||||
<item name="android:isIndicator">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
<resources xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<style name="ActionBar" parent="@android:style/Widget.Holo.ActionBar">
|
||||
<item name="android:background">@drawable/ab_bg</item>
|
||||
<item name="android:backgroundStacked">@drawable/ab_bg_stacked</item>
|
||||
<item name="android:backgroundSplit">@drawable/ab_bg_bottom</item>
|
||||
<item name="android:titleTextStyle">@style/ActionBarTitleFont</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBarTitleFont" parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBarTabStyle" parent="@android:style/Widget.Holo.Light.ActionBar.TabView">
|
||||
<item name="android:background">@drawable/ab_tab_indicator</item>
|
||||
</style>
|
||||
|
||||
<style name="PopupMenu" parent="@android:style/Widget.Holo.Light.ListPopupWindow">
|
||||
</style>
|
||||
|
||||
|
||||
<!-- Button -->
|
||||
<style name="Button" parent="@android:style/Widget.Button">
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:paddingLeft">12dp</item>
|
||||
<item name="android:paddingTop">12dp</item>
|
||||
<item name="android:paddingRight">12dp</item>
|
||||
<item name="android:paddingBottom">12dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Blue">
|
||||
<item name="android:background">@drawable/blue_bn_bg</item>
|
||||
<item name="android:textColor">@color/blue_bn_text</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.White">
|
||||
<item name="android:background">@drawable/white_bn_bg</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:paddingLeft">12dp</item>
|
||||
<item name="android:paddingTop">2dp</item>
|
||||
<item name="android:paddingRight">12dp</item>
|
||||
<item name="android:paddingBottom">2dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Blue">
|
||||
<item name="android:background">@drawable/blue_bn_bg</item>
|
||||
<item name="android:textColor">@color/blue_bn_text</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.White">
|
||||
<item name="android:background">@drawable/white_bn_bg</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Flat">
|
||||
<item name="android:textSize">14sp</item>
|
||||
<item name="android:paddingLeft">18dp</item>
|
||||
<item name="android:paddingTop">2dp</item>
|
||||
<item name="android:paddingRight">18dp</item>
|
||||
<item name="android:paddingBottom">2dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Flat.Blue">
|
||||
<item name="android:background">@drawable/blue_bn_bg</item>
|
||||
<item name="android:textColor">@color/blue_bn_text</item>
|
||||
</style>
|
||||
|
||||
<style name="Button.Small.Flat.White">
|
||||
<item name="android:background">@drawable/white_bn_bg</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="ProgressBar" parent="@android:Widget.Holo.ProgressBar">
|
||||
</style>
|
||||
|
||||
<style name="ProgressBar.Small">
|
||||
<item name="android:indeterminateDrawable">@drawable/progress_small</item>
|
||||
<item name="android:minWidth">16dip</item>
|
||||
<item name="android:maxWidth">16dip</item>
|
||||
<item name="android:minHeight">16dip</item>
|
||||
<item name="android:maxHeight">16dip</item>
|
||||
</style>
|
||||
|
||||
<style name="ProgressBar.Large">
|
||||
<item name="android:indeterminateDrawable">@drawable/progress_large</item>
|
||||
<item name="android:minWidth">76dip</item>
|
||||
<item name="android:maxWidth">76dip</item>
|
||||
<item name="android:minHeight">76dip</item>
|
||||
<item name="android:maxHeight">76dip</item>
|
||||
</style>
|
||||
|
||||
<!-- Text -->
|
||||
<style name="CardTitle">
|
||||
<item name="android:textColor">@color/title_primary</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:lineSpacingExtra">0.0sp</item>
|
||||
</style>
|
||||
|
||||
<style name="CardSubtitle">
|
||||
<item name="android:textSize">@dimen/text_medium</item>
|
||||
<item name="android:textColor">@color/title_secondary</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:drawablePadding">4.0dip</item>
|
||||
<item name="android:lineSpacingExtra">0.0sp</item>
|
||||
</style>
|
||||
|
||||
<style name="DetailTitle">
|
||||
<item name="android:textSize">@dimen/detail_title_size</item>
|
||||
<item name="android:textColor">@color/title_primary</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
<item name="android:singleLine">false</item>
|
||||
<item name="android:lineSpacingExtra">0.0sp</item>
|
||||
</style>
|
||||
|
||||
<style name="DetailSubtitle">
|
||||
<item name="android:textSize">@dimen/detail_subtitle_size</item>
|
||||
<item name="android:textColor">@color/detail_title_secondary</item>
|
||||
<item name="android:ellipsize">marquee</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
</style>
|
||||
|
||||
<style name="TextTag">
|
||||
<item name="android:textSize">@dimen/detail_subtitle_size</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
<item name="android:background">@android:color/black</item>
|
||||
<item name="android:ellipsize">marquee</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:paddingTop">2.0dp</item>
|
||||
<item name="android:paddingBottom">2.0dp</item>
|
||||
<item name="android:paddingLeft">8.0dp</item>
|
||||
<item name="android:paddingRight">8.0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="RatingBarSmall">
|
||||
<item name="android:background">@null</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:button">@null</item>
|
||||
<item name="android:maxHeight">@dimen/rating_bar_small_height</item>
|
||||
<item name="android:progressDrawable">@drawable/ratingbar_small</item>
|
||||
<item name="android:minHeight">@dimen/rating_bar_small_height</item>
|
||||
<item name="android:thumb">@null</item>
|
||||
<item name="android:numStars">5</item>
|
||||
<item name="android:stepSize">0.5</item>
|
||||
<item name="android:isIndicator">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
+53
-53
@@ -1,54 +1,54 @@
|
||||
<resources>
|
||||
|
||||
<!-- Application theme. -->
|
||||
<style name="AppTheme" parent="@android:Theme.Holo.Light">
|
||||
|
||||
<!-- ActionBar -->
|
||||
<item name="@android:actionBarStyle">@style/ActionBar</item>
|
||||
<item name="@android:actionBarTabStyle">@style/ActionBarTabStyle</item>
|
||||
<item name="@android:homeAsUpIndicator">@drawable/ic_ab_back</item>
|
||||
|
||||
<!-- Button -->
|
||||
<item name="@android:buttonStyle">@style/Button.Blue</item>
|
||||
|
||||
<!-- Scrollbar attributes -->
|
||||
<item name="@android:scrollbarThumbVertical">@drawable/scrollbar_blue</item>
|
||||
|
||||
<!-- List attributes -->
|
||||
<!-- item name="@android:listDivider">@drawable/list_divider</item -->
|
||||
|
||||
<!-- No action bar shadow -->
|
||||
<!-- item name="android:windowContentOverlay">@drawable/shadow</item -->
|
||||
|
||||
<!-- Dialog -->
|
||||
<!-- item name="@android:dialogTitleDecorLayout">@layout/dialog_title</item -->
|
||||
|
||||
<!-- item name="@android:searchViewVoiceIcon">@android:drawable/ic_voice_search_api_holo_light</item -->
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Main">
|
||||
<item name="@android:homeAsUpIndicator">@drawable/ic_navigation_drawer</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Main.ListActivity">
|
||||
<item name="android:windowBackground">@color/main_background</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<!-- For activity whose main view is list, make background darker to match white list -->
|
||||
<style name="AppTheme.ListActivity">
|
||||
<item name="android:windowBackground">@color/main_background</item>
|
||||
</style>
|
||||
|
||||
<!-- Theme for start activity -->
|
||||
<style name="AppTheme.NoActionBar.Transparent">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||
</style>
|
||||
<resources>
|
||||
|
||||
<!-- Application theme. -->
|
||||
<style name="AppTheme" parent="@android:Theme.Holo.Light">
|
||||
|
||||
<!-- ActionBar -->
|
||||
<item name="@android:actionBarStyle">@style/ActionBar</item>
|
||||
<item name="@android:actionBarTabStyle">@style/ActionBarTabStyle</item>
|
||||
<item name="@android:homeAsUpIndicator">@drawable/ic_ab_back</item>
|
||||
|
||||
<!-- Button -->
|
||||
<item name="@android:buttonStyle">@style/Button.Blue</item>
|
||||
|
||||
<!-- Scrollbar attributes -->
|
||||
<item name="@android:scrollbarThumbVertical">@drawable/scrollbar_blue</item>
|
||||
|
||||
<!-- List attributes -->
|
||||
<!-- item name="@android:listDivider">@drawable/list_divider</item -->
|
||||
|
||||
<!-- No action bar shadow -->
|
||||
<!-- item name="android:windowContentOverlay">@drawable/shadow</item -->
|
||||
|
||||
<!-- Dialog -->
|
||||
<!-- item name="@android:dialogTitleDecorLayout">@layout/dialog_title</item -->
|
||||
|
||||
<!-- item name="@android:searchViewVoiceIcon">@android:drawable/ic_voice_search_api_holo_light</item -->
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Main">
|
||||
<item name="@android:homeAsUpIndicator">@drawable/ic_navigation_drawer</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Main.ListActivity">
|
||||
<item name="android:windowBackground">@color/main_background</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<!-- For activity whose main view is list, make background darker to match white list -->
|
||||
<style name="AppTheme.ListActivity">
|
||||
<item name="android:windowBackground">@color/main_background</item>
|
||||
</style>
|
||||
|
||||
<!-- Theme for start activity -->
|
||||
<style name="AppTheme.NoActionBar.Transparent">
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,306 +1,306 @@
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
import com.jakewharton.disklrucache.DiskLruCache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Movie;
|
||||
import android.os.Environment;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
// For Gallery cover key is gid,
|
||||
// for example , gid is 618395, token is 0439fa3666,
|
||||
// so key is 618395
|
||||
|
||||
// For Gallery preview key is gid-preview-num-row,
|
||||
// for example , gid is 618395, token is 0439fa3666, page2
|
||||
// so key is 618395-preview-2-0
|
||||
|
||||
// For Gallery image key is gid-preview-num,
|
||||
// for example , gid is 618395, token is 0439fa3666, image2
|
||||
// so key is 618395-image-2
|
||||
|
||||
public class DiskCache {
|
||||
private static String TAG = "ImageDiskCache";
|
||||
|
||||
private static final int VALUE_IDX = 0;
|
||||
|
||||
private Context mContext;
|
||||
private BitmapFactory.Options opt;
|
||||
private final DiskLruCache diskLruCache;
|
||||
|
||||
public DiskCache(Context context, String path, long maxSize) throws NameNotFoundException, IOException {
|
||||
this.mContext = context;
|
||||
|
||||
opt = Ui.getBitmapOpt();
|
||||
if (!Cache.hasSdCard()) {
|
||||
throw new IllegalStateException("Has no SdCard.");
|
||||
}
|
||||
|
||||
File dir = new File(Environment.getExternalStorageDirectory() + path);
|
||||
int version = mContext.getPackageManager().getPackageInfo(
|
||||
mContext.getPackageName(), 0).versionCode;
|
||||
version = 1;
|
||||
diskLruCache = DiskLruCache.open(dir, version, 1, maxSize);
|
||||
}
|
||||
|
||||
public long size() {
|
||||
return diskLruCache.size();
|
||||
}
|
||||
|
||||
public long maxSize() {
|
||||
return diskLruCache.getMaxSize();
|
||||
}
|
||||
|
||||
public void setMaxSize(long maxSize) {
|
||||
diskLruCache.setMaxSize(maxSize);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
diskLruCache.close();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "diskLruCache close error !");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
DiskLruCache.Snapshot snapshot = null;
|
||||
try {
|
||||
snapshot = diskLruCache.get(key);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if(snapshot==null)
|
||||
return false;
|
||||
snapshot.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object get(String key, int type) {
|
||||
DiskLruCache.Snapshot snapshot = null;
|
||||
try {
|
||||
snapshot = diskLruCache.get(key);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if (snapshot == null)
|
||||
return null;
|
||||
InputStream is = snapshot.getInputStream(VALUE_IDX);
|
||||
if (type == Util.BITMAP) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, opt);
|
||||
return bitmap;
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
Movie movie = Movie.decodeStream(is);
|
||||
return movie;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean put(String key, InputStream is) throws IOException {
|
||||
DiskLruCache.Editor editor = diskLruCache.edit(key);
|
||||
if (editor == null)
|
||||
return false;
|
||||
BufferedOutputStream bos = new BufferedOutputStream(editor.newOutputStream(VALUE_IDX));
|
||||
OutputStream os = new CacheOutputStream(bos, editor);
|
||||
Util.copy(is, os);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
diskLruCache.delete();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private class CacheOutputStream extends FilterOutputStream {
|
||||
|
||||
private final DiskLruCache.Editor editor;
|
||||
private boolean failed = false;
|
||||
|
||||
private CacheOutputStream(OutputStream os, DiskLruCache.Editor editor) {
|
||||
super(os);
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
IOException closeException = null;
|
||||
try {
|
||||
super.close();
|
||||
} catch (IOException e) {
|
||||
closeException = e;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
editor.abort();
|
||||
} else {
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
if (closeException != null) throw closeException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
try {
|
||||
super.flush();
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int oneByte) throws IOException {
|
||||
try {
|
||||
super.write(oneByte);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer) throws IOException {
|
||||
try {
|
||||
super.write(buffer);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) throws IOException {
|
||||
try {
|
||||
super.write(buffer, offset, length);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
private Context context = null;
|
||||
private File dir = null;
|
||||
int maxSize = 1024 * 10;
|
||||
|
||||
public DiskCache(Context context, String path, int maxSize) {
|
||||
this.context = context;
|
||||
this.maxSize = maxSize;
|
||||
if (maxSize <= 0)
|
||||
maxSize = 1024 * 10;
|
||||
|
||||
File rootdir = Environment.getExternalStorageDirectory();
|
||||
dir = new File(rootdir + path);
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
synchronized (key) {
|
||||
|
||||
File file = new File(dir, key);
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory())
|
||||
file.delete();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Object get(String key, int type) {
|
||||
synchronized (key) {
|
||||
File file = new File(dir, key);
|
||||
if (!file.isFile())
|
||||
return null;
|
||||
try {
|
||||
InputStream is = new FileInputStream(file);
|
||||
if (type == Util.BITMAP) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, Util.opt);
|
||||
return bitmap;
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
Movie movie = Movie.decodeStream(is);
|
||||
return movie;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.d(TAG, "WTF, I have checked that the file does exists.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean put(String key, InputStream is) throws IOException {
|
||||
synchronized (key) {
|
||||
File file = new File(dir, key);
|
||||
try {
|
||||
file.delete();
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
dir.mkdirs();
|
||||
file.delete();
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e1) {
|
||||
Log.d(TAG, "WTF, mkdirs didn't work? Path is " + file.getPath());
|
||||
e1.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
Util.inputStream2File(is, file);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Put " + key + " error.");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
clearDirectory(dir);
|
||||
}
|
||||
|
||||
private void clearDirectory(File dir) {
|
||||
|
||||
for (File file: dir.listFiles()) {
|
||||
if (file.isDirectory()) clearDirectory(file);
|
||||
file.delete();
|
||||
}
|
||||
|
||||
}*/
|
||||
/*
|
||||
* private static String getStingMD5(String s) { String re = null; try {
|
||||
* MessageDigest messageDigest =MessageDigest.getInstance(Algorithm); byte[]
|
||||
* inputByteArray = s.getBytes(); messageDigest.update(inputByteArray);
|
||||
* byte[] resultByteArray = messageDigest.digest(); re =
|
||||
* byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) {
|
||||
* e.printStackTrace(); Log.d(TAG, e.getMessage()); } return re; }
|
||||
*
|
||||
* private static String byteArrayToHex(byte[] byteArray) { char[] hexDigits
|
||||
* = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
|
||||
* char[] resultCharArray =new char[byteArray.length * 2]; int index = 0;
|
||||
* for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b>>> 4 &
|
||||
* 0xf]; resultCharArray[index++] = hexDigits[b& 0xf]; } return new
|
||||
* String(resultCharArray); }
|
||||
*/
|
||||
}
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import com.hippo.ehviewer.util.Cache;
|
||||
import com.hippo.ehviewer.util.Ui;
|
||||
import com.hippo.ehviewer.util.Util;
|
||||
import com.jakewharton.disklrucache.DiskLruCache;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Movie;
|
||||
import android.os.Environment;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
// For Gallery cover key is gid,
|
||||
// for example , gid is 618395, token is 0439fa3666,
|
||||
// so key is 618395
|
||||
|
||||
// For Gallery preview key is gid-preview-num-row,
|
||||
// for example , gid is 618395, token is 0439fa3666, page2
|
||||
// so key is 618395-preview-2-0
|
||||
|
||||
// For Gallery image key is gid-preview-num,
|
||||
// for example , gid is 618395, token is 0439fa3666, image2
|
||||
// so key is 618395-image-2
|
||||
|
||||
public class DiskCache {
|
||||
private static String TAG = "ImageDiskCache";
|
||||
|
||||
private static final int VALUE_IDX = 0;
|
||||
|
||||
private Context mContext;
|
||||
private BitmapFactory.Options opt;
|
||||
private final DiskLruCache diskLruCache;
|
||||
|
||||
public DiskCache(Context context, String path, long maxSize) throws NameNotFoundException, IOException {
|
||||
this.mContext = context;
|
||||
|
||||
opt = Ui.getBitmapOpt();
|
||||
if (!Cache.hasSdCard()) {
|
||||
throw new IllegalStateException("Has no SdCard.");
|
||||
}
|
||||
|
||||
File dir = new File(Environment.getExternalStorageDirectory() + path);
|
||||
int version = mContext.getPackageManager().getPackageInfo(
|
||||
mContext.getPackageName(), 0).versionCode;
|
||||
version = 1;
|
||||
diskLruCache = DiskLruCache.open(dir, version, 1, maxSize);
|
||||
}
|
||||
|
||||
public long size() {
|
||||
return diskLruCache.size();
|
||||
}
|
||||
|
||||
public long maxSize() {
|
||||
return diskLruCache.getMaxSize();
|
||||
}
|
||||
|
||||
public void setMaxSize(long maxSize) {
|
||||
diskLruCache.setMaxSize(maxSize);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
diskLruCache.close();
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "diskLruCache close error !");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
DiskLruCache.Snapshot snapshot = null;
|
||||
try {
|
||||
snapshot = diskLruCache.get(key);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if(snapshot==null)
|
||||
return false;
|
||||
snapshot.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object get(String key, int type) {
|
||||
DiskLruCache.Snapshot snapshot = null;
|
||||
try {
|
||||
snapshot = diskLruCache.get(key);
|
||||
} catch (IOException e) {
|
||||
}
|
||||
if (snapshot == null)
|
||||
return null;
|
||||
InputStream is = snapshot.getInputStream(VALUE_IDX);
|
||||
if (type == Util.BITMAP) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, opt);
|
||||
return bitmap;
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
Movie movie = Movie.decodeStream(is);
|
||||
return movie;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean put(String key, InputStream is) throws IOException {
|
||||
DiskLruCache.Editor editor = diskLruCache.edit(key);
|
||||
if (editor == null)
|
||||
return false;
|
||||
BufferedOutputStream bos = new BufferedOutputStream(editor.newOutputStream(VALUE_IDX));
|
||||
OutputStream os = new CacheOutputStream(bos, editor);
|
||||
Util.copy(is, os);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
try {
|
||||
diskLruCache.delete();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private class CacheOutputStream extends FilterOutputStream {
|
||||
|
||||
private final DiskLruCache.Editor editor;
|
||||
private boolean failed = false;
|
||||
|
||||
private CacheOutputStream(OutputStream os, DiskLruCache.Editor editor) {
|
||||
super(os);
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
IOException closeException = null;
|
||||
try {
|
||||
super.close();
|
||||
} catch (IOException e) {
|
||||
closeException = e;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
editor.abort();
|
||||
} else {
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
if (closeException != null) throw closeException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
try {
|
||||
super.flush();
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int oneByte) throws IOException {
|
||||
try {
|
||||
super.write(oneByte);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer) throws IOException {
|
||||
try {
|
||||
super.write(buffer);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buffer, int offset, int length) throws IOException {
|
||||
try {
|
||||
super.write(buffer, offset, length);
|
||||
} catch (IOException e) {
|
||||
failed = true;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
private Context context = null;
|
||||
private File dir = null;
|
||||
int maxSize = 1024 * 10;
|
||||
|
||||
public DiskCache(Context context, String path, int maxSize) {
|
||||
this.context = context;
|
||||
this.maxSize = maxSize;
|
||||
if (maxSize <= 0)
|
||||
maxSize = 1024 * 10;
|
||||
|
||||
File rootdir = Environment.getExternalStorageDirectory();
|
||||
dir = new File(rootdir + path);
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
public boolean hasKey(String key) {
|
||||
synchronized (key) {
|
||||
|
||||
File file = new File(dir, key);
|
||||
if (file.exists()) {
|
||||
if (file.isDirectory())
|
||||
file.delete();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Object get(String key, int type) {
|
||||
synchronized (key) {
|
||||
File file = new File(dir, key);
|
||||
if (!file.isFile())
|
||||
return null;
|
||||
try {
|
||||
InputStream is = new FileInputStream(file);
|
||||
if (type == Util.BITMAP) {
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(is, null, Util.opt);
|
||||
return bitmap;
|
||||
} else if (type == Util.MOVIE) {
|
||||
is = new BufferedInputStream(is, 16 * 1024);
|
||||
is.mark(16 * 1024);
|
||||
Movie movie = Movie.decodeStream(is);
|
||||
return movie;
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.d(TAG, "WTF, I have checked that the file does exists.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean put(String key, InputStream is) throws IOException {
|
||||
synchronized (key) {
|
||||
File file = new File(dir, key);
|
||||
try {
|
||||
file.delete();
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
dir.mkdirs();
|
||||
file.delete();
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e1) {
|
||||
Log.d(TAG, "WTF, mkdirs didn't work? Path is " + file.getPath());
|
||||
e1.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
Util.inputStream2File(is, file);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Put " + key + " error.");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
clearDirectory(dir);
|
||||
}
|
||||
|
||||
private void clearDirectory(File dir) {
|
||||
|
||||
for (File file: dir.listFiles()) {
|
||||
if (file.isDirectory()) clearDirectory(file);
|
||||
file.delete();
|
||||
}
|
||||
|
||||
}*/
|
||||
/*
|
||||
* private static String getStingMD5(String s) { String re = null; try {
|
||||
* MessageDigest messageDigest =MessageDigest.getInstance(Algorithm); byte[]
|
||||
* inputByteArray = s.getBytes(); messageDigest.update(inputByteArray);
|
||||
* byte[] resultByteArray = messageDigest.digest(); re =
|
||||
* byteArrayToHex(resultByteArray); } catch (NoSuchAlgorithmException e) {
|
||||
* e.printStackTrace(); Log.d(TAG, e.getMessage()); } return re; }
|
||||
*
|
||||
* private static String byteArrayToHex(byte[] byteArray) { char[] hexDigits
|
||||
* = {'0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F' };
|
||||
* char[] resultCharArray =new char[byteArray.length * 2]; int index = 0;
|
||||
* for (byte b : byteArray) { resultCharArray[index++] = hexDigits[b>>> 4 &
|
||||
* 0xf]; resultCharArray[index++] = hexDigits[b& 0xf]; } return new
|
||||
* String(resultCharArray); }
|
||||
*/
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,5 @@
|
||||
package com.hippo.ehviewer.exception;
|
||||
|
||||
public class NoSdCardException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
package com.hippo.ehviewer.exception;
|
||||
|
||||
public class NoSdCardException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
public class AlphaAnimation extends CanvasAnimation {
|
||||
private final float mStartAlpha;
|
||||
private final float mEndAlpha;
|
||||
private float mCurrentAlpha;
|
||||
|
||||
public AlphaAnimation(float from, float to) {
|
||||
mStartAlpha = from;
|
||||
mEndAlpha = to;
|
||||
mCurrentAlpha = from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(GLCanvas canvas) {
|
||||
canvas.multiplyAlpha(mCurrentAlpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCanvasSaveFlags() {
|
||||
return GLCanvas.SAVE_FLAG_ALPHA;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCalculate(float progress) {
|
||||
mCurrentAlpha = Utils.clamp(mStartAlpha
|
||||
+ (mEndAlpha - mStartAlpha) * progress, 0f, 1f);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
public class AlphaAnimation extends CanvasAnimation {
|
||||
private final float mStartAlpha;
|
||||
private final float mEndAlpha;
|
||||
private float mCurrentAlpha;
|
||||
|
||||
public AlphaAnimation(float from, float to) {
|
||||
mStartAlpha = from;
|
||||
mEndAlpha = to;
|
||||
mCurrentAlpha = from;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(GLCanvas canvas) {
|
||||
canvas.multiplyAlpha(mCurrentAlpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCanvasSaveFlags() {
|
||||
return GLCanvas.SAVE_FLAG_ALPHA;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCalculate(float progress) {
|
||||
mCurrentAlpha = Utils.clamp(mStartAlpha
|
||||
+ (mEndAlpha - mStartAlpha) * progress, 0f, 1f);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
// Animation calculates a value according to the current input time.
|
||||
//
|
||||
// 1. First we need to use setDuration(int) to set the duration of the
|
||||
// animation. The duration is in milliseconds.
|
||||
// 2. Then we should call start(). The actual start time is the first value
|
||||
// passed to calculate(long).
|
||||
// 3. Each time we want to get an animation value, we call
|
||||
// calculate(long currentTimeMillis) to ask the Animation to calculate it.
|
||||
// The parameter passed to calculate(long) should be nonnegative.
|
||||
// 4. Use get() to get that value.
|
||||
//
|
||||
// In step 3, onCalculate(float progress) is called so subclasses can calculate
|
||||
// the value according to progress (progress is a value in [0,1]).
|
||||
//
|
||||
// Before onCalculate(float) is called, There is an optional interpolator which
|
||||
// can change the progress value. The interpolator can be set by
|
||||
// setInterpolator(Interpolator). If the interpolator is used, the value passed
|
||||
// to onCalculate may be (for example, the overshoot effect).
|
||||
//
|
||||
// The isActive() method returns true after the animation start() is called and
|
||||
// before calculate is passed a value which reaches the duration of the
|
||||
// animation.
|
||||
//
|
||||
// The start() method can be called again to restart the Animation.
|
||||
//
|
||||
abstract public class Animation {
|
||||
private static final long ANIMATION_START = -1;
|
||||
private static final long NO_ANIMATION = -2;
|
||||
|
||||
private long mStartTime = NO_ANIMATION;
|
||||
private int mDuration;
|
||||
private Interpolator mInterpolator;
|
||||
|
||||
public void setInterpolator(Interpolator interpolator) {
|
||||
mInterpolator = interpolator;
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
mStartTime = ANIMATION_START;
|
||||
}
|
||||
|
||||
public void setStartTime(long time) {
|
||||
mStartTime = time;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return mStartTime != NO_ANIMATION;
|
||||
}
|
||||
|
||||
public void forceStop() {
|
||||
mStartTime = NO_ANIMATION;
|
||||
}
|
||||
|
||||
public boolean calculate(long currentTimeMillis) {
|
||||
if (mStartTime == NO_ANIMATION) return false;
|
||||
if (mStartTime == ANIMATION_START) mStartTime = currentTimeMillis;
|
||||
int elapse = (int) (currentTimeMillis - mStartTime);
|
||||
float x = Utils.clamp((float) elapse / mDuration, 0f, 1f);
|
||||
Interpolator i = mInterpolator;
|
||||
onCalculate(i != null ? i.getInterpolation(x) : x);
|
||||
if (elapse >= mDuration) mStartTime = NO_ANIMATION;
|
||||
return mStartTime != NO_ANIMATION;
|
||||
}
|
||||
|
||||
abstract protected void onCalculate(float progress);
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
// Animation calculates a value according to the current input time.
|
||||
//
|
||||
// 1. First we need to use setDuration(int) to set the duration of the
|
||||
// animation. The duration is in milliseconds.
|
||||
// 2. Then we should call start(). The actual start time is the first value
|
||||
// passed to calculate(long).
|
||||
// 3. Each time we want to get an animation value, we call
|
||||
// calculate(long currentTimeMillis) to ask the Animation to calculate it.
|
||||
// The parameter passed to calculate(long) should be nonnegative.
|
||||
// 4. Use get() to get that value.
|
||||
//
|
||||
// In step 3, onCalculate(float progress) is called so subclasses can calculate
|
||||
// the value according to progress (progress is a value in [0,1]).
|
||||
//
|
||||
// Before onCalculate(float) is called, There is an optional interpolator which
|
||||
// can change the progress value. The interpolator can be set by
|
||||
// setInterpolator(Interpolator). If the interpolator is used, the value passed
|
||||
// to onCalculate may be (for example, the overshoot effect).
|
||||
//
|
||||
// The isActive() method returns true after the animation start() is called and
|
||||
// before calculate is passed a value which reaches the duration of the
|
||||
// animation.
|
||||
//
|
||||
// The start() method can be called again to restart the Animation.
|
||||
//
|
||||
abstract public class Animation {
|
||||
private static final long ANIMATION_START = -1;
|
||||
private static final long NO_ANIMATION = -2;
|
||||
|
||||
private long mStartTime = NO_ANIMATION;
|
||||
private int mDuration;
|
||||
private Interpolator mInterpolator;
|
||||
|
||||
public void setInterpolator(Interpolator interpolator) {
|
||||
mInterpolator = interpolator;
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
mStartTime = ANIMATION_START;
|
||||
}
|
||||
|
||||
public void setStartTime(long time) {
|
||||
mStartTime = time;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return mStartTime != NO_ANIMATION;
|
||||
}
|
||||
|
||||
public void forceStop() {
|
||||
mStartTime = NO_ANIMATION;
|
||||
}
|
||||
|
||||
public boolean calculate(long currentTimeMillis) {
|
||||
if (mStartTime == NO_ANIMATION) return false;
|
||||
if (mStartTime == ANIMATION_START) mStartTime = currentTimeMillis;
|
||||
int elapse = (int) (currentTimeMillis - mStartTime);
|
||||
float x = Utils.clamp((float) elapse / mDuration, 0f, 1f);
|
||||
Interpolator i = mInterpolator;
|
||||
onCalculate(i != null ? i.getInterpolation(x) : x);
|
||||
if (elapse >= mDuration) mStartTime = NO_ANIMATION;
|
||||
return mStartTime != NO_ANIMATION;
|
||||
}
|
||||
|
||||
abstract protected void onCalculate(float progress);
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
public abstract class CanvasAnimation extends Animation {
|
||||
|
||||
public abstract int getCanvasSaveFlags();
|
||||
public abstract void apply(GLCanvas canvas);
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
public abstract class CanvasAnimation extends Animation {
|
||||
|
||||
public abstract int getCanvasSaveFlags();
|
||||
public abstract void apply(GLCanvas canvas);
|
||||
}
|
||||
@@ -1,40 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
public class FloatAnimation extends Animation {
|
||||
|
||||
private final float mFrom;
|
||||
private final float mTo;
|
||||
private float mCurrent;
|
||||
|
||||
public FloatAnimation(float from, float to, int duration) {
|
||||
mFrom = from;
|
||||
mTo = to;
|
||||
mCurrent = from;
|
||||
setDuration(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCalculate(float progress) {
|
||||
mCurrent = mFrom + (mTo - mFrom) * progress;
|
||||
}
|
||||
|
||||
public float get() {
|
||||
return mCurrent;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.anim;
|
||||
|
||||
public class FloatAnimation extends Animation {
|
||||
|
||||
private final float mFrom;
|
||||
private final float mTo;
|
||||
private float mCurrent;
|
||||
|
||||
public FloatAnimation(float from, float to, int duration) {
|
||||
mFrom = from;
|
||||
mTo = to;
|
||||
mCurrent = from;
|
||||
setDuration(duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCalculate(float progress) {
|
||||
mCurrent = mFrom + (mTo - mFrom) * progress;
|
||||
}
|
||||
|
||||
public float get() {
|
||||
return mCurrent;
|
||||
}
|
||||
}
|
||||
@@ -1,240 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.common;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore.MediaColumns;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class ApiHelper {
|
||||
public static interface VERSION_CODES {
|
||||
// These value are copied from Build.VERSION_CODES
|
||||
public static final int GINGERBREAD_MR1 = 10;
|
||||
public static final int HONEYCOMB = 11;
|
||||
public static final int HONEYCOMB_MR1 = 12;
|
||||
public static final int HONEYCOMB_MR2 = 13;
|
||||
public static final int ICE_CREAM_SANDWICH = 14;
|
||||
public static final int ICE_CREAM_SANDWICH_MR1 = 15;
|
||||
public static final int JELLY_BEAN = 16;
|
||||
public static final int JELLY_BEAN_MR1 = 17;
|
||||
public static final int JELLY_BEAN_MR2 = 18;
|
||||
}
|
||||
|
||||
public static final boolean AT_LEAST_16 = Build.VERSION.SDK_INT >= 16;
|
||||
|
||||
public static final boolean USE_888_PIXEL_FORMAT =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean ENABLE_PHOTO_EDITOR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE =
|
||||
hasField(View.class, "SYSTEM_UI_FLAG_LAYOUT_STABLE");
|
||||
|
||||
public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION =
|
||||
hasField(View.class, "SYSTEM_UI_FLAG_HIDE_NAVIGATION");
|
||||
|
||||
public static final boolean HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT =
|
||||
hasField(MediaColumns.class, "WIDTH");
|
||||
|
||||
public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_FACTORY =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SET_BEAM_PUSH_URIS =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_SET_DEFALT_BUFFER_SIZE = hasMethod(
|
||||
"android.graphics.SurfaceTexture", "setDefaultBufferSize",
|
||||
int.class, int.class);
|
||||
|
||||
public static final boolean HAS_RELEASE_SURFACE_TEXTURE = hasMethod(
|
||||
"android.graphics.SurfaceTexture", "release");
|
||||
|
||||
public static final boolean HAS_SURFACE_TEXTURE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_MTP =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1;
|
||||
|
||||
public static final boolean HAS_AUTO_FOCUS_MOVE_CALLBACK =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_REMOTE_VIEWS_SERVICE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_INTENT_EXTRA_LOCAL_ONLY =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SET_SYSTEM_UI_VISIBILITY =
|
||||
hasMethod(View.class, "setSystemUiVisibility", int.class);
|
||||
|
||||
public static final boolean HAS_FACE_DETECTION;
|
||||
static {
|
||||
boolean hasFaceDetection = false;
|
||||
try {
|
||||
Class<?> listenerClass = Class.forName(
|
||||
"android.hardware.Camera$FaceDetectionListener");
|
||||
hasFaceDetection =
|
||||
hasMethod(Camera.class, "setFaceDetectionListener", listenerClass) &&
|
||||
hasMethod(Camera.class, "startFaceDetection") &&
|
||||
hasMethod(Camera.class, "stopFaceDetection") &&
|
||||
hasMethod(Camera.Parameters.class, "getMaxNumDetectedFaces");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
HAS_FACE_DETECTION = hasFaceDetection;
|
||||
}
|
||||
|
||||
public static final boolean HAS_GET_CAMERA_DISABLED =
|
||||
hasMethod(DevicePolicyManager.class, "getCameraDisabled", ComponentName.class);
|
||||
|
||||
public static final boolean HAS_MEDIA_ACTION_SOUND =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_OLD_PANORAMA =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_TIME_LAPSE_RECORDING =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_ZOOM_WHEN_RECORDING =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_CAMERA_FOCUS_AREA =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_CAMERA_METERING_AREA =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_FINE_RESOLUTION_QUALITY_LEVELS =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_MOTION_EVENT_TRANSFORM =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_EFFECTS_RECORDING = false;
|
||||
|
||||
// "Background" filter does not have "context" input port in jelly bean.
|
||||
public static final boolean HAS_EFFECTS_RECORDING_CONTEXT_INPUT =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1;
|
||||
|
||||
public static final boolean HAS_GET_SUPPORTED_VIDEO_SIZE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SET_ICON_ATTRIBUTE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_MEDIA_PROVIDER_FILES_TABLE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SURFACE_TEXTURE_RECORDING =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_ACTION_BAR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
// Ex: View.setTranslationX.
|
||||
public static final boolean HAS_VIEW_TRANSFORM_PROPERTIES =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_CAMERA_HDR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1;
|
||||
|
||||
public static final boolean HAS_OPTIONS_IN_MUTABLE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean CAN_START_PREVIEW_IN_JPEG_CALLBACK =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_VIEW_PROPERTY_ANIMATOR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1;
|
||||
|
||||
public static final boolean HAS_POST_ON_ANIMATION =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_ANNOUNCE_FOR_ACCESSIBILITY =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_OBJECT_ANIMATION =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_GLES20_REQUIRED =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_ROTATION_ANIMATION =
|
||||
hasField(WindowManager.LayoutParams.class, "rotationAnimation");
|
||||
|
||||
public static final boolean HAS_ORIENTATION_LOCK =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2;
|
||||
|
||||
public static final boolean HAS_CANCELLATION_SIGNAL =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_MEDIA_MUXER =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2;
|
||||
|
||||
public static final boolean HAS_DISPLAY_LISTENER =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1;
|
||||
|
||||
public static int getIntFieldIfExists(Class<?> klass, String fieldName,
|
||||
Class<?> obj, int defaultVal) {
|
||||
try {
|
||||
Field f = klass.getDeclaredField(fieldName);
|
||||
return f.getInt(obj);
|
||||
} catch (Exception e) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasField(Class<?> klass, String fieldName) {
|
||||
try {
|
||||
klass.getDeclaredField(fieldName);
|
||||
return true;
|
||||
} catch (NoSuchFieldException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasMethod(String className, String methodName,
|
||||
Class<?>... parameterTypes) {
|
||||
try {
|
||||
Class<?> klass = Class.forName(className);
|
||||
klass.getDeclaredMethod(methodName, parameterTypes);
|
||||
return true;
|
||||
} catch (Throwable th) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasMethod(
|
||||
Class<?> klass, String methodName, Class<?> ... paramTypes) {
|
||||
try {
|
||||
klass.getDeclaredMethod(methodName, paramTypes);
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.common;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore.MediaColumns;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class ApiHelper {
|
||||
public static interface VERSION_CODES {
|
||||
// These value are copied from Build.VERSION_CODES
|
||||
public static final int GINGERBREAD_MR1 = 10;
|
||||
public static final int HONEYCOMB = 11;
|
||||
public static final int HONEYCOMB_MR1 = 12;
|
||||
public static final int HONEYCOMB_MR2 = 13;
|
||||
public static final int ICE_CREAM_SANDWICH = 14;
|
||||
public static final int ICE_CREAM_SANDWICH_MR1 = 15;
|
||||
public static final int JELLY_BEAN = 16;
|
||||
public static final int JELLY_BEAN_MR1 = 17;
|
||||
public static final int JELLY_BEAN_MR2 = 18;
|
||||
}
|
||||
|
||||
public static final boolean AT_LEAST_16 = Build.VERSION.SDK_INT >= 16;
|
||||
|
||||
public static final boolean USE_888_PIXEL_FORMAT =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean ENABLE_PHOTO_EDITOR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE =
|
||||
hasField(View.class, "SYSTEM_UI_FLAG_LAYOUT_STABLE");
|
||||
|
||||
public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION =
|
||||
hasField(View.class, "SYSTEM_UI_FLAG_HIDE_NAVIGATION");
|
||||
|
||||
public static final boolean HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT =
|
||||
hasField(MediaColumns.class, "WIDTH");
|
||||
|
||||
public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_FACTORY =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SET_BEAM_PUSH_URIS =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_SET_DEFALT_BUFFER_SIZE = hasMethod(
|
||||
"android.graphics.SurfaceTexture", "setDefaultBufferSize",
|
||||
int.class, int.class);
|
||||
|
||||
public static final boolean HAS_RELEASE_SURFACE_TEXTURE = hasMethod(
|
||||
"android.graphics.SurfaceTexture", "release");
|
||||
|
||||
public static final boolean HAS_SURFACE_TEXTURE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_MTP =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1;
|
||||
|
||||
public static final boolean HAS_AUTO_FOCUS_MOVE_CALLBACK =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_REMOTE_VIEWS_SERVICE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_INTENT_EXTRA_LOCAL_ONLY =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SET_SYSTEM_UI_VISIBILITY =
|
||||
hasMethod(View.class, "setSystemUiVisibility", int.class);
|
||||
|
||||
public static final boolean HAS_FACE_DETECTION;
|
||||
static {
|
||||
boolean hasFaceDetection = false;
|
||||
try {
|
||||
Class<?> listenerClass = Class.forName(
|
||||
"android.hardware.Camera$FaceDetectionListener");
|
||||
hasFaceDetection =
|
||||
hasMethod(Camera.class, "setFaceDetectionListener", listenerClass) &&
|
||||
hasMethod(Camera.class, "startFaceDetection") &&
|
||||
hasMethod(Camera.class, "stopFaceDetection") &&
|
||||
hasMethod(Camera.Parameters.class, "getMaxNumDetectedFaces");
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
HAS_FACE_DETECTION = hasFaceDetection;
|
||||
}
|
||||
|
||||
public static final boolean HAS_GET_CAMERA_DISABLED =
|
||||
hasMethod(DevicePolicyManager.class, "getCameraDisabled", ComponentName.class);
|
||||
|
||||
public static final boolean HAS_MEDIA_ACTION_SOUND =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_OLD_PANORAMA =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_TIME_LAPSE_RECORDING =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_ZOOM_WHEN_RECORDING =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_CAMERA_FOCUS_AREA =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_CAMERA_METERING_AREA =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_FINE_RESOLUTION_QUALITY_LEVELS =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_MOTION_EVENT_TRANSFORM =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_EFFECTS_RECORDING = false;
|
||||
|
||||
// "Background" filter does not have "context" input port in jelly bean.
|
||||
public static final boolean HAS_EFFECTS_RECORDING_CONTEXT_INPUT =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1;
|
||||
|
||||
public static final boolean HAS_GET_SUPPORTED_VIDEO_SIZE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SET_ICON_ATTRIBUTE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_MEDIA_PROVIDER_FILES_TABLE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_SURFACE_TEXTURE_RECORDING =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_ACTION_BAR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
// Ex: View.setTranslationX.
|
||||
public static final boolean HAS_VIEW_TRANSFORM_PROPERTIES =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_CAMERA_HDR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1;
|
||||
|
||||
public static final boolean HAS_OPTIONS_IN_MUTABLE =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean CAN_START_PREVIEW_IN_JPEG_CALLBACK =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
|
||||
public static final boolean HAS_VIEW_PROPERTY_ANIMATOR =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1;
|
||||
|
||||
public static final boolean HAS_POST_ON_ANIMATION =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_ANNOUNCE_FOR_ACCESSIBILITY =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_OBJECT_ANIMATION =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_GLES20_REQUIRED =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
|
||||
|
||||
public static final boolean HAS_ROTATION_ANIMATION =
|
||||
hasField(WindowManager.LayoutParams.class, "rotationAnimation");
|
||||
|
||||
public static final boolean HAS_ORIENTATION_LOCK =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2;
|
||||
|
||||
public static final boolean HAS_CANCELLATION_SIGNAL =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
|
||||
|
||||
public static final boolean HAS_MEDIA_MUXER =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2;
|
||||
|
||||
public static final boolean HAS_DISPLAY_LISTENER =
|
||||
Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1;
|
||||
|
||||
public static int getIntFieldIfExists(Class<?> klass, String fieldName,
|
||||
Class<?> obj, int defaultVal) {
|
||||
try {
|
||||
Field f = klass.getDeclaredField(fieldName);
|
||||
return f.getInt(obj);
|
||||
} catch (Exception e) {
|
||||
return defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasField(Class<?> klass, String fieldName) {
|
||||
try {
|
||||
klass.getDeclaredField(fieldName);
|
||||
return true;
|
||||
} catch (NoSuchFieldException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasMethod(String className, String methodName,
|
||||
Class<?>... parameterTypes) {
|
||||
try {
|
||||
Class<?> klass = Class.forName(className);
|
||||
klass.getDeclaredMethod(methodName, parameterTypes);
|
||||
return true;
|
||||
} catch (Throwable th) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasMethod(
|
||||
Class<?> klass, String methodName, Class<?> ... paramTypes) {
|
||||
try {
|
||||
klass.getDeclaredMethod(methodName, paramTypes);
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,217 +1,217 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.RectF;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
// BasicTexture is a Texture corresponds to a real GL texture.
|
||||
// The state of a BasicTexture indicates whether its data is loaded to GL memory.
|
||||
// If a BasicTexture is loaded into GL memory, it has a GL texture id.
|
||||
public abstract class BasicTexture implements Texture {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "BasicTexture";
|
||||
protected static final int UNSPECIFIED = -1;
|
||||
|
||||
protected static final int STATE_UNLOADED = 0;
|
||||
protected static final int STATE_LOADED = 1;
|
||||
protected static final int STATE_ERROR = -1;
|
||||
|
||||
// Log a warning if a texture is larger along a dimension
|
||||
private static final int MAX_TEXTURE_SIZE = 4096;
|
||||
|
||||
protected int mId = -1;
|
||||
protected int mState;
|
||||
|
||||
protected int mWidth = UNSPECIFIED;
|
||||
protected int mHeight = UNSPECIFIED;
|
||||
|
||||
protected int mTextureWidth;
|
||||
protected int mTextureHeight;
|
||||
|
||||
private boolean mHasBorder;
|
||||
|
||||
protected GLCanvas mCanvasRef = null;
|
||||
private static WeakHashMap<BasicTexture, Object> sAllTextures
|
||||
= new WeakHashMap<BasicTexture, Object>();
|
||||
private static ThreadLocal sInFinalizer = new ThreadLocal();
|
||||
|
||||
protected BasicTexture(GLCanvas canvas, int id, int state) {
|
||||
setAssociatedCanvas(canvas);
|
||||
mId = id;
|
||||
mState = state;
|
||||
synchronized (sAllTextures) {
|
||||
sAllTextures.put(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected BasicTexture() {
|
||||
this(null, 0, STATE_UNLOADED);
|
||||
}
|
||||
|
||||
protected void setAssociatedCanvas(GLCanvas canvas) {
|
||||
mCanvasRef = canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content size of this texture. In OpenGL, the actual texture
|
||||
* size must be of power of 2, the size of the content may be smaller.
|
||||
*/
|
||||
public void setSize(int width, int height) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0;
|
||||
mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0;
|
||||
if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
|
||||
Log.w(TAG, String.format("texture is too large: %d x %d",
|
||||
mTextureWidth, mTextureHeight), new Exception());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFlippedVertically() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
// Returns the width rounded to the next power of 2.
|
||||
public int getTextureWidth() {
|
||||
return mTextureWidth;
|
||||
}
|
||||
|
||||
// Returns the height rounded to the next power of 2.
|
||||
public int getTextureHeight() {
|
||||
return mTextureHeight;
|
||||
}
|
||||
|
||||
// Returns true if the texture has one pixel transparent border around the
|
||||
// actual content. This is used to avoid jigged edges.
|
||||
//
|
||||
// The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap
|
||||
// mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially
|
||||
// covered by the texture will use the color of the edge texel. If we add
|
||||
// the transparent border, the color of the edge texel will be mixed with
|
||||
// appropriate amount of transparent.
|
||||
//
|
||||
// Currently our background is black, so we can draw the thumbnails without
|
||||
// enabling blending.
|
||||
public boolean hasBorder() {
|
||||
return mHasBorder;
|
||||
}
|
||||
|
||||
protected void setBorder(boolean hasBorder) {
|
||||
mHasBorder = hasBorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
canvas.drawTexture(this, x, y, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
canvas.drawTexture(this, x, y, w, h);
|
||||
}
|
||||
|
||||
public void draw(GLCanvas canvas, RectF source, RectF target) {
|
||||
canvas.drawTexture(this, source, target);
|
||||
}
|
||||
|
||||
// onBind is called before GLCanvas binds this texture.
|
||||
// It should make sure the data is uploaded to GL memory.
|
||||
abstract protected boolean onBind(GLCanvas canvas);
|
||||
|
||||
// Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
|
||||
abstract protected int getTarget();
|
||||
|
||||
public boolean isLoaded() {
|
||||
return mState == STATE_LOADED;
|
||||
}
|
||||
|
||||
// recycle() is called when the texture will never be used again,
|
||||
// so it can free all resources.
|
||||
public void recycle() {
|
||||
freeResource();
|
||||
}
|
||||
|
||||
// yield() is called when the texture will not be used temporarily,
|
||||
// so it can free some resources.
|
||||
// The default implementation unloads the texture from GL memory, so
|
||||
// the subclass should make sure it can reload the texture to GL memory
|
||||
// later, or it will have to override this method.
|
||||
public void yield() {
|
||||
freeResource();
|
||||
}
|
||||
|
||||
private void freeResource() {
|
||||
GLCanvas canvas = mCanvasRef;
|
||||
if (canvas != null && mId != -1) {
|
||||
canvas.unloadTexture(this);
|
||||
mId = -1; // Don't free it again.
|
||||
}
|
||||
mState = STATE_UNLOADED;
|
||||
setAssociatedCanvas(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
sInFinalizer.set(BasicTexture.class);
|
||||
recycle();
|
||||
sInFinalizer.set(null);
|
||||
}
|
||||
|
||||
// This is for deciding if we can call Bitmap's recycle().
|
||||
// We cannot call Bitmap's recycle() in finalizer because at that point
|
||||
// the finalizer of Bitmap may already be called so recycle() will crash.
|
||||
public static boolean inFinalizer() {
|
||||
return sInFinalizer.get() != null;
|
||||
}
|
||||
|
||||
public static void yieldAllTextures() {
|
||||
synchronized (sAllTextures) {
|
||||
for (BasicTexture t : sAllTextures.keySet()) {
|
||||
t.yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidateAllTextures() {
|
||||
synchronized (sAllTextures) {
|
||||
for (BasicTexture t : sAllTextures.keySet()) {
|
||||
t.mState = STATE_UNLOADED;
|
||||
t.setAssociatedCanvas(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.RectF;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
// BasicTexture is a Texture corresponds to a real GL texture.
|
||||
// The state of a BasicTexture indicates whether its data is loaded to GL memory.
|
||||
// If a BasicTexture is loaded into GL memory, it has a GL texture id.
|
||||
public abstract class BasicTexture implements Texture {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "BasicTexture";
|
||||
protected static final int UNSPECIFIED = -1;
|
||||
|
||||
protected static final int STATE_UNLOADED = 0;
|
||||
protected static final int STATE_LOADED = 1;
|
||||
protected static final int STATE_ERROR = -1;
|
||||
|
||||
// Log a warning if a texture is larger along a dimension
|
||||
private static final int MAX_TEXTURE_SIZE = 4096;
|
||||
|
||||
protected int mId = -1;
|
||||
protected int mState;
|
||||
|
||||
protected int mWidth = UNSPECIFIED;
|
||||
protected int mHeight = UNSPECIFIED;
|
||||
|
||||
protected int mTextureWidth;
|
||||
protected int mTextureHeight;
|
||||
|
||||
private boolean mHasBorder;
|
||||
|
||||
protected GLCanvas mCanvasRef = null;
|
||||
private static WeakHashMap<BasicTexture, Object> sAllTextures
|
||||
= new WeakHashMap<BasicTexture, Object>();
|
||||
private static ThreadLocal sInFinalizer = new ThreadLocal();
|
||||
|
||||
protected BasicTexture(GLCanvas canvas, int id, int state) {
|
||||
setAssociatedCanvas(canvas);
|
||||
mId = id;
|
||||
mState = state;
|
||||
synchronized (sAllTextures) {
|
||||
sAllTextures.put(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected BasicTexture() {
|
||||
this(null, 0, STATE_UNLOADED);
|
||||
}
|
||||
|
||||
protected void setAssociatedCanvas(GLCanvas canvas) {
|
||||
mCanvasRef = canvas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content size of this texture. In OpenGL, the actual texture
|
||||
* size must be of power of 2, the size of the content may be smaller.
|
||||
*/
|
||||
public void setSize(int width, int height) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0;
|
||||
mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0;
|
||||
if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
|
||||
Log.w(TAG, String.format("texture is too large: %d x %d",
|
||||
mTextureWidth, mTextureHeight), new Exception());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFlippedVertically() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
// Returns the width rounded to the next power of 2.
|
||||
public int getTextureWidth() {
|
||||
return mTextureWidth;
|
||||
}
|
||||
|
||||
// Returns the height rounded to the next power of 2.
|
||||
public int getTextureHeight() {
|
||||
return mTextureHeight;
|
||||
}
|
||||
|
||||
// Returns true if the texture has one pixel transparent border around the
|
||||
// actual content. This is used to avoid jigged edges.
|
||||
//
|
||||
// The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap
|
||||
// mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially
|
||||
// covered by the texture will use the color of the edge texel. If we add
|
||||
// the transparent border, the color of the edge texel will be mixed with
|
||||
// appropriate amount of transparent.
|
||||
//
|
||||
// Currently our background is black, so we can draw the thumbnails without
|
||||
// enabling blending.
|
||||
public boolean hasBorder() {
|
||||
return mHasBorder;
|
||||
}
|
||||
|
||||
protected void setBorder(boolean hasBorder) {
|
||||
mHasBorder = hasBorder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
canvas.drawTexture(this, x, y, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
canvas.drawTexture(this, x, y, w, h);
|
||||
}
|
||||
|
||||
public void draw(GLCanvas canvas, RectF source, RectF target) {
|
||||
canvas.drawTexture(this, source, target);
|
||||
}
|
||||
|
||||
// onBind is called before GLCanvas binds this texture.
|
||||
// It should make sure the data is uploaded to GL memory.
|
||||
abstract protected boolean onBind(GLCanvas canvas);
|
||||
|
||||
// Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
|
||||
abstract protected int getTarget();
|
||||
|
||||
public boolean isLoaded() {
|
||||
return mState == STATE_LOADED;
|
||||
}
|
||||
|
||||
// recycle() is called when the texture will never be used again,
|
||||
// so it can free all resources.
|
||||
public void recycle() {
|
||||
freeResource();
|
||||
}
|
||||
|
||||
// yield() is called when the texture will not be used temporarily,
|
||||
// so it can free some resources.
|
||||
// The default implementation unloads the texture from GL memory, so
|
||||
// the subclass should make sure it can reload the texture to GL memory
|
||||
// later, or it will have to override this method.
|
||||
public void yield() {
|
||||
freeResource();
|
||||
}
|
||||
|
||||
private void freeResource() {
|
||||
GLCanvas canvas = mCanvasRef;
|
||||
if (canvas != null && mId != -1) {
|
||||
canvas.unloadTexture(this);
|
||||
mId = -1; // Don't free it again.
|
||||
}
|
||||
mState = STATE_UNLOADED;
|
||||
setAssociatedCanvas(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() {
|
||||
sInFinalizer.set(BasicTexture.class);
|
||||
recycle();
|
||||
sInFinalizer.set(null);
|
||||
}
|
||||
|
||||
// This is for deciding if we can call Bitmap's recycle().
|
||||
// We cannot call Bitmap's recycle() in finalizer because at that point
|
||||
// the finalizer of Bitmap may already be called so recycle() will crash.
|
||||
public static boolean inFinalizer() {
|
||||
return sInFinalizer.get() != null;
|
||||
}
|
||||
|
||||
public static void yieldAllTextures() {
|
||||
synchronized (sAllTextures) {
|
||||
for (BasicTexture t : sAllTextures.keySet()) {
|
||||
t.yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidateAllTextures() {
|
||||
synchronized (sAllTextures) {
|
||||
for (BasicTexture t : sAllTextures.keySet()) {
|
||||
t.mState = STATE_UNLOADED;
|
||||
t.setAssociatedCanvas(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
// BitmapTexture is a texture whose content is specified by a fixed Bitmap.
|
||||
//
|
||||
// The texture does not own the Bitmap. The user should make sure the Bitmap
|
||||
// is valid during the texture's lifetime. When the texture is recycled, it
|
||||
// does not free the Bitmap.
|
||||
public class BitmapTexture extends UploadedTexture {
|
||||
protected Bitmap mContentBitmap;
|
||||
|
||||
public BitmapTexture(Bitmap bitmap) {
|
||||
this(bitmap, false);
|
||||
}
|
||||
|
||||
public BitmapTexture(Bitmap bitmap, boolean hasBorder) {
|
||||
super(hasBorder);
|
||||
Assert.assertTrue(bitmap != null && !bitmap.isRecycled());
|
||||
mContentBitmap = bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
return mContentBitmap;
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
return mContentBitmap;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
// BitmapTexture is a texture whose content is specified by a fixed Bitmap.
|
||||
//
|
||||
// The texture does not own the Bitmap. The user should make sure the Bitmap
|
||||
// is valid during the texture's lifetime. When the texture is recycled, it
|
||||
// does not free the Bitmap.
|
||||
public class BitmapTexture extends UploadedTexture {
|
||||
protected Bitmap mContentBitmap;
|
||||
|
||||
public BitmapTexture(Bitmap bitmap) {
|
||||
this(bitmap, false);
|
||||
}
|
||||
|
||||
public BitmapTexture(Bitmap bitmap, boolean hasBorder) {
|
||||
super(hasBorder);
|
||||
Assert.assertTrue(bitmap != null && !bitmap.isRecycled());
|
||||
mContentBitmap = bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
return mContentBitmap;
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
return mContentBitmap;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Canvas;
|
||||
|
||||
// CanvasTexture is a texture whose content is the drawing on a Canvas.
|
||||
// The subclasses should override onDraw() to draw on the bitmap.
|
||||
// By default CanvasTexture is not opaque.
|
||||
abstract class CanvasTexture extends UploadedTexture {
|
||||
protected Canvas mCanvas;
|
||||
private final Config mConfig;
|
||||
|
||||
public CanvasTexture(int width, int height) {
|
||||
mConfig = Config.ARGB_8888;
|
||||
setSize(width, height);
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
@SuppressLint("WrongCall")
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, mConfig);
|
||||
mCanvas = new Canvas(bitmap);
|
||||
onDraw(mCanvas, bitmap);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
if (!inFinalizer()) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected void onDraw(Canvas canvas, Bitmap backing);
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Canvas;
|
||||
|
||||
// CanvasTexture is a texture whose content is the drawing on a Canvas.
|
||||
// The subclasses should override onDraw() to draw on the bitmap.
|
||||
// By default CanvasTexture is not opaque.
|
||||
abstract class CanvasTexture extends UploadedTexture {
|
||||
protected Canvas mCanvas;
|
||||
private final Config mConfig;
|
||||
|
||||
public CanvasTexture(int width, int height) {
|
||||
mConfig = Config.ARGB_8888;
|
||||
setSize(width, height);
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
@SuppressLint("WrongCall")
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, mConfig);
|
||||
mCanvas = new Canvas(bitmap);
|
||||
onDraw(mCanvas, bitmap);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
if (!inFinalizer()) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected void onDraw(Canvas canvas, Bitmap backing);
|
||||
}
|
||||
@@ -1,63 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
// ColorTexture is a texture which fills the rectangle with the specified color.
|
||||
public class ColorTexture implements Texture {
|
||||
|
||||
private final int mColor;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
public ColorTexture(int color) {
|
||||
mColor = color;
|
||||
mWidth = 1;
|
||||
mHeight = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
draw(canvas, x, y, mWidth, mHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
canvas.fillRect(x, y, w, h, mColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return Utils.isOpaque(mColor);
|
||||
}
|
||||
|
||||
public void setSize(int width, int height) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
// ColorTexture is a texture which fills the rectangle with the specified color.
|
||||
public class ColorTexture implements Texture {
|
||||
|
||||
private final int mColor;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
public ColorTexture(int color) {
|
||||
mColor = color;
|
||||
mWidth = 1;
|
||||
mHeight = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
draw(canvas, x, y, mWidth, mHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
canvas.fillRect(x, y, w, h, mColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return Utils.isOpaque(mColor);
|
||||
}
|
||||
|
||||
public void setSize(int width, int height) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
// ExtTexture is a texture whose content comes from a external texture.
|
||||
// Before drawing, setSize() should be called.
|
||||
public class ExtTexture extends BasicTexture {
|
||||
|
||||
private int mTarget;
|
||||
|
||||
public ExtTexture(GLCanvas canvas, int target) {
|
||||
GLId glId = canvas.getGLId();
|
||||
mId = glId.generateTexture();
|
||||
mTarget = target;
|
||||
}
|
||||
|
||||
private void uploadToCanvas(GLCanvas canvas) {
|
||||
canvas.setTextureParameters(this);
|
||||
setAssociatedCanvas(canvas);
|
||||
mState = STATE_LOADED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBind(GLCanvas canvas) {
|
||||
if (!isLoaded()) {
|
||||
uploadToCanvas(canvas);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTarget() {
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void yield() {
|
||||
// we cannot free the texture because we have no backup.
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
// ExtTexture is a texture whose content comes from a external texture.
|
||||
// Before drawing, setSize() should be called.
|
||||
public class ExtTexture extends BasicTexture {
|
||||
|
||||
private int mTarget;
|
||||
|
||||
public ExtTexture(GLCanvas canvas, int target) {
|
||||
GLId glId = canvas.getGLId();
|
||||
mId = glId.generateTexture();
|
||||
mTarget = target;
|
||||
}
|
||||
|
||||
private void uploadToCanvas(GLCanvas canvas) {
|
||||
canvas.setTextureParameters(this);
|
||||
setAssociatedCanvas(canvas);
|
||||
mState = STATE_LOADED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBind(GLCanvas canvas) {
|
||||
if (!isLoaded()) {
|
||||
uploadToCanvas(canvas);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTarget() {
|
||||
return mTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void yield() {
|
||||
// we cannot free the texture because we have no backup.
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
|
||||
// FadeInTexture is a texture which begins with a color, then gradually animates
|
||||
// into a given texture.
|
||||
public class FadeInTexture extends FadeTexture implements Texture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "FadeInTexture";
|
||||
|
||||
private final int mColor;
|
||||
private final TiledTexture mTexture;
|
||||
|
||||
public FadeInTexture(int color, TiledTexture texture) {
|
||||
super(texture.getWidth(), texture.getHeight(), texture.isOpaque());
|
||||
mColor = color;
|
||||
mTexture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
if (isAnimating()) {
|
||||
mTexture.drawMixed(canvas, mColor, getRatio(), x, y, w, h);
|
||||
} else {
|
||||
mTexture.draw(canvas, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
|
||||
// FadeInTexture is a texture which begins with a color, then gradually animates
|
||||
// into a given texture.
|
||||
public class FadeInTexture extends FadeTexture implements Texture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "FadeInTexture";
|
||||
|
||||
private final int mColor;
|
||||
private final TiledTexture mTexture;
|
||||
|
||||
public FadeInTexture(int color, TiledTexture texture) {
|
||||
super(texture.getWidth(), texture.getHeight(), texture.isOpaque());
|
||||
mColor = color;
|
||||
mTexture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
if (isAnimating()) {
|
||||
mTexture.drawMixed(canvas, mColor, getRatio(), x, y, w, h);
|
||||
} else {
|
||||
mTexture.draw(canvas, x, y, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
|
||||
// FadeOutTexture is a texture which begins with a given texture, then gradually animates
|
||||
// into fading out totally.
|
||||
public class FadeOutTexture extends FadeTexture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "FadeOutTexture";
|
||||
|
||||
private final BasicTexture mTexture;
|
||||
|
||||
public FadeOutTexture(BasicTexture texture) {
|
||||
super(texture.getWidth(), texture.getHeight(), texture.isOpaque());
|
||||
mTexture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
if (isAnimating()) {
|
||||
canvas.save(GLCanvas.SAVE_FLAG_ALPHA);
|
||||
canvas.setAlpha(getRatio());
|
||||
mTexture.draw(canvas, x, y, w, h);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
|
||||
// FadeOutTexture is a texture which begins with a given texture, then gradually animates
|
||||
// into fading out totally.
|
||||
public class FadeOutTexture extends FadeTexture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "FadeOutTexture";
|
||||
|
||||
private final BasicTexture mTexture;
|
||||
|
||||
public FadeOutTexture(BasicTexture texture) {
|
||||
super(texture.getWidth(), texture.getHeight(), texture.isOpaque());
|
||||
mTexture = texture;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
if (isAnimating()) {
|
||||
canvas.save(GLCanvas.SAVE_FLAG_ALPHA);
|
||||
canvas.setAlpha(getRatio());
|
||||
mTexture.draw(canvas, x, y, w, h);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.ui.AnimationTime;
|
||||
|
||||
// FadeTexture is a texture which fades the given texture along the time.
|
||||
public abstract class FadeTexture implements Texture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "FadeTexture";
|
||||
|
||||
// The duration of the fading animation in milliseconds
|
||||
public static final int DURATION = 180;
|
||||
|
||||
private final long mStartTime;
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final boolean mIsOpaque;
|
||||
private boolean mIsAnimating;
|
||||
|
||||
public FadeTexture(int width, int height, boolean opaque) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mIsOpaque = opaque;
|
||||
mStartTime = now();
|
||||
mIsAnimating = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
draw(canvas, x, y, mWidth, mHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return mIsOpaque;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
public boolean isAnimating() {
|
||||
if (mIsAnimating) {
|
||||
if (now() - mStartTime >= DURATION) {
|
||||
mIsAnimating = false;
|
||||
}
|
||||
}
|
||||
return mIsAnimating;
|
||||
}
|
||||
|
||||
protected float getRatio() {
|
||||
float r = (float)(now() - mStartTime) / DURATION;
|
||||
return Utils.clamp(1.0f - r, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private long now() {
|
||||
return AnimationTime.get();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.ui.AnimationTime;
|
||||
|
||||
// FadeTexture is a texture which fades the given texture along the time.
|
||||
public abstract class FadeTexture implements Texture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "FadeTexture";
|
||||
|
||||
// The duration of the fading animation in milliseconds
|
||||
public static final int DURATION = 180;
|
||||
|
||||
private final long mStartTime;
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final boolean mIsOpaque;
|
||||
private boolean mIsAnimating;
|
||||
|
||||
public FadeTexture(int width, int height, boolean opaque) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mIsOpaque = opaque;
|
||||
mStartTime = now();
|
||||
mIsAnimating = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
draw(canvas, x, y, mWidth, mHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return mIsOpaque;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
public boolean isAnimating() {
|
||||
if (mIsAnimating) {
|
||||
if (now() - mStartTime >= DURATION) {
|
||||
mIsAnimating = false;
|
||||
}
|
||||
}
|
||||
return mIsAnimating;
|
||||
}
|
||||
|
||||
protected float getRatio() {
|
||||
float r = (float)(now() - mStartTime) / DURATION;
|
||||
return Utils.clamp(1.0f - r, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
private long now() {
|
||||
return AnimationTime.get();
|
||||
}
|
||||
}
|
||||
@@ -1,68 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
import javax.microedition.khronos.opengles.GL11ExtensionPack;
|
||||
|
||||
/**
|
||||
* Open GL ES 1.1 implementation for generating and destroying texture IDs and
|
||||
* buffer IDs
|
||||
*/
|
||||
public class GLES11IdImpl implements GLId {
|
||||
private static int sNextId = 1;
|
||||
// Mutex for sNextId
|
||||
private static Object sLock = new Object();
|
||||
|
||||
@Override
|
||||
public int generateTexture() {
|
||||
synchronized (sLock) {
|
||||
return sNextId++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glGenBuffers(int n, int[] buffers, int offset) {
|
||||
synchronized (sLock) {
|
||||
while (n-- > 0) {
|
||||
buffers[offset + n] = sNextId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
|
||||
synchronized (sLock) {
|
||||
gl.glDeleteTextures(n, textures, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
|
||||
synchronized (sLock) {
|
||||
gl.glDeleteBuffers(n, buffers, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
|
||||
synchronized (sLock) {
|
||||
gl11ep.glDeleteFramebuffersOES(n, buffers, offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
import javax.microedition.khronos.opengles.GL11ExtensionPack;
|
||||
|
||||
/**
|
||||
* Open GL ES 1.1 implementation for generating and destroying texture IDs and
|
||||
* buffer IDs
|
||||
*/
|
||||
public class GLES11IdImpl implements GLId {
|
||||
private static int sNextId = 1;
|
||||
// Mutex for sNextId
|
||||
private static Object sLock = new Object();
|
||||
|
||||
@Override
|
||||
public int generateTexture() {
|
||||
synchronized (sLock) {
|
||||
return sNextId++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glGenBuffers(int n, int[] buffers, int offset) {
|
||||
synchronized (sLock) {
|
||||
while (n-- > 0) {
|
||||
buffers[offset + n] = sNextId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
|
||||
synchronized (sLock) {
|
||||
gl.glDeleteTextures(n, textures, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
|
||||
synchronized (sLock) {
|
||||
gl.glDeleteBuffers(n, buffers, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
|
||||
synchronized (sLock) {
|
||||
gl11ep.glDeleteFramebuffersOES(n, buffers, offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,42 +1,42 @@
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
import javax.microedition.khronos.opengles.GL11ExtensionPack;
|
||||
|
||||
public class GLES20IdImpl implements GLId {
|
||||
private final int[] mTempIntArray = new int[1];
|
||||
|
||||
@Override
|
||||
public int generateTexture() {
|
||||
GLES20.glGenTextures(1, mTempIntArray, 0);
|
||||
GLES20Canvas.checkError();
|
||||
return mTempIntArray[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glGenBuffers(int n, int[] buffers, int offset) {
|
||||
GLES20.glGenBuffers(n, buffers, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
|
||||
GLES20.glDeleteTextures(n, textures, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
|
||||
GLES20.glDeleteBuffers(n, buffers, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
|
||||
GLES20.glDeleteFramebuffers(n, buffers, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
}
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
import javax.microedition.khronos.opengles.GL11ExtensionPack;
|
||||
|
||||
public class GLES20IdImpl implements GLId {
|
||||
private final int[] mTempIntArray = new int[1];
|
||||
|
||||
@Override
|
||||
public int generateTexture() {
|
||||
GLES20.glGenTextures(1, mTempIntArray, 0);
|
||||
GLES20Canvas.checkError();
|
||||
return mTempIntArray[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glGenBuffers(int n, int[] buffers, int offset) {
|
||||
GLES20.glGenBuffers(n, buffers, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
|
||||
GLES20.glDeleteTextures(n, textures, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
|
||||
GLES20.glDeleteBuffers(n, buffers, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
|
||||
GLES20.glDeleteFramebuffers(n, buffers, offset);
|
||||
GLES20Canvas.checkError();
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
import javax.microedition.khronos.opengles.GL11ExtensionPack;
|
||||
|
||||
// This mimics corresponding GL functions.
|
||||
public interface GLId {
|
||||
public int generateTexture();
|
||||
|
||||
public void glGenBuffers(int n, int[] buffers, int offset);
|
||||
|
||||
public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset);
|
||||
|
||||
public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset);
|
||||
|
||||
public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset);
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import javax.microedition.khronos.opengles.GL11;
|
||||
import javax.microedition.khronos.opengles.GL11ExtensionPack;
|
||||
|
||||
// This mimics corresponding GL functions.
|
||||
public interface GLId {
|
||||
public int generateTexture();
|
||||
|
||||
public void glGenBuffers(int n, int[] buffers, int offset);
|
||||
|
||||
public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset);
|
||||
|
||||
public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset);
|
||||
|
||||
public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset);
|
||||
}
|
||||
@@ -1,48 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class GLPaint {
|
||||
private float mLineWidth = 1f;
|
||||
private int mColor = 0;
|
||||
|
||||
public GLPaint() {}
|
||||
|
||||
public GLPaint(int color, float width) {
|
||||
setColor(color);
|
||||
setLineWidth(width);
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return mColor;
|
||||
}
|
||||
|
||||
public void setLineWidth(float width) {
|
||||
Assert.assertTrue(width >= 0);
|
||||
mLineWidth = width;
|
||||
}
|
||||
|
||||
public float getLineWidth() {
|
||||
return mLineWidth;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class GLPaint {
|
||||
private float mLineWidth = 1f;
|
||||
private int mColor = 0;
|
||||
|
||||
public GLPaint() {}
|
||||
|
||||
public GLPaint(int color, float width) {
|
||||
setColor(color);
|
||||
setLineWidth(width);
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return mColor;
|
||||
}
|
||||
|
||||
public void setLineWidth(float width) {
|
||||
Assert.assertTrue(width >= 0);
|
||||
mLineWidth = width;
|
||||
}
|
||||
|
||||
public float getLineWidth() {
|
||||
return mLineWidth;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
|
||||
|
||||
// MultiLineTexture is a texture shows the content of a specified String.
|
||||
//
|
||||
// To create a MultiLineTexture, use the newInstance() method and specify
|
||||
// the String, the font size, and the color.
|
||||
class MultiLineTexture extends CanvasTexture {
|
||||
private final Layout mLayout;
|
||||
|
||||
private MultiLineTexture(Layout layout) {
|
||||
super(layout.getWidth(), layout.getHeight());
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
public static MultiLineTexture newInstance(
|
||||
String text, int maxWidth, float textSize, int color,
|
||||
Layout.Alignment alignment) {
|
||||
TextPaint paint = StringTexture.getDefaultPaint(textSize, color);
|
||||
Layout layout = new StaticLayout(text, 0, text.length(), paint,
|
||||
maxWidth, alignment, 1, 0, true, null, 0);
|
||||
|
||||
return new MultiLineTexture(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas, Bitmap backing) {
|
||||
mLayout.draw(canvas);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
|
||||
|
||||
// MultiLineTexture is a texture shows the content of a specified String.
|
||||
//
|
||||
// To create a MultiLineTexture, use the newInstance() method and specify
|
||||
// the String, the font size, and the color.
|
||||
class MultiLineTexture extends CanvasTexture {
|
||||
private final Layout mLayout;
|
||||
|
||||
private MultiLineTexture(Layout layout) {
|
||||
super(layout.getWidth(), layout.getHeight());
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
public static MultiLineTexture newInstance(
|
||||
String text, int maxWidth, float textSize, int color,
|
||||
Layout.Alignment alignment) {
|
||||
TextPaint paint = StringTexture.getDefaultPaint(textSize, color);
|
||||
Layout layout = new StaticLayout(text, 0, text.length(), paint,
|
||||
maxWidth, alignment, 1, 0, true, null, 0);
|
||||
|
||||
return new MultiLineTexture(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas, Bitmap backing) {
|
||||
mLayout.draw(canvas);
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
// See "frameworks/base/include/utils/ResourceTypes.h" for the format of
|
||||
// NinePatch chunk.
|
||||
class NinePatchChunk {
|
||||
|
||||
public static final int NO_COLOR = 0x00000001;
|
||||
public static final int TRANSPARENT_COLOR = 0x00000000;
|
||||
|
||||
public Rect mPaddings = new Rect();
|
||||
|
||||
public int mDivX[];
|
||||
public int mDivY[];
|
||||
public int mColor[];
|
||||
|
||||
private static void readIntArray(int[] data, ByteBuffer buffer) {
|
||||
for (int i = 0, n = data.length; i < n; ++i) {
|
||||
data[i] = buffer.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkDivCount(int length) {
|
||||
if (length == 0 || (length & 0x01) != 0) {
|
||||
throw new RuntimeException("invalid nine-patch: " + length);
|
||||
}
|
||||
}
|
||||
|
||||
public static NinePatchChunk deserialize(byte[] data) {
|
||||
ByteBuffer byteBuffer =
|
||||
ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
|
||||
|
||||
byte wasSerialized = byteBuffer.get();
|
||||
if (wasSerialized == 0) return null;
|
||||
|
||||
NinePatchChunk chunk = new NinePatchChunk();
|
||||
chunk.mDivX = new int[byteBuffer.get()];
|
||||
chunk.mDivY = new int[byteBuffer.get()];
|
||||
chunk.mColor = new int[byteBuffer.get()];
|
||||
|
||||
checkDivCount(chunk.mDivX.length);
|
||||
checkDivCount(chunk.mDivY.length);
|
||||
|
||||
// skip 8 bytes
|
||||
byteBuffer.getInt();
|
||||
byteBuffer.getInt();
|
||||
|
||||
chunk.mPaddings.left = byteBuffer.getInt();
|
||||
chunk.mPaddings.right = byteBuffer.getInt();
|
||||
chunk.mPaddings.top = byteBuffer.getInt();
|
||||
chunk.mPaddings.bottom = byteBuffer.getInt();
|
||||
|
||||
// skip 4 bytes
|
||||
byteBuffer.getInt();
|
||||
|
||||
readIntArray(chunk.mDivX, byteBuffer);
|
||||
readIntArray(chunk.mDivY, byteBuffer);
|
||||
readIntArray(chunk.mColor, byteBuffer);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
// See "frameworks/base/include/utils/ResourceTypes.h" for the format of
|
||||
// NinePatch chunk.
|
||||
class NinePatchChunk {
|
||||
|
||||
public static final int NO_COLOR = 0x00000001;
|
||||
public static final int TRANSPARENT_COLOR = 0x00000000;
|
||||
|
||||
public Rect mPaddings = new Rect();
|
||||
|
||||
public int mDivX[];
|
||||
public int mDivY[];
|
||||
public int mColor[];
|
||||
|
||||
private static void readIntArray(int[] data, ByteBuffer buffer) {
|
||||
for (int i = 0, n = data.length; i < n; ++i) {
|
||||
data[i] = buffer.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkDivCount(int length) {
|
||||
if (length == 0 || (length & 0x01) != 0) {
|
||||
throw new RuntimeException("invalid nine-patch: " + length);
|
||||
}
|
||||
}
|
||||
|
||||
public static NinePatchChunk deserialize(byte[] data) {
|
||||
ByteBuffer byteBuffer =
|
||||
ByteBuffer.wrap(data).order(ByteOrder.nativeOrder());
|
||||
|
||||
byte wasSerialized = byteBuffer.get();
|
||||
if (wasSerialized == 0) return null;
|
||||
|
||||
NinePatchChunk chunk = new NinePatchChunk();
|
||||
chunk.mDivX = new int[byteBuffer.get()];
|
||||
chunk.mDivY = new int[byteBuffer.get()];
|
||||
chunk.mColor = new int[byteBuffer.get()];
|
||||
|
||||
checkDivCount(chunk.mDivX.length);
|
||||
checkDivCount(chunk.mDivY.length);
|
||||
|
||||
// skip 8 bytes
|
||||
byteBuffer.getInt();
|
||||
byteBuffer.getInt();
|
||||
|
||||
chunk.mPaddings.left = byteBuffer.getInt();
|
||||
chunk.mPaddings.right = byteBuffer.getInt();
|
||||
chunk.mPaddings.top = byteBuffer.getInt();
|
||||
chunk.mPaddings.bottom = byteBuffer.getInt();
|
||||
|
||||
// skip 4 bytes
|
||||
byteBuffer.getInt();
|
||||
|
||||
readIntArray(chunk.mDivX, byteBuffer);
|
||||
readIntArray(chunk.mDivY, byteBuffer);
|
||||
readIntArray(chunk.mColor, byteBuffer);
|
||||
|
||||
return chunk;
|
||||
}
|
||||
}
|
||||
@@ -1,424 +1,424 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
// NinePatchTexture is a texture backed by a NinePatch resource.
|
||||
//
|
||||
// getPaddings() returns paddings specified in the NinePatch.
|
||||
// getNinePatchChunk() returns the layout data specified in the NinePatch.
|
||||
//
|
||||
public class NinePatchTexture extends ResourceTexture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "NinePatchTexture";
|
||||
private NinePatchChunk mChunk;
|
||||
private SmallCache<NinePatchInstance> mInstanceCache
|
||||
= new SmallCache<NinePatchInstance>();
|
||||
|
||||
public NinePatchTexture(Context context, int resId) {
|
||||
super(context, resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
if (mBitmap != null) return mBitmap;
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(
|
||||
mContext.getResources(), mResId, options);
|
||||
mBitmap = bitmap;
|
||||
setSize(bitmap.getWidth(), bitmap.getHeight());
|
||||
byte[] chunkData = bitmap.getNinePatchChunk();
|
||||
mChunk = chunkData == null
|
||||
? null
|
||||
: NinePatchChunk.deserialize(bitmap.getNinePatchChunk());
|
||||
if (mChunk == null) {
|
||||
throw new RuntimeException("invalid nine-patch image: " + mResId);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public Rect getPaddings() {
|
||||
// get the paddings from nine patch
|
||||
if (mChunk == null) onGetBitmap();
|
||||
return mChunk.mPaddings;
|
||||
}
|
||||
|
||||
public NinePatchChunk getNinePatchChunk() {
|
||||
if (mChunk == null) onGetBitmap();
|
||||
return mChunk;
|
||||
}
|
||||
|
||||
// This is a simple cache for a small number of things. Linear search
|
||||
// is used because the cache is small. It also tries to remove less used
|
||||
// item when the cache is full by moving the often-used items to the front.
|
||||
private static class SmallCache<V> {
|
||||
private static final int CACHE_SIZE = 16;
|
||||
private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2;
|
||||
private int[] mKey = new int[CACHE_SIZE];
|
||||
private V[] mValue = (V[]) new Object[CACHE_SIZE];
|
||||
private int mCount; // number of items in this cache
|
||||
|
||||
// Puts a value into the cache. If the cache is full, also returns
|
||||
// a less used item, otherwise returns null.
|
||||
public V put(int key, V value) {
|
||||
if (mCount == CACHE_SIZE) {
|
||||
V old = mValue[CACHE_SIZE - 1]; // remove the last item
|
||||
mKey[CACHE_SIZE - 1] = key;
|
||||
mValue[CACHE_SIZE - 1] = value;
|
||||
return old;
|
||||
} else {
|
||||
mKey[mCount] = key;
|
||||
mValue[mCount] = value;
|
||||
mCount++;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public V get(int key) {
|
||||
for (int i = 0; i < mCount; i++) {
|
||||
if (mKey[i] == key) {
|
||||
// Move the accessed item one position to the front, so it
|
||||
// will less likely to be removed when cache is full. Only
|
||||
// do this if the cache is starting to get full.
|
||||
if (mCount > CACHE_SIZE_START_MOVE && i > 0) {
|
||||
int tmpKey = mKey[i];
|
||||
mKey[i] = mKey[i - 1];
|
||||
mKey[i - 1] = tmpKey;
|
||||
|
||||
V tmpValue = mValue[i];
|
||||
mValue[i] = mValue[i - 1];
|
||||
mValue[i - 1] = tmpValue;
|
||||
}
|
||||
return mValue[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
for (int i = 0; i < mCount; i++) {
|
||||
mValue[i] = null; // make sure it's can be garbage-collected.
|
||||
}
|
||||
mCount = 0;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
public V valueAt(int i) {
|
||||
return mValue[i];
|
||||
}
|
||||
}
|
||||
|
||||
private NinePatchInstance findInstance(GLCanvas canvas, int w, int h) {
|
||||
int key = w;
|
||||
key = (key << 16) | h;
|
||||
NinePatchInstance instance = mInstanceCache.get(key);
|
||||
|
||||
if (instance == null) {
|
||||
instance = new NinePatchInstance(this, w, h);
|
||||
NinePatchInstance removed = mInstanceCache.put(key, instance);
|
||||
if (removed != null) {
|
||||
removed.recycle(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
if (!isLoaded()) {
|
||||
mInstanceCache.clear();
|
||||
}
|
||||
|
||||
if (w != 0 && h != 0) {
|
||||
findInstance(canvas, w, h).draw(canvas, this, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
super.recycle();
|
||||
GLCanvas canvas = mCanvasRef;
|
||||
if (canvas == null) return;
|
||||
int n = mInstanceCache.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
NinePatchInstance instance = mInstanceCache.valueAt(i);
|
||||
instance.recycle(canvas);
|
||||
}
|
||||
mInstanceCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// This keeps data for a specialization of NinePatchTexture with the size
|
||||
// (width, height). We pre-compute the coordinates for efficiency.
|
||||
class NinePatchInstance {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "NinePatchInstance";
|
||||
|
||||
// We need 16 vertices for a normal nine-patch image (the 4x4 vertices)
|
||||
private static final int VERTEX_BUFFER_SIZE = 16 * 2;
|
||||
|
||||
// We need 22 indices for a normal nine-patch image, plus 2 for each
|
||||
// transparent region. Current there are at most 1 transparent region.
|
||||
private static final int INDEX_BUFFER_SIZE = 22 + 2;
|
||||
|
||||
private FloatBuffer mXyBuffer;
|
||||
private FloatBuffer mUvBuffer;
|
||||
private ByteBuffer mIndexBuffer;
|
||||
|
||||
// Names for buffer names: xy, uv, index.
|
||||
private int mXyBufferName = -1;
|
||||
private int mUvBufferName;
|
||||
private int mIndexBufferName;
|
||||
|
||||
private int mIdxCount;
|
||||
|
||||
public NinePatchInstance(NinePatchTexture tex, int width, int height) {
|
||||
NinePatchChunk chunk = tex.getNinePatchChunk();
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw new RuntimeException("invalid dimension");
|
||||
}
|
||||
|
||||
// The code should be easily extended to handle the general cases by
|
||||
// allocating more space for buffers. But let's just handle the only
|
||||
// use case.
|
||||
if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) {
|
||||
throw new RuntimeException("unsupported nine patch");
|
||||
}
|
||||
|
||||
float divX[] = new float[4];
|
||||
float divY[] = new float[4];
|
||||
float divU[] = new float[4];
|
||||
float divV[] = new float[4];
|
||||
|
||||
int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width);
|
||||
int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height);
|
||||
|
||||
prepareVertexData(divX, divY, divU, divV, nx, ny, chunk.mColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stretches the texture according to the nine-patch rules. It will
|
||||
* linearly distribute the strechy parts defined in the nine-patch chunk to
|
||||
* the target area.
|
||||
*
|
||||
* <pre>
|
||||
* source
|
||||
* /--------------^---------------\
|
||||
* u0 u1 u2 u3 u4 u5
|
||||
* div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
|
||||
* | div0 div1 div2 div3 |
|
||||
* | | / / / /
|
||||
* | | / / / /
|
||||
* | | / / / /
|
||||
* |fffff|ssss|fff|sss|ffff| ---> x
|
||||
* x0 x1 x2 x3 x4 x5
|
||||
* \----------v------------/
|
||||
* target
|
||||
*
|
||||
* f: fixed segment
|
||||
* s: stretchy segment
|
||||
* </pre>
|
||||
*
|
||||
* @param div the stretch parts defined in nine-patch chunk
|
||||
* @param source the length of the texture
|
||||
* @param target the length on the drawing plan
|
||||
* @param u output, the positions of these dividers in the texture
|
||||
* coordinate
|
||||
* @param x output, the corresponding position of these dividers on the
|
||||
* drawing plan
|
||||
* @return the number of these dividers.
|
||||
*/
|
||||
private static int stretch(
|
||||
float x[], float u[], int div[], int source, int target) {
|
||||
int textureSize = Utils.nextPowerOf2(source);
|
||||
float textureBound = (float) source / textureSize;
|
||||
|
||||
float stretch = 0;
|
||||
for (int i = 0, n = div.length; i < n; i += 2) {
|
||||
stretch += div[i + 1] - div[i];
|
||||
}
|
||||
|
||||
float remaining = target - source + stretch;
|
||||
|
||||
float lastX = 0;
|
||||
float lastU = 0;
|
||||
|
||||
x[0] = 0;
|
||||
u[0] = 0;
|
||||
for (int i = 0, n = div.length; i < n; i += 2) {
|
||||
// Make the stretchy segment a little smaller to prevent sampling
|
||||
// on neighboring fixed segments.
|
||||
// fixed segment
|
||||
x[i + 1] = lastX + (div[i] - lastU) + 0.5f;
|
||||
u[i + 1] = Math.min((div[i] + 0.5f) / textureSize, textureBound);
|
||||
|
||||
// stretchy segment
|
||||
float partU = div[i + 1] - div[i];
|
||||
float partX = remaining * partU / stretch;
|
||||
remaining -= partX;
|
||||
stretch -= partU;
|
||||
|
||||
lastX = x[i + 1] + partX;
|
||||
lastU = div[i + 1];
|
||||
x[i + 2] = lastX - 0.5f;
|
||||
u[i + 2] = Math.min((lastU - 0.5f)/ textureSize, textureBound);
|
||||
}
|
||||
// the last fixed segment
|
||||
x[div.length + 1] = target;
|
||||
u[div.length + 1] = textureBound;
|
||||
|
||||
// remove segments with length 0.
|
||||
int last = 0;
|
||||
for (int i = 1, n = div.length + 2; i < n; ++i) {
|
||||
if ((x[i] - x[last]) < 1f) continue;
|
||||
x[++last] = x[i];
|
||||
u[last] = u[i];
|
||||
}
|
||||
return last + 1;
|
||||
}
|
||||
|
||||
private void prepareVertexData(float x[], float y[], float u[], float v[],
|
||||
int nx, int ny, int[] color) {
|
||||
/*
|
||||
* Given a 3x3 nine-patch image, the vertex order is defined as the
|
||||
* following graph:
|
||||
*
|
||||
* (0) (1) (2) (3)
|
||||
* | /| /| /|
|
||||
* | / | / | / |
|
||||
* (4) (5) (6) (7)
|
||||
* | \ | \ | \ |
|
||||
* | \| \| \|
|
||||
* (8) (9) (A) (B)
|
||||
* | /| /| /|
|
||||
* | / | / | / |
|
||||
* (C) (D) (E) (F)
|
||||
*
|
||||
* And we draw the triangle strip in the following index order:
|
||||
*
|
||||
* index: 04152637B6A5948C9DAEBF
|
||||
*/
|
||||
int pntCount = 0;
|
||||
float xy[] = new float[VERTEX_BUFFER_SIZE];
|
||||
float uv[] = new float[VERTEX_BUFFER_SIZE];
|
||||
for (int j = 0; j < ny; ++j) {
|
||||
for (int i = 0; i < nx; ++i) {
|
||||
int xIndex = (pntCount++) << 1;
|
||||
int yIndex = xIndex + 1;
|
||||
xy[xIndex] = x[i];
|
||||
xy[yIndex] = y[j];
|
||||
uv[xIndex] = u[i];
|
||||
uv[yIndex] = v[j];
|
||||
}
|
||||
}
|
||||
|
||||
int idxCount = 1;
|
||||
boolean isForward = false;
|
||||
byte index[] = new byte[INDEX_BUFFER_SIZE];
|
||||
for (int row = 0; row < ny - 1; row++) {
|
||||
--idxCount;
|
||||
isForward = !isForward;
|
||||
|
||||
int start, end, inc;
|
||||
if (isForward) {
|
||||
start = 0;
|
||||
end = nx;
|
||||
inc = 1;
|
||||
} else {
|
||||
start = nx - 1;
|
||||
end = -1;
|
||||
inc = -1;
|
||||
}
|
||||
|
||||
for (int col = start; col != end; col += inc) {
|
||||
int k = row * nx + col;
|
||||
if (col != start) {
|
||||
int colorIdx = row * (nx - 1) + col;
|
||||
if (isForward) colorIdx--;
|
||||
if (color[colorIdx] == NinePatchChunk.TRANSPARENT_COLOR) {
|
||||
index[idxCount] = index[idxCount - 1];
|
||||
++idxCount;
|
||||
index[idxCount++] = (byte) k;
|
||||
}
|
||||
}
|
||||
|
||||
index[idxCount++] = (byte) k;
|
||||
index[idxCount++] = (byte) (k + nx);
|
||||
}
|
||||
}
|
||||
|
||||
mIdxCount = idxCount;
|
||||
|
||||
int size = (pntCount * 2) * (Float.SIZE / Byte.SIZE);
|
||||
mXyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
|
||||
mUvBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
|
||||
mIndexBuffer = allocateDirectNativeOrderBuffer(mIdxCount);
|
||||
|
||||
mXyBuffer.put(xy, 0, pntCount * 2).position(0);
|
||||
mUvBuffer.put(uv, 0, pntCount * 2).position(0);
|
||||
mIndexBuffer.put(index, 0, idxCount).position(0);
|
||||
}
|
||||
|
||||
private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
|
||||
return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
private void prepareBuffers(GLCanvas canvas) {
|
||||
mXyBufferName = canvas.uploadBuffer(mXyBuffer);
|
||||
mUvBufferName = canvas.uploadBuffer(mUvBuffer);
|
||||
mIndexBufferName = canvas.uploadBuffer(mIndexBuffer);
|
||||
|
||||
// These buffers are never used again.
|
||||
mXyBuffer = null;
|
||||
mUvBuffer = null;
|
||||
mIndexBuffer = null;
|
||||
}
|
||||
|
||||
public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) {
|
||||
if (mXyBufferName == -1) {
|
||||
prepareBuffers(canvas);
|
||||
}
|
||||
canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount);
|
||||
}
|
||||
|
||||
public void recycle(GLCanvas canvas) {
|
||||
if (mXyBuffer == null) {
|
||||
canvas.deleteBuffer(mXyBufferName);
|
||||
canvas.deleteBuffer(mUvBufferName);
|
||||
canvas.deleteBuffer(mIndexBufferName);
|
||||
mXyBufferName = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
// NinePatchTexture is a texture backed by a NinePatch resource.
|
||||
//
|
||||
// getPaddings() returns paddings specified in the NinePatch.
|
||||
// getNinePatchChunk() returns the layout data specified in the NinePatch.
|
||||
//
|
||||
public class NinePatchTexture extends ResourceTexture {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "NinePatchTexture";
|
||||
private NinePatchChunk mChunk;
|
||||
private SmallCache<NinePatchInstance> mInstanceCache
|
||||
= new SmallCache<NinePatchInstance>();
|
||||
|
||||
public NinePatchTexture(Context context, int resId) {
|
||||
super(context, resId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
if (mBitmap != null) return mBitmap;
|
||||
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(
|
||||
mContext.getResources(), mResId, options);
|
||||
mBitmap = bitmap;
|
||||
setSize(bitmap.getWidth(), bitmap.getHeight());
|
||||
byte[] chunkData = bitmap.getNinePatchChunk();
|
||||
mChunk = chunkData == null
|
||||
? null
|
||||
: NinePatchChunk.deserialize(bitmap.getNinePatchChunk());
|
||||
if (mChunk == null) {
|
||||
throw new RuntimeException("invalid nine-patch image: " + mResId);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public Rect getPaddings() {
|
||||
// get the paddings from nine patch
|
||||
if (mChunk == null) onGetBitmap();
|
||||
return mChunk.mPaddings;
|
||||
}
|
||||
|
||||
public NinePatchChunk getNinePatchChunk() {
|
||||
if (mChunk == null) onGetBitmap();
|
||||
return mChunk;
|
||||
}
|
||||
|
||||
// This is a simple cache for a small number of things. Linear search
|
||||
// is used because the cache is small. It also tries to remove less used
|
||||
// item when the cache is full by moving the often-used items to the front.
|
||||
private static class SmallCache<V> {
|
||||
private static final int CACHE_SIZE = 16;
|
||||
private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2;
|
||||
private int[] mKey = new int[CACHE_SIZE];
|
||||
private V[] mValue = (V[]) new Object[CACHE_SIZE];
|
||||
private int mCount; // number of items in this cache
|
||||
|
||||
// Puts a value into the cache. If the cache is full, also returns
|
||||
// a less used item, otherwise returns null.
|
||||
public V put(int key, V value) {
|
||||
if (mCount == CACHE_SIZE) {
|
||||
V old = mValue[CACHE_SIZE - 1]; // remove the last item
|
||||
mKey[CACHE_SIZE - 1] = key;
|
||||
mValue[CACHE_SIZE - 1] = value;
|
||||
return old;
|
||||
} else {
|
||||
mKey[mCount] = key;
|
||||
mValue[mCount] = value;
|
||||
mCount++;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public V get(int key) {
|
||||
for (int i = 0; i < mCount; i++) {
|
||||
if (mKey[i] == key) {
|
||||
// Move the accessed item one position to the front, so it
|
||||
// will less likely to be removed when cache is full. Only
|
||||
// do this if the cache is starting to get full.
|
||||
if (mCount > CACHE_SIZE_START_MOVE && i > 0) {
|
||||
int tmpKey = mKey[i];
|
||||
mKey[i] = mKey[i - 1];
|
||||
mKey[i - 1] = tmpKey;
|
||||
|
||||
V tmpValue = mValue[i];
|
||||
mValue[i] = mValue[i - 1];
|
||||
mValue[i - 1] = tmpValue;
|
||||
}
|
||||
return mValue[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
for (int i = 0; i < mCount; i++) {
|
||||
mValue[i] = null; // make sure it's can be garbage-collected.
|
||||
}
|
||||
mCount = 0;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
public V valueAt(int i) {
|
||||
return mValue[i];
|
||||
}
|
||||
}
|
||||
|
||||
private NinePatchInstance findInstance(GLCanvas canvas, int w, int h) {
|
||||
int key = w;
|
||||
key = (key << 16) | h;
|
||||
NinePatchInstance instance = mInstanceCache.get(key);
|
||||
|
||||
if (instance == null) {
|
||||
instance = new NinePatchInstance(this, w, h);
|
||||
NinePatchInstance removed = mInstanceCache.put(key, instance);
|
||||
if (removed != null) {
|
||||
removed.recycle(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h) {
|
||||
if (!isLoaded()) {
|
||||
mInstanceCache.clear();
|
||||
}
|
||||
|
||||
if (w != 0 && h != 0) {
|
||||
findInstance(canvas, w, h).draw(canvas, this, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
super.recycle();
|
||||
GLCanvas canvas = mCanvasRef;
|
||||
if (canvas == null) return;
|
||||
int n = mInstanceCache.size();
|
||||
for (int i = 0; i < n; i++) {
|
||||
NinePatchInstance instance = mInstanceCache.valueAt(i);
|
||||
instance.recycle(canvas);
|
||||
}
|
||||
mInstanceCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// This keeps data for a specialization of NinePatchTexture with the size
|
||||
// (width, height). We pre-compute the coordinates for efficiency.
|
||||
class NinePatchInstance {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "NinePatchInstance";
|
||||
|
||||
// We need 16 vertices for a normal nine-patch image (the 4x4 vertices)
|
||||
private static final int VERTEX_BUFFER_SIZE = 16 * 2;
|
||||
|
||||
// We need 22 indices for a normal nine-patch image, plus 2 for each
|
||||
// transparent region. Current there are at most 1 transparent region.
|
||||
private static final int INDEX_BUFFER_SIZE = 22 + 2;
|
||||
|
||||
private FloatBuffer mXyBuffer;
|
||||
private FloatBuffer mUvBuffer;
|
||||
private ByteBuffer mIndexBuffer;
|
||||
|
||||
// Names for buffer names: xy, uv, index.
|
||||
private int mXyBufferName = -1;
|
||||
private int mUvBufferName;
|
||||
private int mIndexBufferName;
|
||||
|
||||
private int mIdxCount;
|
||||
|
||||
public NinePatchInstance(NinePatchTexture tex, int width, int height) {
|
||||
NinePatchChunk chunk = tex.getNinePatchChunk();
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
throw new RuntimeException("invalid dimension");
|
||||
}
|
||||
|
||||
// The code should be easily extended to handle the general cases by
|
||||
// allocating more space for buffers. But let's just handle the only
|
||||
// use case.
|
||||
if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) {
|
||||
throw new RuntimeException("unsupported nine patch");
|
||||
}
|
||||
|
||||
float divX[] = new float[4];
|
||||
float divY[] = new float[4];
|
||||
float divU[] = new float[4];
|
||||
float divV[] = new float[4];
|
||||
|
||||
int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width);
|
||||
int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height);
|
||||
|
||||
prepareVertexData(divX, divY, divU, divV, nx, ny, chunk.mColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stretches the texture according to the nine-patch rules. It will
|
||||
* linearly distribute the strechy parts defined in the nine-patch chunk to
|
||||
* the target area.
|
||||
*
|
||||
* <pre>
|
||||
* source
|
||||
* /--------------^---------------\
|
||||
* u0 u1 u2 u3 u4 u5
|
||||
* div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
|
||||
* | div0 div1 div2 div3 |
|
||||
* | | / / / /
|
||||
* | | / / / /
|
||||
* | | / / / /
|
||||
* |fffff|ssss|fff|sss|ffff| ---> x
|
||||
* x0 x1 x2 x3 x4 x5
|
||||
* \----------v------------/
|
||||
* target
|
||||
*
|
||||
* f: fixed segment
|
||||
* s: stretchy segment
|
||||
* </pre>
|
||||
*
|
||||
* @param div the stretch parts defined in nine-patch chunk
|
||||
* @param source the length of the texture
|
||||
* @param target the length on the drawing plan
|
||||
* @param u output, the positions of these dividers in the texture
|
||||
* coordinate
|
||||
* @param x output, the corresponding position of these dividers on the
|
||||
* drawing plan
|
||||
* @return the number of these dividers.
|
||||
*/
|
||||
private static int stretch(
|
||||
float x[], float u[], int div[], int source, int target) {
|
||||
int textureSize = Utils.nextPowerOf2(source);
|
||||
float textureBound = (float) source / textureSize;
|
||||
|
||||
float stretch = 0;
|
||||
for (int i = 0, n = div.length; i < n; i += 2) {
|
||||
stretch += div[i + 1] - div[i];
|
||||
}
|
||||
|
||||
float remaining = target - source + stretch;
|
||||
|
||||
float lastX = 0;
|
||||
float lastU = 0;
|
||||
|
||||
x[0] = 0;
|
||||
u[0] = 0;
|
||||
for (int i = 0, n = div.length; i < n; i += 2) {
|
||||
// Make the stretchy segment a little smaller to prevent sampling
|
||||
// on neighboring fixed segments.
|
||||
// fixed segment
|
||||
x[i + 1] = lastX + (div[i] - lastU) + 0.5f;
|
||||
u[i + 1] = Math.min((div[i] + 0.5f) / textureSize, textureBound);
|
||||
|
||||
// stretchy segment
|
||||
float partU = div[i + 1] - div[i];
|
||||
float partX = remaining * partU / stretch;
|
||||
remaining -= partX;
|
||||
stretch -= partU;
|
||||
|
||||
lastX = x[i + 1] + partX;
|
||||
lastU = div[i + 1];
|
||||
x[i + 2] = lastX - 0.5f;
|
||||
u[i + 2] = Math.min((lastU - 0.5f)/ textureSize, textureBound);
|
||||
}
|
||||
// the last fixed segment
|
||||
x[div.length + 1] = target;
|
||||
u[div.length + 1] = textureBound;
|
||||
|
||||
// remove segments with length 0.
|
||||
int last = 0;
|
||||
for (int i = 1, n = div.length + 2; i < n; ++i) {
|
||||
if ((x[i] - x[last]) < 1f) continue;
|
||||
x[++last] = x[i];
|
||||
u[last] = u[i];
|
||||
}
|
||||
return last + 1;
|
||||
}
|
||||
|
||||
private void prepareVertexData(float x[], float y[], float u[], float v[],
|
||||
int nx, int ny, int[] color) {
|
||||
/*
|
||||
* Given a 3x3 nine-patch image, the vertex order is defined as the
|
||||
* following graph:
|
||||
*
|
||||
* (0) (1) (2) (3)
|
||||
* | /| /| /|
|
||||
* | / | / | / |
|
||||
* (4) (5) (6) (7)
|
||||
* | \ | \ | \ |
|
||||
* | \| \| \|
|
||||
* (8) (9) (A) (B)
|
||||
* | /| /| /|
|
||||
* | / | / | / |
|
||||
* (C) (D) (E) (F)
|
||||
*
|
||||
* And we draw the triangle strip in the following index order:
|
||||
*
|
||||
* index: 04152637B6A5948C9DAEBF
|
||||
*/
|
||||
int pntCount = 0;
|
||||
float xy[] = new float[VERTEX_BUFFER_SIZE];
|
||||
float uv[] = new float[VERTEX_BUFFER_SIZE];
|
||||
for (int j = 0; j < ny; ++j) {
|
||||
for (int i = 0; i < nx; ++i) {
|
||||
int xIndex = (pntCount++) << 1;
|
||||
int yIndex = xIndex + 1;
|
||||
xy[xIndex] = x[i];
|
||||
xy[yIndex] = y[j];
|
||||
uv[xIndex] = u[i];
|
||||
uv[yIndex] = v[j];
|
||||
}
|
||||
}
|
||||
|
||||
int idxCount = 1;
|
||||
boolean isForward = false;
|
||||
byte index[] = new byte[INDEX_BUFFER_SIZE];
|
||||
for (int row = 0; row < ny - 1; row++) {
|
||||
--idxCount;
|
||||
isForward = !isForward;
|
||||
|
||||
int start, end, inc;
|
||||
if (isForward) {
|
||||
start = 0;
|
||||
end = nx;
|
||||
inc = 1;
|
||||
} else {
|
||||
start = nx - 1;
|
||||
end = -1;
|
||||
inc = -1;
|
||||
}
|
||||
|
||||
for (int col = start; col != end; col += inc) {
|
||||
int k = row * nx + col;
|
||||
if (col != start) {
|
||||
int colorIdx = row * (nx - 1) + col;
|
||||
if (isForward) colorIdx--;
|
||||
if (color[colorIdx] == NinePatchChunk.TRANSPARENT_COLOR) {
|
||||
index[idxCount] = index[idxCount - 1];
|
||||
++idxCount;
|
||||
index[idxCount++] = (byte) k;
|
||||
}
|
||||
}
|
||||
|
||||
index[idxCount++] = (byte) k;
|
||||
index[idxCount++] = (byte) (k + nx);
|
||||
}
|
||||
}
|
||||
|
||||
mIdxCount = idxCount;
|
||||
|
||||
int size = (pntCount * 2) * (Float.SIZE / Byte.SIZE);
|
||||
mXyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
|
||||
mUvBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer();
|
||||
mIndexBuffer = allocateDirectNativeOrderBuffer(mIdxCount);
|
||||
|
||||
mXyBuffer.put(xy, 0, pntCount * 2).position(0);
|
||||
mUvBuffer.put(uv, 0, pntCount * 2).position(0);
|
||||
mIndexBuffer.put(index, 0, idxCount).position(0);
|
||||
}
|
||||
|
||||
private static ByteBuffer allocateDirectNativeOrderBuffer(int size) {
|
||||
return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
private void prepareBuffers(GLCanvas canvas) {
|
||||
mXyBufferName = canvas.uploadBuffer(mXyBuffer);
|
||||
mUvBufferName = canvas.uploadBuffer(mUvBuffer);
|
||||
mIndexBufferName = canvas.uploadBuffer(mIndexBuffer);
|
||||
|
||||
// These buffers are never used again.
|
||||
mXyBuffer = null;
|
||||
mUvBuffer = null;
|
||||
mIndexBuffer = null;
|
||||
}
|
||||
|
||||
public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) {
|
||||
if (mXyBufferName == -1) {
|
||||
prepareBuffers(canvas);
|
||||
}
|
||||
canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount);
|
||||
}
|
||||
|
||||
public void recycle(GLCanvas canvas) {
|
||||
if (mXyBuffer == null) {
|
||||
canvas.deleteBuffer(mXyBufferName);
|
||||
canvas.deleteBuffer(mUvBufferName);
|
||||
canvas.deleteBuffer(mIndexBufferName);
|
||||
mXyBufferName = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
// ResourceTexture is a texture whose Bitmap is decoded from a resource.
|
||||
// By default ResourceTexture is not opaque.
|
||||
public class ResourceTexture extends UploadedTexture {
|
||||
|
||||
protected final Context mContext;
|
||||
protected final int mResId;
|
||||
|
||||
public ResourceTexture(Context context, int resId) {
|
||||
Assert.assertNotNull(context);
|
||||
mContext = context;
|
||||
mResId = resId;
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
|
||||
return BitmapFactory.decodeResource(
|
||||
mContext.getResources(), mResId, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
if (!inFinalizer()) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
// ResourceTexture is a texture whose Bitmap is decoded from a resource.
|
||||
// By default ResourceTexture is not opaque.
|
||||
public class ResourceTexture extends UploadedTexture {
|
||||
|
||||
protected final Context mContext;
|
||||
protected final int mResId;
|
||||
|
||||
public ResourceTexture(Context context, int resId) {
|
||||
Assert.assertNotNull(context);
|
||||
mContext = context;
|
||||
mResId = resId;
|
||||
setOpaque(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
|
||||
return BitmapFactory.decodeResource(
|
||||
mContext.getResources(), mResId, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
if (!inFinalizer()) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.FloatMath;
|
||||
|
||||
// StringTexture is a texture shows the content of a specified String.
|
||||
//
|
||||
// To create a StringTexture, use the newInstance() method and specify
|
||||
// the String, the font size, and the color.
|
||||
public class StringTexture extends CanvasTexture {
|
||||
private final String mText;
|
||||
private final TextPaint mPaint;
|
||||
private final FontMetricsInt mMetrics;
|
||||
|
||||
private StringTexture(String text, TextPaint paint,
|
||||
FontMetricsInt metrics, int width, int height) {
|
||||
super(width, height);
|
||||
mText = text;
|
||||
mPaint = paint;
|
||||
mMetrics = metrics;
|
||||
}
|
||||
|
||||
public static TextPaint getDefaultPaint(float textSize, int color) {
|
||||
TextPaint paint = new TextPaint();
|
||||
paint.setTextSize(textSize);
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(color);
|
||||
paint.setShadowLayer(2f, 0f, 0f, Color.BLACK);
|
||||
return paint;
|
||||
}
|
||||
|
||||
public static StringTexture newInstance(
|
||||
String text, float textSize, int color) {
|
||||
return newInstance(text, getDefaultPaint(textSize, color));
|
||||
}
|
||||
|
||||
public static StringTexture newInstance(
|
||||
String text, float textSize, int color,
|
||||
float lengthLimit, boolean isBold) {
|
||||
TextPaint paint = getDefaultPaint(textSize, color);
|
||||
if (isBold) {
|
||||
paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
}
|
||||
if (lengthLimit > 0) {
|
||||
text = TextUtils.ellipsize(
|
||||
text, paint, lengthLimit, TextUtils.TruncateAt.END).toString();
|
||||
}
|
||||
return newInstance(text, paint);
|
||||
}
|
||||
|
||||
private static StringTexture newInstance(String text, TextPaint paint) {
|
||||
FontMetricsInt metrics = paint.getFontMetricsInt();
|
||||
int width = (int) FloatMath.ceil(paint.measureText(text));
|
||||
int height = metrics.bottom - metrics.top;
|
||||
// The texture size needs to be at least 1x1.
|
||||
if (width <= 0) width = 1;
|
||||
if (height <= 0) height = 1;
|
||||
return new StringTexture(text, paint, metrics, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas, Bitmap backing) {
|
||||
canvas.translate(0, -mMetrics.ascent);
|
||||
canvas.drawText(mText, 0, 0, mPaint);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.FloatMath;
|
||||
|
||||
// StringTexture is a texture shows the content of a specified String.
|
||||
//
|
||||
// To create a StringTexture, use the newInstance() method and specify
|
||||
// the String, the font size, and the color.
|
||||
public class StringTexture extends CanvasTexture {
|
||||
private final String mText;
|
||||
private final TextPaint mPaint;
|
||||
private final FontMetricsInt mMetrics;
|
||||
|
||||
private StringTexture(String text, TextPaint paint,
|
||||
FontMetricsInt metrics, int width, int height) {
|
||||
super(width, height);
|
||||
mText = text;
|
||||
mPaint = paint;
|
||||
mMetrics = metrics;
|
||||
}
|
||||
|
||||
public static TextPaint getDefaultPaint(float textSize, int color) {
|
||||
TextPaint paint = new TextPaint();
|
||||
paint.setTextSize(textSize);
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(color);
|
||||
paint.setShadowLayer(2f, 0f, 0f, Color.BLACK);
|
||||
return paint;
|
||||
}
|
||||
|
||||
public static StringTexture newInstance(
|
||||
String text, float textSize, int color) {
|
||||
return newInstance(text, getDefaultPaint(textSize, color));
|
||||
}
|
||||
|
||||
public static StringTexture newInstance(
|
||||
String text, float textSize, int color,
|
||||
float lengthLimit, boolean isBold) {
|
||||
TextPaint paint = getDefaultPaint(textSize, color);
|
||||
if (isBold) {
|
||||
paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
|
||||
}
|
||||
if (lengthLimit > 0) {
|
||||
text = TextUtils.ellipsize(
|
||||
text, paint, lengthLimit, TextUtils.TruncateAt.END).toString();
|
||||
}
|
||||
return newInstance(text, paint);
|
||||
}
|
||||
|
||||
private static StringTexture newInstance(String text, TextPaint paint) {
|
||||
FontMetricsInt metrics = paint.getFontMetricsInt();
|
||||
int width = (int) FloatMath.ceil(paint.measureText(text));
|
||||
int height = metrics.bottom - metrics.top;
|
||||
// The texture size needs to be at least 1x1.
|
||||
if (width <= 0) width = 1;
|
||||
if (height <= 0) height = 1;
|
||||
return new StringTexture(text, paint, metrics, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas, Bitmap backing) {
|
||||
canvas.translate(0, -mMetrics.ascent);
|
||||
canvas.drawText(mText, 0, 0, mPaint);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
|
||||
// Texture is a rectangular image which can be drawn on GLCanvas.
|
||||
// The isOpaque() function gives a hint about whether the texture is opaque,
|
||||
// so the drawing can be done faster.
|
||||
//
|
||||
// This is the current texture hierarchy:
|
||||
//
|
||||
// Texture
|
||||
// -- ColorTexture
|
||||
// -- FadeInTexture
|
||||
// -- BasicTexture
|
||||
// -- UploadedTexture
|
||||
// -- BitmapTexture
|
||||
// -- Tile
|
||||
// -- ResourceTexture
|
||||
// -- NinePatchTexture
|
||||
// -- CanvasTexture
|
||||
// -- StringTexture
|
||||
//
|
||||
public interface Texture {
|
||||
public int getWidth();
|
||||
public int getHeight();
|
||||
public void draw(GLCanvas canvas, int x, int y);
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h);
|
||||
public boolean isOpaque();
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
|
||||
// Texture is a rectangular image which can be drawn on GLCanvas.
|
||||
// The isOpaque() function gives a hint about whether the texture is opaque,
|
||||
// so the drawing can be done faster.
|
||||
//
|
||||
// This is the current texture hierarchy:
|
||||
//
|
||||
// Texture
|
||||
// -- ColorTexture
|
||||
// -- FadeInTexture
|
||||
// -- BasicTexture
|
||||
// -- UploadedTexture
|
||||
// -- BitmapTexture
|
||||
// -- Tile
|
||||
// -- ResourceTexture
|
||||
// -- NinePatchTexture
|
||||
// -- CanvasTexture
|
||||
// -- StringTexture
|
||||
//
|
||||
public interface Texture {
|
||||
public int getWidth();
|
||||
public int getHeight();
|
||||
public void draw(GLCanvas canvas, int x, int y);
|
||||
public void draw(GLCanvas canvas, int x, int y, int w, int h);
|
||||
public boolean isOpaque();
|
||||
}
|
||||
@@ -1,105 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot;
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot.OnGLIdleListener;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
public class TextureUploader implements OnGLIdleListener {
|
||||
private static final int INIT_CAPACITY = 64;
|
||||
private static final int QUOTA_PER_FRAME = 1;
|
||||
|
||||
private final ArrayDeque<UploadedTexture> mFgTextures =
|
||||
new ArrayDeque<UploadedTexture>(INIT_CAPACITY);
|
||||
private final ArrayDeque<UploadedTexture> mBgTextures =
|
||||
new ArrayDeque<UploadedTexture>(INIT_CAPACITY);
|
||||
private final GLRoot mGLRoot;
|
||||
private volatile boolean mIsQueued = false;
|
||||
|
||||
public TextureUploader(GLRoot root) {
|
||||
mGLRoot = root;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
while (!mFgTextures.isEmpty()) {
|
||||
mFgTextures.pop().setIsUploading(false);
|
||||
}
|
||||
while (!mBgTextures.isEmpty()) {
|
||||
mBgTextures.pop().setIsUploading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// caller should hold synchronized on "this"
|
||||
private void queueSelfIfNeed() {
|
||||
if (mIsQueued) return;
|
||||
mIsQueued = true;
|
||||
mGLRoot.addOnGLIdleListener(this);
|
||||
}
|
||||
|
||||
public synchronized void addBgTexture(UploadedTexture t) {
|
||||
if (t.isContentValid()) return;
|
||||
mBgTextures.addLast(t);
|
||||
t.setIsUploading(true);
|
||||
queueSelfIfNeed();
|
||||
}
|
||||
|
||||
public synchronized void addFgTexture(UploadedTexture t) {
|
||||
if (t.isContentValid()) return;
|
||||
mFgTextures.addLast(t);
|
||||
t.setIsUploading(true);
|
||||
queueSelfIfNeed();
|
||||
}
|
||||
|
||||
private int upload(GLCanvas canvas, ArrayDeque<UploadedTexture> deque,
|
||||
int uploadQuota, boolean isBackground) {
|
||||
while (uploadQuota > 0) {
|
||||
UploadedTexture t;
|
||||
synchronized (this) {
|
||||
if (deque.isEmpty()) break;
|
||||
t = deque.removeFirst();
|
||||
t.setIsUploading(false);
|
||||
if (t.isContentValid()) continue;
|
||||
|
||||
// this has to be protected by the synchronized block
|
||||
// to prevent the inner bitmap get recycled
|
||||
t.updateContent(canvas);
|
||||
}
|
||||
|
||||
// It will took some more time for a texture to be drawn for
|
||||
// the first time.
|
||||
// Thus, when scrolling, if a new column appears on screen,
|
||||
// it may cause a UI jank even these textures are uploaded.
|
||||
if (isBackground) t.draw(canvas, 0, 0);
|
||||
--uploadQuota;
|
||||
}
|
||||
return uploadQuota;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
|
||||
int uploadQuota = QUOTA_PER_FRAME;
|
||||
uploadQuota = upload(canvas, mFgTextures, uploadQuota, false);
|
||||
if (uploadQuota < QUOTA_PER_FRAME) mGLRoot.requestRender();
|
||||
upload(canvas, mBgTextures, uploadQuota, true);
|
||||
synchronized (this) {
|
||||
mIsQueued = !mFgTextures.isEmpty() || !mBgTextures.isEmpty();
|
||||
return mIsQueued;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot;
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot.OnGLIdleListener;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
|
||||
public class TextureUploader implements OnGLIdleListener {
|
||||
private static final int INIT_CAPACITY = 64;
|
||||
private static final int QUOTA_PER_FRAME = 1;
|
||||
|
||||
private final ArrayDeque<UploadedTexture> mFgTextures =
|
||||
new ArrayDeque<UploadedTexture>(INIT_CAPACITY);
|
||||
private final ArrayDeque<UploadedTexture> mBgTextures =
|
||||
new ArrayDeque<UploadedTexture>(INIT_CAPACITY);
|
||||
private final GLRoot mGLRoot;
|
||||
private volatile boolean mIsQueued = false;
|
||||
|
||||
public TextureUploader(GLRoot root) {
|
||||
mGLRoot = root;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
while (!mFgTextures.isEmpty()) {
|
||||
mFgTextures.pop().setIsUploading(false);
|
||||
}
|
||||
while (!mBgTextures.isEmpty()) {
|
||||
mBgTextures.pop().setIsUploading(false);
|
||||
}
|
||||
}
|
||||
|
||||
// caller should hold synchronized on "this"
|
||||
private void queueSelfIfNeed() {
|
||||
if (mIsQueued) return;
|
||||
mIsQueued = true;
|
||||
mGLRoot.addOnGLIdleListener(this);
|
||||
}
|
||||
|
||||
public synchronized void addBgTexture(UploadedTexture t) {
|
||||
if (t.isContentValid()) return;
|
||||
mBgTextures.addLast(t);
|
||||
t.setIsUploading(true);
|
||||
queueSelfIfNeed();
|
||||
}
|
||||
|
||||
public synchronized void addFgTexture(UploadedTexture t) {
|
||||
if (t.isContentValid()) return;
|
||||
mFgTextures.addLast(t);
|
||||
t.setIsUploading(true);
|
||||
queueSelfIfNeed();
|
||||
}
|
||||
|
||||
private int upload(GLCanvas canvas, ArrayDeque<UploadedTexture> deque,
|
||||
int uploadQuota, boolean isBackground) {
|
||||
while (uploadQuota > 0) {
|
||||
UploadedTexture t;
|
||||
synchronized (this) {
|
||||
if (deque.isEmpty()) break;
|
||||
t = deque.removeFirst();
|
||||
t.setIsUploading(false);
|
||||
if (t.isContentValid()) continue;
|
||||
|
||||
// this has to be protected by the synchronized block
|
||||
// to prevent the inner bitmap get recycled
|
||||
t.updateContent(canvas);
|
||||
}
|
||||
|
||||
// It will took some more time for a texture to be drawn for
|
||||
// the first time.
|
||||
// Thus, when scrolling, if a new column appears on screen,
|
||||
// it may cause a UI jank even these textures are uploaded.
|
||||
if (isBackground) t.draw(canvas, 0, 0);
|
||||
--uploadQuota;
|
||||
}
|
||||
return uploadQuota;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
|
||||
int uploadQuota = QUOTA_PER_FRAME;
|
||||
uploadQuota = upload(canvas, mFgTextures, uploadQuota, false);
|
||||
if (uploadQuota < QUOTA_PER_FRAME) mGLRoot.requestRender();
|
||||
upload(canvas, mBgTextures, uploadQuota, true);
|
||||
synchronized (this) {
|
||||
mIsQueued = !mFgTextures.isEmpty() || !mBgTextures.isEmpty();
|
||||
return mIsQueued;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,349 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.RectF;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot;
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot.OnGLIdleListener;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
|
||||
// This class is similar to BitmapTexture, except the bitmap is
|
||||
// split into tiles. By doing so, we may increase the time required to
|
||||
// upload the whole bitmap but we reduce the time of uploading each tile
|
||||
// so it make the animation more smooth and prevents jank.
|
||||
public class TiledTexture implements Texture {
|
||||
private static final int CONTENT_SIZE = 254;
|
||||
private static final int BORDER_SIZE = 1;
|
||||
private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE;
|
||||
private static final int INIT_CAPACITY = 8;
|
||||
|
||||
// We are targeting at 60fps, so we have 16ms for each frame.
|
||||
// In this 16ms, we use about 4~8 ms to upload tiles.
|
||||
private static final long UPLOAD_TILE_LIMIT = 4; // ms
|
||||
|
||||
private static Tile sFreeTileHead = null;
|
||||
private static final Object sFreeTileLock = new Object();
|
||||
|
||||
private static Bitmap sUploadBitmap;
|
||||
private static Canvas sCanvas;
|
||||
private static Paint sBitmapPaint;
|
||||
private static Paint sPaint;
|
||||
|
||||
private int mUploadIndex = 0;
|
||||
|
||||
private final Tile[] mTiles; // Can be modified in different threads.
|
||||
// Should be protected by "synchronized."
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final RectF mSrcRect = new RectF();
|
||||
private final RectF mDestRect = new RectF();
|
||||
|
||||
public static class Uploader implements OnGLIdleListener {
|
||||
private final ArrayDeque<TiledTexture> mTextures =
|
||||
new ArrayDeque<TiledTexture>(INIT_CAPACITY);
|
||||
|
||||
private final GLRoot mGlRoot;
|
||||
private boolean mIsQueued = false;
|
||||
|
||||
public Uploader(GLRoot glRoot) {
|
||||
mGlRoot = glRoot;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
mTextures.clear();
|
||||
}
|
||||
|
||||
public synchronized void addTexture(TiledTexture t) {
|
||||
if (t.isReady()) return;
|
||||
mTextures.addLast(t);
|
||||
|
||||
if (mIsQueued) return;
|
||||
mIsQueued = true;
|
||||
mGlRoot.addOnGLIdleListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
|
||||
ArrayDeque<TiledTexture> deque = mTextures;
|
||||
synchronized (this) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
long dueTime = now + UPLOAD_TILE_LIMIT;
|
||||
while (now < dueTime && !deque.isEmpty()) {
|
||||
TiledTexture t = deque.peekFirst();
|
||||
if (t.uploadNextTile(canvas)) {
|
||||
deque.removeFirst();
|
||||
mGlRoot.requestRender();
|
||||
}
|
||||
now = SystemClock.uptimeMillis();
|
||||
}
|
||||
mIsQueued = !mTextures.isEmpty();
|
||||
|
||||
// return true to keep this listener in the queue
|
||||
return mIsQueued;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Tile extends UploadedTexture {
|
||||
public int offsetX;
|
||||
public int offsetY;
|
||||
public Bitmap bitmap;
|
||||
public Tile nextFreeTile;
|
||||
public int contentWidth;
|
||||
public int contentHeight;
|
||||
|
||||
@Override
|
||||
public void setSize(int width, int height) {
|
||||
contentWidth = width;
|
||||
contentHeight = height;
|
||||
mWidth = width + 2 * BORDER_SIZE;
|
||||
mHeight = height + 2 * BORDER_SIZE;
|
||||
mTextureWidth = TILE_SIZE;
|
||||
mTextureHeight = TILE_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
int x = BORDER_SIZE - offsetX;
|
||||
int y = BORDER_SIZE - offsetY;
|
||||
int r = bitmap.getWidth() + x;
|
||||
int b = bitmap.getHeight() + y;
|
||||
sCanvas.drawBitmap(bitmap, x, y, sBitmapPaint);
|
||||
bitmap = null;
|
||||
|
||||
// draw borders if need
|
||||
if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
|
||||
if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
|
||||
if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
|
||||
if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
|
||||
|
||||
return sUploadBitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private static void freeTile(Tile tile) {
|
||||
tile.invalidateContent();
|
||||
tile.bitmap = null;
|
||||
synchronized (sFreeTileLock) {
|
||||
tile.nextFreeTile = sFreeTileHead;
|
||||
sFreeTileHead = tile;
|
||||
}
|
||||
}
|
||||
|
||||
private static Tile obtainTile() {
|
||||
synchronized (sFreeTileLock) {
|
||||
Tile result = sFreeTileHead;
|
||||
if (result == null) return new Tile();
|
||||
sFreeTileHead = result.nextFreeTile;
|
||||
result.nextFreeTile = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean uploadNextTile(GLCanvas canvas) {
|
||||
if (mUploadIndex == mTiles.length) return true;
|
||||
|
||||
synchronized (mTiles) {
|
||||
Tile next = mTiles[mUploadIndex++];
|
||||
|
||||
// Make sure tile has not already been recycled by the time
|
||||
// this is called (race condition in onGLIdle)
|
||||
if (next.bitmap != null) {
|
||||
boolean hasBeenLoad = next.isLoaded();
|
||||
next.updateContent(canvas);
|
||||
|
||||
// It will take some time for a texture to be drawn for the first
|
||||
// time. When scrolling, we need to draw several tiles on the screen
|
||||
// at the same time. It may cause a UI jank even these textures has
|
||||
// been uploaded.
|
||||
if (!hasBeenLoad) next.draw(canvas, 0, 0);
|
||||
}
|
||||
}
|
||||
return mUploadIndex == mTiles.length;
|
||||
}
|
||||
|
||||
public TiledTexture(Bitmap bitmap) {
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
ArrayList<Tile> list = new ArrayList<Tile>();
|
||||
|
||||
for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) {
|
||||
for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) {
|
||||
Tile tile = obtainTile();
|
||||
tile.offsetX = x;
|
||||
tile.offsetY = y;
|
||||
tile.bitmap = bitmap;
|
||||
tile.setSize(
|
||||
Math.min(CONTENT_SIZE, mWidth - x),
|
||||
Math.min(CONTENT_SIZE, mHeight - y));
|
||||
list.add(tile);
|
||||
}
|
||||
}
|
||||
mTiles = list.toArray(new Tile[list.size()]);
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return mUploadIndex == mTiles.length;
|
||||
}
|
||||
|
||||
// Can be called in UI thread.
|
||||
public void recycle() {
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
freeTile(mTiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void freeResources() {
|
||||
sUploadBitmap = null;
|
||||
sCanvas = null;
|
||||
sBitmapPaint = null;
|
||||
sPaint = null;
|
||||
}
|
||||
|
||||
public static void prepareResources() {
|
||||
sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888);
|
||||
sCanvas = new Canvas(sUploadBitmap);
|
||||
sBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
|
||||
sBitmapPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
|
||||
sPaint = new Paint();
|
||||
sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
|
||||
sPaint.setColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
// We want to draw the "source" on the "target".
|
||||
// This method is to find the "output" rectangle which is
|
||||
// the corresponding area of the "src".
|
||||
// (x,y) target
|
||||
// (x0,y0) source +---------------+
|
||||
// +----------+ | |
|
||||
// | src | | output |
|
||||
// | +--+ | linear map | +----+ |
|
||||
// | +--+ | ----------> | | | |
|
||||
// | | by (scaleX, scaleY) | +----+ |
|
||||
// +----------+ | |
|
||||
// Texture +---------------+
|
||||
// Canvas
|
||||
private static void mapRect(RectF output,
|
||||
RectF src, float x0, float y0, float x, float y, float scaleX,
|
||||
float scaleY) {
|
||||
output.set(x + (src.left - x0) * scaleX,
|
||||
y + (src.top - y0) * scaleY,
|
||||
x + (src.right - x0) * scaleX,
|
||||
y + (src.bottom - y0) * scaleY);
|
||||
}
|
||||
|
||||
// Draws a mixed color of this texture and a specified color onto the
|
||||
// a rectangle. The used color is: from * (1 - ratio) + to * ratio.
|
||||
public void drawMixed(GLCanvas canvas, int color, float ratio,
|
||||
int x, int y, int width, int height) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float scaleX = (float) width / mWidth;
|
||||
float scaleY = (float) height / mHeight;
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
Tile t = mTiles[i];
|
||||
src.set(0, 0, t.contentWidth, t.contentHeight);
|
||||
src.offset(t.offsetX, t.offsetY);
|
||||
mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
|
||||
src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
|
||||
canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draws the texture on to the specified rectangle.
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int width, int height) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float scaleX = (float) width / mWidth;
|
||||
float scaleY = (float) height / mHeight;
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
Tile t = mTiles[i];
|
||||
src.set(0, 0, t.contentWidth, t.contentHeight);
|
||||
src.offset(t.offsetX, t.offsetY);
|
||||
mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
|
||||
src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
|
||||
canvas.drawTexture(t, mSrcRect, mDestRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a sub region of this texture on to the specified rectangle.
|
||||
public void draw(GLCanvas canvas, RectF source, RectF target) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float x0 = source.left;
|
||||
float y0 = source.top;
|
||||
float x = target.left;
|
||||
float y = target.top;
|
||||
float scaleX = target.width() / source.width();
|
||||
float scaleY = target.height() / source.height();
|
||||
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
Tile t = mTiles[i];
|
||||
src.set(0, 0, t.contentWidth, t.contentHeight);
|
||||
src.offset(t.offsetX, t.offsetY);
|
||||
if (!src.intersect(source)) continue;
|
||||
mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
|
||||
src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
|
||||
canvas.drawTexture(t, src, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
draw(canvas, x, y, mWidth, mHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.glrenderer;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.RectF;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot;
|
||||
import com.hippo.ehviewer.gallery.ui.GLRoot.OnGLIdleListener;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
|
||||
// This class is similar to BitmapTexture, except the bitmap is
|
||||
// split into tiles. By doing so, we may increase the time required to
|
||||
// upload the whole bitmap but we reduce the time of uploading each tile
|
||||
// so it make the animation more smooth and prevents jank.
|
||||
public class TiledTexture implements Texture {
|
||||
private static final int CONTENT_SIZE = 254;
|
||||
private static final int BORDER_SIZE = 1;
|
||||
private static final int TILE_SIZE = CONTENT_SIZE + 2 * BORDER_SIZE;
|
||||
private static final int INIT_CAPACITY = 8;
|
||||
|
||||
// We are targeting at 60fps, so we have 16ms for each frame.
|
||||
// In this 16ms, we use about 4~8 ms to upload tiles.
|
||||
private static final long UPLOAD_TILE_LIMIT = 4; // ms
|
||||
|
||||
private static Tile sFreeTileHead = null;
|
||||
private static final Object sFreeTileLock = new Object();
|
||||
|
||||
private static Bitmap sUploadBitmap;
|
||||
private static Canvas sCanvas;
|
||||
private static Paint sBitmapPaint;
|
||||
private static Paint sPaint;
|
||||
|
||||
private int mUploadIndex = 0;
|
||||
|
||||
private final Tile[] mTiles; // Can be modified in different threads.
|
||||
// Should be protected by "synchronized."
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final RectF mSrcRect = new RectF();
|
||||
private final RectF mDestRect = new RectF();
|
||||
|
||||
public static class Uploader implements OnGLIdleListener {
|
||||
private final ArrayDeque<TiledTexture> mTextures =
|
||||
new ArrayDeque<TiledTexture>(INIT_CAPACITY);
|
||||
|
||||
private final GLRoot mGlRoot;
|
||||
private boolean mIsQueued = false;
|
||||
|
||||
public Uploader(GLRoot glRoot) {
|
||||
mGlRoot = glRoot;
|
||||
}
|
||||
|
||||
public synchronized void clear() {
|
||||
mTextures.clear();
|
||||
}
|
||||
|
||||
public synchronized void addTexture(TiledTexture t) {
|
||||
if (t.isReady()) return;
|
||||
mTextures.addLast(t);
|
||||
|
||||
if (mIsQueued) return;
|
||||
mIsQueued = true;
|
||||
mGlRoot.addOnGLIdleListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGLIdle(GLCanvas canvas, boolean renderRequested) {
|
||||
ArrayDeque<TiledTexture> deque = mTextures;
|
||||
synchronized (this) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
long dueTime = now + UPLOAD_TILE_LIMIT;
|
||||
while (now < dueTime && !deque.isEmpty()) {
|
||||
TiledTexture t = deque.peekFirst();
|
||||
if (t.uploadNextTile(canvas)) {
|
||||
deque.removeFirst();
|
||||
mGlRoot.requestRender();
|
||||
}
|
||||
now = SystemClock.uptimeMillis();
|
||||
}
|
||||
mIsQueued = !mTextures.isEmpty();
|
||||
|
||||
// return true to keep this listener in the queue
|
||||
return mIsQueued;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Tile extends UploadedTexture {
|
||||
public int offsetX;
|
||||
public int offsetY;
|
||||
public Bitmap bitmap;
|
||||
public Tile nextFreeTile;
|
||||
public int contentWidth;
|
||||
public int contentHeight;
|
||||
|
||||
@Override
|
||||
public void setSize(int width, int height) {
|
||||
contentWidth = width;
|
||||
contentHeight = height;
|
||||
mWidth = width + 2 * BORDER_SIZE;
|
||||
mHeight = height + 2 * BORDER_SIZE;
|
||||
mTextureWidth = TILE_SIZE;
|
||||
mTextureHeight = TILE_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap onGetBitmap() {
|
||||
int x = BORDER_SIZE - offsetX;
|
||||
int y = BORDER_SIZE - offsetY;
|
||||
int r = bitmap.getWidth() + x;
|
||||
int b = bitmap.getHeight() + y;
|
||||
sCanvas.drawBitmap(bitmap, x, y, sBitmapPaint);
|
||||
bitmap = null;
|
||||
|
||||
// draw borders if need
|
||||
if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
|
||||
if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
|
||||
if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
|
||||
if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
|
||||
|
||||
return sUploadBitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFreeBitmap(Bitmap bitmap) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
private static void freeTile(Tile tile) {
|
||||
tile.invalidateContent();
|
||||
tile.bitmap = null;
|
||||
synchronized (sFreeTileLock) {
|
||||
tile.nextFreeTile = sFreeTileHead;
|
||||
sFreeTileHead = tile;
|
||||
}
|
||||
}
|
||||
|
||||
private static Tile obtainTile() {
|
||||
synchronized (sFreeTileLock) {
|
||||
Tile result = sFreeTileHead;
|
||||
if (result == null) return new Tile();
|
||||
sFreeTileHead = result.nextFreeTile;
|
||||
result.nextFreeTile = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean uploadNextTile(GLCanvas canvas) {
|
||||
if (mUploadIndex == mTiles.length) return true;
|
||||
|
||||
synchronized (mTiles) {
|
||||
Tile next = mTiles[mUploadIndex++];
|
||||
|
||||
// Make sure tile has not already been recycled by the time
|
||||
// this is called (race condition in onGLIdle)
|
||||
if (next.bitmap != null) {
|
||||
boolean hasBeenLoad = next.isLoaded();
|
||||
next.updateContent(canvas);
|
||||
|
||||
// It will take some time for a texture to be drawn for the first
|
||||
// time. When scrolling, we need to draw several tiles on the screen
|
||||
// at the same time. It may cause a UI jank even these textures has
|
||||
// been uploaded.
|
||||
if (!hasBeenLoad) next.draw(canvas, 0, 0);
|
||||
}
|
||||
}
|
||||
return mUploadIndex == mTiles.length;
|
||||
}
|
||||
|
||||
public TiledTexture(Bitmap bitmap) {
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
ArrayList<Tile> list = new ArrayList<Tile>();
|
||||
|
||||
for (int x = 0, w = mWidth; x < w; x += CONTENT_SIZE) {
|
||||
for (int y = 0, h = mHeight; y < h; y += CONTENT_SIZE) {
|
||||
Tile tile = obtainTile();
|
||||
tile.offsetX = x;
|
||||
tile.offsetY = y;
|
||||
tile.bitmap = bitmap;
|
||||
tile.setSize(
|
||||
Math.min(CONTENT_SIZE, mWidth - x),
|
||||
Math.min(CONTENT_SIZE, mHeight - y));
|
||||
list.add(tile);
|
||||
}
|
||||
}
|
||||
mTiles = list.toArray(new Tile[list.size()]);
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return mUploadIndex == mTiles.length;
|
||||
}
|
||||
|
||||
// Can be called in UI thread.
|
||||
public void recycle() {
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
freeTile(mTiles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void freeResources() {
|
||||
sUploadBitmap = null;
|
||||
sCanvas = null;
|
||||
sBitmapPaint = null;
|
||||
sPaint = null;
|
||||
}
|
||||
|
||||
public static void prepareResources() {
|
||||
sUploadBitmap = Bitmap.createBitmap(TILE_SIZE, TILE_SIZE, Config.ARGB_8888);
|
||||
sCanvas = new Canvas(sUploadBitmap);
|
||||
sBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
|
||||
sBitmapPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
|
||||
sPaint = new Paint();
|
||||
sPaint.setXfermode(new PorterDuffXfermode(Mode.SRC));
|
||||
sPaint.setColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
// We want to draw the "source" on the "target".
|
||||
// This method is to find the "output" rectangle which is
|
||||
// the corresponding area of the "src".
|
||||
// (x,y) target
|
||||
// (x0,y0) source +---------------+
|
||||
// +----------+ | |
|
||||
// | src | | output |
|
||||
// | +--+ | linear map | +----+ |
|
||||
// | +--+ | ----------> | | | |
|
||||
// | | by (scaleX, scaleY) | +----+ |
|
||||
// +----------+ | |
|
||||
// Texture +---------------+
|
||||
// Canvas
|
||||
private static void mapRect(RectF output,
|
||||
RectF src, float x0, float y0, float x, float y, float scaleX,
|
||||
float scaleY) {
|
||||
output.set(x + (src.left - x0) * scaleX,
|
||||
y + (src.top - y0) * scaleY,
|
||||
x + (src.right - x0) * scaleX,
|
||||
y + (src.bottom - y0) * scaleY);
|
||||
}
|
||||
|
||||
// Draws a mixed color of this texture and a specified color onto the
|
||||
// a rectangle. The used color is: from * (1 - ratio) + to * ratio.
|
||||
public void drawMixed(GLCanvas canvas, int color, float ratio,
|
||||
int x, int y, int width, int height) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float scaleX = (float) width / mWidth;
|
||||
float scaleY = (float) height / mHeight;
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
Tile t = mTiles[i];
|
||||
src.set(0, 0, t.contentWidth, t.contentHeight);
|
||||
src.offset(t.offsetX, t.offsetY);
|
||||
mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
|
||||
src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
|
||||
canvas.drawMixed(t, color, ratio, mSrcRect, mDestRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draws the texture on to the specified rectangle.
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y, int width, int height) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float scaleX = (float) width / mWidth;
|
||||
float scaleY = (float) height / mHeight;
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
Tile t = mTiles[i];
|
||||
src.set(0, 0, t.contentWidth, t.contentHeight);
|
||||
src.offset(t.offsetX, t.offsetY);
|
||||
mapRect(dest, src, 0, 0, x, y, scaleX, scaleY);
|
||||
src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
|
||||
canvas.drawTexture(t, mSrcRect, mDestRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a sub region of this texture on to the specified rectangle.
|
||||
public void draw(GLCanvas canvas, RectF source, RectF target) {
|
||||
RectF src = mSrcRect;
|
||||
RectF dest = mDestRect;
|
||||
float x0 = source.left;
|
||||
float y0 = source.top;
|
||||
float x = target.left;
|
||||
float y = target.top;
|
||||
float scaleX = target.width() / source.width();
|
||||
float scaleY = target.height() / source.height();
|
||||
|
||||
synchronized (mTiles) {
|
||||
for (int i = 0, n = mTiles.length; i < n; ++i) {
|
||||
Tile t = mTiles[i];
|
||||
src.set(0, 0, t.contentWidth, t.contentHeight);
|
||||
src.offset(t.offsetX, t.offsetY);
|
||||
if (!src.intersect(source)) continue;
|
||||
mapRect(dest, src, x0, y0, x, y, scaleX, scaleY);
|
||||
src.offset(BORDER_SIZE - t.offsetX, BORDER_SIZE - t.offsetY);
|
||||
canvas.drawTexture(t, src, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GLCanvas canvas, int x, int y) {
|
||||
draw(canvas, x, y, mWidth, mHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaque() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,45 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
//
|
||||
// The animation time should ideally be the vsync time the frame will be
|
||||
// displayed, but that is an unknown time in the future. So we use the system
|
||||
// time just after eglSwapBuffers (when GLSurfaceView.onDrawFrame is called)
|
||||
// as a approximation.
|
||||
//
|
||||
public class AnimationTime {
|
||||
private static volatile long sTime;
|
||||
|
||||
// Sets current time as the animation time.
|
||||
public static void update() {
|
||||
sTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
// Returns the animation time.
|
||||
public static long get() {
|
||||
return sTime;
|
||||
}
|
||||
|
||||
public static long startTime() {
|
||||
sTime = SystemClock.uptimeMillis();
|
||||
return sTime;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
//
|
||||
// The animation time should ideally be the vsync time the frame will be
|
||||
// displayed, but that is an unknown time in the future. So we use the system
|
||||
// time just after eglSwapBuffers (when GLSurfaceView.onDrawFrame is called)
|
||||
// as a approximation.
|
||||
//
|
||||
public class AnimationTime {
|
||||
private static volatile long sTime;
|
||||
|
||||
// Sets current time as the animation time.
|
||||
public static void update() {
|
||||
sTime = SystemClock.uptimeMillis();
|
||||
}
|
||||
|
||||
// Returns the animation time.
|
||||
public static long get() {
|
||||
return sTime;
|
||||
}
|
||||
|
||||
public static long startTime() {
|
||||
sTime = SystemClock.uptimeMillis();
|
||||
return sTime;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class DownUpDetector {
|
||||
public interface DownUpListener {
|
||||
void onDown(MotionEvent e);
|
||||
void onUp(MotionEvent e);
|
||||
}
|
||||
|
||||
private boolean mStillDown;
|
||||
private DownUpListener mListener;
|
||||
|
||||
public DownUpDetector(DownUpListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private void setState(boolean down, MotionEvent e) {
|
||||
if (down == mStillDown) return;
|
||||
mStillDown = down;
|
||||
if (down) {
|
||||
mListener.onDown(e);
|
||||
} else {
|
||||
mListener.onUp(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onTouchEvent(MotionEvent ev) {
|
||||
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
setState(true, ev);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_POINTER_DOWN: // Multitouch event - abort.
|
||||
setState(false, ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDown() {
|
||||
return mStillDown;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class DownUpDetector {
|
||||
public interface DownUpListener {
|
||||
void onDown(MotionEvent e);
|
||||
void onUp(MotionEvent e);
|
||||
}
|
||||
|
||||
private boolean mStillDown;
|
||||
private DownUpListener mListener;
|
||||
|
||||
public DownUpDetector(DownUpListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private void setState(boolean down, MotionEvent e) {
|
||||
if (down == mStillDown) return;
|
||||
mStillDown = down;
|
||||
if (down) {
|
||||
mListener.onDown(e);
|
||||
} else {
|
||||
mListener.onUp(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onTouchEvent(MotionEvent ev) {
|
||||
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
setState(true, ev);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
case MotionEvent.ACTION_POINTER_DOWN: // Multitouch event - abort.
|
||||
setState(false, ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDown() {
|
||||
return mStillDown;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
|
||||
import com.hippo.ehviewer.gallery.anim.CanvasAnimation;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
public interface GLRoot {
|
||||
|
||||
// Listener will be called when GL is idle AND before each frame.
|
||||
// Mainly used for uploading textures.
|
||||
public static interface OnGLIdleListener {
|
||||
public boolean onGLIdle(
|
||||
GLCanvas canvas, boolean renderRequested);
|
||||
}
|
||||
|
||||
public void addOnGLIdleListener(OnGLIdleListener listener);
|
||||
public void registerLaunchedAnimation(CanvasAnimation animation);
|
||||
public void requestRenderForced();
|
||||
public void requestRender();
|
||||
public void requestLayoutContentPane();
|
||||
|
||||
public void lockRenderThread();
|
||||
public void unlockRenderThread();
|
||||
|
||||
public void setContentPane(GLView content);
|
||||
public void setOrientationSource(OrientationSource source);
|
||||
public int getDisplayRotation();
|
||||
public int getCompensation();
|
||||
public Matrix getCompensationMatrix();
|
||||
public void freeze();
|
||||
public void unfreeze();
|
||||
public void setLightsOutMode(boolean enabled);
|
||||
|
||||
public Context getContext();
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
|
||||
import com.hippo.ehviewer.gallery.anim.CanvasAnimation;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
public interface GLRoot {
|
||||
|
||||
// Listener will be called when GL is idle AND before each frame.
|
||||
// Mainly used for uploading textures.
|
||||
public static interface OnGLIdleListener {
|
||||
public boolean onGLIdle(
|
||||
GLCanvas canvas, boolean renderRequested);
|
||||
}
|
||||
|
||||
public void addOnGLIdleListener(OnGLIdleListener listener);
|
||||
public void registerLaunchedAnimation(CanvasAnimation animation);
|
||||
public void requestRenderForced();
|
||||
public void requestRender();
|
||||
public void requestLayoutContentPane();
|
||||
|
||||
public void lockRenderThread();
|
||||
public void unlockRenderThread();
|
||||
|
||||
public void setContentPane(GLView content);
|
||||
public void setOrientationSource(OrientationSource source);
|
||||
public int getDisplayRotation();
|
||||
public int getCompensation();
|
||||
public Matrix getCompensationMatrix();
|
||||
public void freeze();
|
||||
public void unfreeze();
|
||||
public void setLightsOutMode(boolean enabled);
|
||||
|
||||
public Context getContext();
|
||||
}
|
||||
@@ -1,447 +1,447 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.hippo.ehviewer.gallery.anim.CanvasAnimation;
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
// GLView is a UI component. It can render to a GLCanvas and accept touch
|
||||
// events. A GLView may have zero or more child GLView and they form a tree
|
||||
// structure. The rendering and event handling will pass through the tree
|
||||
// structure.
|
||||
//
|
||||
// A GLView tree should be attached to a GLRoot before event dispatching and
|
||||
// rendering happens. GLView asks GLRoot to re-render or re-layout the
|
||||
// GLView hierarchy using requestRender() and requestLayoutContentPane().
|
||||
//
|
||||
// The render() method is called in a separate thread. Before calling
|
||||
// dispatchTouchEvent() and layout(), GLRoot acquires a lock to avoid the
|
||||
// rendering thread running at the same time. If there are other entry points
|
||||
// from main thread (like a Handler) in your GLView, you need to call
|
||||
// lockRendering() if the rendering thread should not run at the same time.
|
||||
//
|
||||
public class GLView {
|
||||
private static final String TAG = "GLView";
|
||||
|
||||
public static final int VISIBLE = 0;
|
||||
public static final int INVISIBLE = 1;
|
||||
|
||||
private static final int FLAG_INVISIBLE = 1;
|
||||
private static final int FLAG_SET_MEASURED_SIZE = 2;
|
||||
private static final int FLAG_LAYOUT_REQUESTED = 4;
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick(GLView v);
|
||||
}
|
||||
|
||||
protected final Rect mBounds = new Rect();
|
||||
protected final Rect mPaddings = new Rect();
|
||||
|
||||
private GLRoot mRoot;
|
||||
protected GLView mParent;
|
||||
private ArrayList<GLView> mComponents;
|
||||
private GLView mMotionTarget;
|
||||
|
||||
private CanvasAnimation mAnimation;
|
||||
|
||||
private int mViewFlags = 0;
|
||||
|
||||
protected int mMeasuredWidth = 0;
|
||||
protected int mMeasuredHeight = 0;
|
||||
|
||||
private int mLastWidthSpec = -1;
|
||||
private int mLastHeightSpec = -1;
|
||||
|
||||
protected int mScrollY = 0;
|
||||
protected int mScrollX = 0;
|
||||
protected int mScrollHeight = 0;
|
||||
protected int mScrollWidth = 0;
|
||||
|
||||
private float [] mBackgroundColor;
|
||||
|
||||
public void startAnimation(CanvasAnimation animation) {
|
||||
GLRoot root = getGLRoot();
|
||||
if (root == null) throw new IllegalStateException();
|
||||
mAnimation = animation;
|
||||
if (mAnimation != null) {
|
||||
mAnimation.start();
|
||||
root.registerLaunchedAnimation(mAnimation);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
// Sets the visiblity of this GLView (either GLView.VISIBLE or
|
||||
// GLView.INVISIBLE).
|
||||
public void setVisibility(int visibility) {
|
||||
if (visibility == getVisibility()) return;
|
||||
if (visibility == VISIBLE) {
|
||||
mViewFlags &= ~FLAG_INVISIBLE;
|
||||
} else {
|
||||
mViewFlags |= FLAG_INVISIBLE;
|
||||
}
|
||||
onVisibilityChanged(visibility);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
// Returns GLView.VISIBLE or GLView.INVISIBLE
|
||||
public int getVisibility() {
|
||||
return (mViewFlags & FLAG_INVISIBLE) == 0 ? VISIBLE : INVISIBLE;
|
||||
}
|
||||
|
||||
// This should only be called on the content pane (the topmost GLView).
|
||||
public void attachToRoot(GLRoot root) {
|
||||
Utils.assertTrue(mParent == null && mRoot == null);
|
||||
onAttachToRoot(root);
|
||||
}
|
||||
|
||||
// This should only be called on the content pane (the topmost GLView).
|
||||
public void detachFromRoot() {
|
||||
Utils.assertTrue(mParent == null && mRoot != null);
|
||||
onDetachFromRoot();
|
||||
}
|
||||
|
||||
// Returns the number of children of the GLView.
|
||||
public int getComponentCount() {
|
||||
return mComponents == null ? 0 : mComponents.size();
|
||||
}
|
||||
|
||||
// Returns the children for the given index.
|
||||
public GLView getComponent(int index) {
|
||||
if (mComponents == null) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
return mComponents.get(index);
|
||||
}
|
||||
|
||||
// Adds a child to this GLView.
|
||||
public void addComponent(GLView component) {
|
||||
// Make sure the component doesn't have a parent currently.
|
||||
if (component.mParent != null) throw new IllegalStateException();
|
||||
|
||||
// Build parent-child links
|
||||
if (mComponents == null) {
|
||||
mComponents = new ArrayList<GLView>();
|
||||
}
|
||||
mComponents.add(component);
|
||||
component.mParent = this;
|
||||
|
||||
// If this is added after we have a root, tell the component.
|
||||
if (mRoot != null) {
|
||||
component.onAttachToRoot(mRoot);
|
||||
}
|
||||
}
|
||||
|
||||
// Removes a child from this GLView.
|
||||
public boolean removeComponent(GLView component) {
|
||||
if (mComponents == null) return false;
|
||||
if (mComponents.remove(component)) {
|
||||
removeOneComponent(component);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removes all children of this GLView.
|
||||
public void removeAllComponents() {
|
||||
for (int i = 0, n = mComponents.size(); i < n; ++i) {
|
||||
removeOneComponent(mComponents.get(i));
|
||||
}
|
||||
mComponents.clear();
|
||||
}
|
||||
|
||||
private void removeOneComponent(GLView component) {
|
||||
if (mMotionTarget == component) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
MotionEvent cancelEvent = MotionEvent.obtain(
|
||||
now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
|
||||
dispatchTouchEvent(cancelEvent);
|
||||
cancelEvent.recycle();
|
||||
}
|
||||
component.onDetachFromRoot();
|
||||
component.mParent = null;
|
||||
}
|
||||
|
||||
public Rect bounds() {
|
||||
return mBounds;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mBounds.right - mBounds.left;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mBounds.bottom - mBounds.top;
|
||||
}
|
||||
|
||||
public GLRoot getGLRoot() {
|
||||
return mRoot;
|
||||
}
|
||||
|
||||
// Request re-rendering of the view hierarchy.
|
||||
// This is used for animation or when the contents changed.
|
||||
public void invalidate() {
|
||||
GLRoot root = getGLRoot();
|
||||
if (root != null) root.requestRender();
|
||||
}
|
||||
|
||||
// Request re-layout of the view hierarchy.
|
||||
public void requestLayout() {
|
||||
mViewFlags |= FLAG_LAYOUT_REQUESTED;
|
||||
mLastHeightSpec = -1;
|
||||
mLastWidthSpec = -1;
|
||||
if (mParent != null) {
|
||||
mParent.requestLayout();
|
||||
} else {
|
||||
// Is this a content pane ?
|
||||
GLRoot root = getGLRoot();
|
||||
if (root != null) root.requestLayoutContentPane();
|
||||
}
|
||||
}
|
||||
|
||||
protected void render(GLCanvas canvas) {
|
||||
renderBackground(canvas);
|
||||
canvas.save();
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
renderChild(canvas, getComponent(i));
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
public float [] getBackgroundColor() {
|
||||
return mBackgroundColor;
|
||||
}
|
||||
|
||||
public void setBackgroundColor(float [] color) {
|
||||
mBackgroundColor = color;
|
||||
}
|
||||
|
||||
protected void renderBackground(GLCanvas view) {
|
||||
if (mBackgroundColor != null) {
|
||||
view.clearBuffer(mBackgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderChild(GLCanvas canvas, GLView component) {
|
||||
if (component.getVisibility() != GLView.VISIBLE
|
||||
&& component.mAnimation == null) return;
|
||||
|
||||
int xoffset = component.mBounds.left - mScrollX;
|
||||
int yoffset = component.mBounds.top - mScrollY;
|
||||
|
||||
canvas.translate(xoffset, yoffset);
|
||||
|
||||
CanvasAnimation anim = component.mAnimation;
|
||||
if (anim != null) {
|
||||
canvas.save(anim.getCanvasSaveFlags());
|
||||
if (anim.calculate(AnimationTime.get())) {
|
||||
invalidate();
|
||||
} else {
|
||||
component.mAnimation = null;
|
||||
}
|
||||
anim.apply(canvas);
|
||||
}
|
||||
component.render(canvas);
|
||||
if (anim != null) canvas.restore();
|
||||
canvas.translate(-xoffset, -yoffset);
|
||||
}
|
||||
|
||||
protected boolean onTouch(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean dispatchTouchEvent(MotionEvent event,
|
||||
int x, int y, GLView component, boolean checkBounds) {
|
||||
Rect rect = component.mBounds;
|
||||
int left = rect.left;
|
||||
int top = rect.top;
|
||||
if (!checkBounds || rect.contains(x, y)) {
|
||||
event.offsetLocation(-left, -top);
|
||||
if (component.dispatchTouchEvent(event)) {
|
||||
event.offsetLocation(left, top);
|
||||
return true;
|
||||
}
|
||||
event.offsetLocation(left, top);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean dispatchTouchEvent(MotionEvent event) {
|
||||
int x = (int) event.getX();
|
||||
int y = (int) event.getY();
|
||||
int action = event.getAction();
|
||||
if (mMotionTarget != null) {
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
MotionEvent cancel = MotionEvent.obtain(event);
|
||||
cancel.setAction(MotionEvent.ACTION_CANCEL);
|
||||
dispatchTouchEvent(cancel, x, y, mMotionTarget, false);
|
||||
mMotionTarget = null;
|
||||
} else {
|
||||
dispatchTouchEvent(event, x, y, mMotionTarget, false);
|
||||
if (action == MotionEvent.ACTION_CANCEL
|
||||
|| action == MotionEvent.ACTION_UP) {
|
||||
mMotionTarget = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
// in the reverse rendering order
|
||||
for (int i = getComponentCount() - 1; i >= 0; --i) {
|
||||
GLView component = getComponent(i);
|
||||
if (component.getVisibility() != GLView.VISIBLE) continue;
|
||||
if (dispatchTouchEvent(event, x, y, component, true)) {
|
||||
mMotionTarget = component;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return onTouch(event);
|
||||
}
|
||||
|
||||
public Rect getPaddings() {
|
||||
return mPaddings;
|
||||
}
|
||||
|
||||
@SuppressLint("WrongCall")
|
||||
public void layout(int left, int top, int right, int bottom) {
|
||||
boolean sizeChanged = setBounds(left, top, right, bottom);
|
||||
mViewFlags &= ~FLAG_LAYOUT_REQUESTED;
|
||||
// We call onLayout no matter sizeChanged is true or not because the
|
||||
// orientation may change without changing the size of the View (for
|
||||
// example, rotate the device by 180 degrees), and we want to handle
|
||||
// orientation change in onLayout.
|
||||
onLayout(sizeChanged, left, top, right, bottom);
|
||||
}
|
||||
|
||||
private boolean setBounds(int left, int top, int right, int bottom) {
|
||||
boolean sizeChanged = (right - left) != (mBounds.right - mBounds.left)
|
||||
|| (bottom - top) != (mBounds.bottom - mBounds.top);
|
||||
mBounds.set(left, top, right, bottom);
|
||||
return sizeChanged;
|
||||
}
|
||||
|
||||
@SuppressLint("WrongCall")
|
||||
public void measure(int widthSpec, int heightSpec) {
|
||||
if (widthSpec == mLastWidthSpec && heightSpec == mLastHeightSpec
|
||||
&& (mViewFlags & FLAG_LAYOUT_REQUESTED) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLastWidthSpec = widthSpec;
|
||||
mLastHeightSpec = heightSpec;
|
||||
|
||||
mViewFlags &= ~FLAG_SET_MEASURED_SIZE;
|
||||
onMeasure(widthSpec, heightSpec);
|
||||
if ((mViewFlags & FLAG_SET_MEASURED_SIZE) == 0) {
|
||||
throw new IllegalStateException(getClass().getName()
|
||||
+ " should call setMeasuredSize() in onMeasure()");
|
||||
}
|
||||
}
|
||||
|
||||
protected void onMeasure(int widthSpec, int heightSpec) {
|
||||
}
|
||||
|
||||
protected void setMeasuredSize(int width, int height) {
|
||||
mViewFlags |= FLAG_SET_MEASURED_SIZE;
|
||||
mMeasuredWidth = width;
|
||||
mMeasuredHeight = height;
|
||||
}
|
||||
|
||||
public int getMeasuredWidth() {
|
||||
return mMeasuredWidth;
|
||||
}
|
||||
|
||||
public int getMeasuredHeight() {
|
||||
return mMeasuredHeight;
|
||||
}
|
||||
|
||||
protected void onLayout(
|
||||
boolean changeSize, int left, int top, int right, int bottom) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bounds of the given descendant that relative to this view.
|
||||
*/
|
||||
public boolean getBoundsOf(GLView descendant, Rect out) {
|
||||
int xoffset = 0;
|
||||
int yoffset = 0;
|
||||
GLView view = descendant;
|
||||
while (view != this) {
|
||||
if (view == null) return false;
|
||||
Rect bounds = view.mBounds;
|
||||
xoffset += bounds.left;
|
||||
yoffset += bounds.top;
|
||||
view = view.mParent;
|
||||
}
|
||||
out.set(xoffset, yoffset, xoffset + descendant.getWidth(),
|
||||
yoffset + descendant.getHeight());
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void onVisibilityChanged(int visibility) {
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
GLView child = getComponent(i);
|
||||
if (child.getVisibility() == GLView.VISIBLE) {
|
||||
child.onVisibilityChanged(visibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onAttachToRoot(GLRoot root) {
|
||||
mRoot = root;
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
getComponent(i).onAttachToRoot(root);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onDetachFromRoot() {
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
getComponent(i).onDetachFromRoot();
|
||||
}
|
||||
mRoot = null;
|
||||
}
|
||||
|
||||
public void lockRendering() {
|
||||
if (mRoot != null) {
|
||||
mRoot.lockRenderThread();
|
||||
}
|
||||
}
|
||||
|
||||
public void unlockRendering() {
|
||||
if (mRoot != null) {
|
||||
mRoot.unlockRenderThread();
|
||||
}
|
||||
}
|
||||
|
||||
// This is for debugging only.
|
||||
// Dump the view hierarchy into log.
|
||||
void dumpTree(String prefix) {
|
||||
Log.d(TAG, prefix + getClass().getSimpleName());
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
getComponent(i).dumpTree(prefix + "....");
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.hippo.ehviewer.gallery.anim.CanvasAnimation;
|
||||
import com.hippo.ehviewer.gallery.common.Utils;
|
||||
import com.hippo.ehviewer.gallery.glrenderer.GLCanvas;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
// GLView is a UI component. It can render to a GLCanvas and accept touch
|
||||
// events. A GLView may have zero or more child GLView and they form a tree
|
||||
// structure. The rendering and event handling will pass through the tree
|
||||
// structure.
|
||||
//
|
||||
// A GLView tree should be attached to a GLRoot before event dispatching and
|
||||
// rendering happens. GLView asks GLRoot to re-render or re-layout the
|
||||
// GLView hierarchy using requestRender() and requestLayoutContentPane().
|
||||
//
|
||||
// The render() method is called in a separate thread. Before calling
|
||||
// dispatchTouchEvent() and layout(), GLRoot acquires a lock to avoid the
|
||||
// rendering thread running at the same time. If there are other entry points
|
||||
// from main thread (like a Handler) in your GLView, you need to call
|
||||
// lockRendering() if the rendering thread should not run at the same time.
|
||||
//
|
||||
public class GLView {
|
||||
private static final String TAG = "GLView";
|
||||
|
||||
public static final int VISIBLE = 0;
|
||||
public static final int INVISIBLE = 1;
|
||||
|
||||
private static final int FLAG_INVISIBLE = 1;
|
||||
private static final int FLAG_SET_MEASURED_SIZE = 2;
|
||||
private static final int FLAG_LAYOUT_REQUESTED = 4;
|
||||
|
||||
public interface OnClickListener {
|
||||
void onClick(GLView v);
|
||||
}
|
||||
|
||||
protected final Rect mBounds = new Rect();
|
||||
protected final Rect mPaddings = new Rect();
|
||||
|
||||
private GLRoot mRoot;
|
||||
protected GLView mParent;
|
||||
private ArrayList<GLView> mComponents;
|
||||
private GLView mMotionTarget;
|
||||
|
||||
private CanvasAnimation mAnimation;
|
||||
|
||||
private int mViewFlags = 0;
|
||||
|
||||
protected int mMeasuredWidth = 0;
|
||||
protected int mMeasuredHeight = 0;
|
||||
|
||||
private int mLastWidthSpec = -1;
|
||||
private int mLastHeightSpec = -1;
|
||||
|
||||
protected int mScrollY = 0;
|
||||
protected int mScrollX = 0;
|
||||
protected int mScrollHeight = 0;
|
||||
protected int mScrollWidth = 0;
|
||||
|
||||
private float [] mBackgroundColor;
|
||||
|
||||
public void startAnimation(CanvasAnimation animation) {
|
||||
GLRoot root = getGLRoot();
|
||||
if (root == null) throw new IllegalStateException();
|
||||
mAnimation = animation;
|
||||
if (mAnimation != null) {
|
||||
mAnimation.start();
|
||||
root.registerLaunchedAnimation(mAnimation);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
// Sets the visiblity of this GLView (either GLView.VISIBLE or
|
||||
// GLView.INVISIBLE).
|
||||
public void setVisibility(int visibility) {
|
||||
if (visibility == getVisibility()) return;
|
||||
if (visibility == VISIBLE) {
|
||||
mViewFlags &= ~FLAG_INVISIBLE;
|
||||
} else {
|
||||
mViewFlags |= FLAG_INVISIBLE;
|
||||
}
|
||||
onVisibilityChanged(visibility);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
// Returns GLView.VISIBLE or GLView.INVISIBLE
|
||||
public int getVisibility() {
|
||||
return (mViewFlags & FLAG_INVISIBLE) == 0 ? VISIBLE : INVISIBLE;
|
||||
}
|
||||
|
||||
// This should only be called on the content pane (the topmost GLView).
|
||||
public void attachToRoot(GLRoot root) {
|
||||
Utils.assertTrue(mParent == null && mRoot == null);
|
||||
onAttachToRoot(root);
|
||||
}
|
||||
|
||||
// This should only be called on the content pane (the topmost GLView).
|
||||
public void detachFromRoot() {
|
||||
Utils.assertTrue(mParent == null && mRoot != null);
|
||||
onDetachFromRoot();
|
||||
}
|
||||
|
||||
// Returns the number of children of the GLView.
|
||||
public int getComponentCount() {
|
||||
return mComponents == null ? 0 : mComponents.size();
|
||||
}
|
||||
|
||||
// Returns the children for the given index.
|
||||
public GLView getComponent(int index) {
|
||||
if (mComponents == null) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
return mComponents.get(index);
|
||||
}
|
||||
|
||||
// Adds a child to this GLView.
|
||||
public void addComponent(GLView component) {
|
||||
// Make sure the component doesn't have a parent currently.
|
||||
if (component.mParent != null) throw new IllegalStateException();
|
||||
|
||||
// Build parent-child links
|
||||
if (mComponents == null) {
|
||||
mComponents = new ArrayList<GLView>();
|
||||
}
|
||||
mComponents.add(component);
|
||||
component.mParent = this;
|
||||
|
||||
// If this is added after we have a root, tell the component.
|
||||
if (mRoot != null) {
|
||||
component.onAttachToRoot(mRoot);
|
||||
}
|
||||
}
|
||||
|
||||
// Removes a child from this GLView.
|
||||
public boolean removeComponent(GLView component) {
|
||||
if (mComponents == null) return false;
|
||||
if (mComponents.remove(component)) {
|
||||
removeOneComponent(component);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Removes all children of this GLView.
|
||||
public void removeAllComponents() {
|
||||
for (int i = 0, n = mComponents.size(); i < n; ++i) {
|
||||
removeOneComponent(mComponents.get(i));
|
||||
}
|
||||
mComponents.clear();
|
||||
}
|
||||
|
||||
private void removeOneComponent(GLView component) {
|
||||
if (mMotionTarget == component) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
MotionEvent cancelEvent = MotionEvent.obtain(
|
||||
now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
|
||||
dispatchTouchEvent(cancelEvent);
|
||||
cancelEvent.recycle();
|
||||
}
|
||||
component.onDetachFromRoot();
|
||||
component.mParent = null;
|
||||
}
|
||||
|
||||
public Rect bounds() {
|
||||
return mBounds;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mBounds.right - mBounds.left;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mBounds.bottom - mBounds.top;
|
||||
}
|
||||
|
||||
public GLRoot getGLRoot() {
|
||||
return mRoot;
|
||||
}
|
||||
|
||||
// Request re-rendering of the view hierarchy.
|
||||
// This is used for animation or when the contents changed.
|
||||
public void invalidate() {
|
||||
GLRoot root = getGLRoot();
|
||||
if (root != null) root.requestRender();
|
||||
}
|
||||
|
||||
// Request re-layout of the view hierarchy.
|
||||
public void requestLayout() {
|
||||
mViewFlags |= FLAG_LAYOUT_REQUESTED;
|
||||
mLastHeightSpec = -1;
|
||||
mLastWidthSpec = -1;
|
||||
if (mParent != null) {
|
||||
mParent.requestLayout();
|
||||
} else {
|
||||
// Is this a content pane ?
|
||||
GLRoot root = getGLRoot();
|
||||
if (root != null) root.requestLayoutContentPane();
|
||||
}
|
||||
}
|
||||
|
||||
protected void render(GLCanvas canvas) {
|
||||
renderBackground(canvas);
|
||||
canvas.save();
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
renderChild(canvas, getComponent(i));
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
public float [] getBackgroundColor() {
|
||||
return mBackgroundColor;
|
||||
}
|
||||
|
||||
public void setBackgroundColor(float [] color) {
|
||||
mBackgroundColor = color;
|
||||
}
|
||||
|
||||
protected void renderBackground(GLCanvas view) {
|
||||
if (mBackgroundColor != null) {
|
||||
view.clearBuffer(mBackgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderChild(GLCanvas canvas, GLView component) {
|
||||
if (component.getVisibility() != GLView.VISIBLE
|
||||
&& component.mAnimation == null) return;
|
||||
|
||||
int xoffset = component.mBounds.left - mScrollX;
|
||||
int yoffset = component.mBounds.top - mScrollY;
|
||||
|
||||
canvas.translate(xoffset, yoffset);
|
||||
|
||||
CanvasAnimation anim = component.mAnimation;
|
||||
if (anim != null) {
|
||||
canvas.save(anim.getCanvasSaveFlags());
|
||||
if (anim.calculate(AnimationTime.get())) {
|
||||
invalidate();
|
||||
} else {
|
||||
component.mAnimation = null;
|
||||
}
|
||||
anim.apply(canvas);
|
||||
}
|
||||
component.render(canvas);
|
||||
if (anim != null) canvas.restore();
|
||||
canvas.translate(-xoffset, -yoffset);
|
||||
}
|
||||
|
||||
protected boolean onTouch(MotionEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean dispatchTouchEvent(MotionEvent event,
|
||||
int x, int y, GLView component, boolean checkBounds) {
|
||||
Rect rect = component.mBounds;
|
||||
int left = rect.left;
|
||||
int top = rect.top;
|
||||
if (!checkBounds || rect.contains(x, y)) {
|
||||
event.offsetLocation(-left, -top);
|
||||
if (component.dispatchTouchEvent(event)) {
|
||||
event.offsetLocation(left, top);
|
||||
return true;
|
||||
}
|
||||
event.offsetLocation(left, top);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean dispatchTouchEvent(MotionEvent event) {
|
||||
int x = (int) event.getX();
|
||||
int y = (int) event.getY();
|
||||
int action = event.getAction();
|
||||
if (mMotionTarget != null) {
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
MotionEvent cancel = MotionEvent.obtain(event);
|
||||
cancel.setAction(MotionEvent.ACTION_CANCEL);
|
||||
dispatchTouchEvent(cancel, x, y, mMotionTarget, false);
|
||||
mMotionTarget = null;
|
||||
} else {
|
||||
dispatchTouchEvent(event, x, y, mMotionTarget, false);
|
||||
if (action == MotionEvent.ACTION_CANCEL
|
||||
|| action == MotionEvent.ACTION_UP) {
|
||||
mMotionTarget = null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (action == MotionEvent.ACTION_DOWN) {
|
||||
// in the reverse rendering order
|
||||
for (int i = getComponentCount() - 1; i >= 0; --i) {
|
||||
GLView component = getComponent(i);
|
||||
if (component.getVisibility() != GLView.VISIBLE) continue;
|
||||
if (dispatchTouchEvent(event, x, y, component, true)) {
|
||||
mMotionTarget = component;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return onTouch(event);
|
||||
}
|
||||
|
||||
public Rect getPaddings() {
|
||||
return mPaddings;
|
||||
}
|
||||
|
||||
@SuppressLint("WrongCall")
|
||||
public void layout(int left, int top, int right, int bottom) {
|
||||
boolean sizeChanged = setBounds(left, top, right, bottom);
|
||||
mViewFlags &= ~FLAG_LAYOUT_REQUESTED;
|
||||
// We call onLayout no matter sizeChanged is true or not because the
|
||||
// orientation may change without changing the size of the View (for
|
||||
// example, rotate the device by 180 degrees), and we want to handle
|
||||
// orientation change in onLayout.
|
||||
onLayout(sizeChanged, left, top, right, bottom);
|
||||
}
|
||||
|
||||
private boolean setBounds(int left, int top, int right, int bottom) {
|
||||
boolean sizeChanged = (right - left) != (mBounds.right - mBounds.left)
|
||||
|| (bottom - top) != (mBounds.bottom - mBounds.top);
|
||||
mBounds.set(left, top, right, bottom);
|
||||
return sizeChanged;
|
||||
}
|
||||
|
||||
@SuppressLint("WrongCall")
|
||||
public void measure(int widthSpec, int heightSpec) {
|
||||
if (widthSpec == mLastWidthSpec && heightSpec == mLastHeightSpec
|
||||
&& (mViewFlags & FLAG_LAYOUT_REQUESTED) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mLastWidthSpec = widthSpec;
|
||||
mLastHeightSpec = heightSpec;
|
||||
|
||||
mViewFlags &= ~FLAG_SET_MEASURED_SIZE;
|
||||
onMeasure(widthSpec, heightSpec);
|
||||
if ((mViewFlags & FLAG_SET_MEASURED_SIZE) == 0) {
|
||||
throw new IllegalStateException(getClass().getName()
|
||||
+ " should call setMeasuredSize() in onMeasure()");
|
||||
}
|
||||
}
|
||||
|
||||
protected void onMeasure(int widthSpec, int heightSpec) {
|
||||
}
|
||||
|
||||
protected void setMeasuredSize(int width, int height) {
|
||||
mViewFlags |= FLAG_SET_MEASURED_SIZE;
|
||||
mMeasuredWidth = width;
|
||||
mMeasuredHeight = height;
|
||||
}
|
||||
|
||||
public int getMeasuredWidth() {
|
||||
return mMeasuredWidth;
|
||||
}
|
||||
|
||||
public int getMeasuredHeight() {
|
||||
return mMeasuredHeight;
|
||||
}
|
||||
|
||||
protected void onLayout(
|
||||
boolean changeSize, int left, int top, int right, int bottom) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bounds of the given descendant that relative to this view.
|
||||
*/
|
||||
public boolean getBoundsOf(GLView descendant, Rect out) {
|
||||
int xoffset = 0;
|
||||
int yoffset = 0;
|
||||
GLView view = descendant;
|
||||
while (view != this) {
|
||||
if (view == null) return false;
|
||||
Rect bounds = view.mBounds;
|
||||
xoffset += bounds.left;
|
||||
yoffset += bounds.top;
|
||||
view = view.mParent;
|
||||
}
|
||||
out.set(xoffset, yoffset, xoffset + descendant.getWidth(),
|
||||
yoffset + descendant.getHeight());
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void onVisibilityChanged(int visibility) {
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
GLView child = getComponent(i);
|
||||
if (child.getVisibility() == GLView.VISIBLE) {
|
||||
child.onVisibilityChanged(visibility);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void onAttachToRoot(GLRoot root) {
|
||||
mRoot = root;
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
getComponent(i).onAttachToRoot(root);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onDetachFromRoot() {
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
getComponent(i).onDetachFromRoot();
|
||||
}
|
||||
mRoot = null;
|
||||
}
|
||||
|
||||
public void lockRendering() {
|
||||
if (mRoot != null) {
|
||||
mRoot.lockRenderThread();
|
||||
}
|
||||
}
|
||||
|
||||
public void unlockRendering() {
|
||||
if (mRoot != null) {
|
||||
mRoot.unlockRenderThread();
|
||||
}
|
||||
}
|
||||
|
||||
// This is for debugging only.
|
||||
// Dump the view hierarchy into log.
|
||||
void dumpTree(String prefix) {
|
||||
Log.d(TAG, prefix + getClass().getSimpleName());
|
||||
for (int i = 0, n = getComponentCount(); i < n; ++i) {
|
||||
getComponent(i).dumpTree(prefix + "....");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
public interface OrientationSource {
|
||||
public int getDisplayRotation();
|
||||
public int getCompensation();
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.gallery.ui;
|
||||
|
||||
public interface OrientationSource {
|
||||
public int getDisplayRotation();
|
||||
public int getCompensation();
|
||||
}
|
||||
@@ -1,120 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.gallery.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Matrix;
|
||||
import android.util.FloatMath;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.MotionEvent.PointerCoords;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.ApiHelper;
|
||||
|
||||
public final class MotionEventHelper {
|
||||
private MotionEventHelper() {}
|
||||
|
||||
public static MotionEvent transformEvent(MotionEvent e, Matrix m) {
|
||||
// We try to use the new transform method if possible because it uses
|
||||
// less memory.
|
||||
if (ApiHelper.HAS_MOTION_EVENT_TRANSFORM) {
|
||||
return transformEventNew(e, m);
|
||||
} else {
|
||||
return transformEventOld(e, m);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
|
||||
private static MotionEvent transformEventNew(MotionEvent e, Matrix m) {
|
||||
MotionEvent newEvent = MotionEvent.obtain(e);
|
||||
newEvent.transform(m);
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
// This is copied from Input.cpp in the android framework.
|
||||
private static MotionEvent transformEventOld(MotionEvent e, Matrix m) {
|
||||
long downTime = e.getDownTime();
|
||||
long eventTime = e.getEventTime();
|
||||
int action = e.getAction();
|
||||
int pointerCount = e.getPointerCount();
|
||||
int[] pointerIds = getPointerIds(e);
|
||||
PointerCoords[] pointerCoords = getPointerCoords(e);
|
||||
int metaState = e.getMetaState();
|
||||
float xPrecision = e.getXPrecision();
|
||||
float yPrecision = e.getYPrecision();
|
||||
int deviceId = e.getDeviceId();
|
||||
int edgeFlags = e.getEdgeFlags();
|
||||
int source = e.getSource();
|
||||
int flags = e.getFlags();
|
||||
|
||||
// Copy the x and y coordinates into an array, map them, and copy back.
|
||||
float[] xy = new float[pointerCoords.length * 2];
|
||||
for (int i = 0; i < pointerCount;i++) {
|
||||
xy[2 * i] = pointerCoords[i].x;
|
||||
xy[2 * i + 1] = pointerCoords[i].y;
|
||||
}
|
||||
m.mapPoints(xy);
|
||||
for (int i = 0; i < pointerCount;i++) {
|
||||
pointerCoords[i].x = xy[2 * i];
|
||||
pointerCoords[i].y = xy[2 * i + 1];
|
||||
pointerCoords[i].orientation = transformAngle(
|
||||
m, pointerCoords[i].orientation);
|
||||
}
|
||||
|
||||
MotionEvent n = MotionEvent.obtain(downTime, eventTime, action,
|
||||
pointerCount, pointerIds, pointerCoords, metaState, xPrecision,
|
||||
yPrecision, deviceId, edgeFlags, source, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
private static int[] getPointerIds(MotionEvent e) {
|
||||
int n = e.getPointerCount();
|
||||
int[] r = new int[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
r[i] = e.getPointerId(i);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static PointerCoords[] getPointerCoords(MotionEvent e) {
|
||||
int n = e.getPointerCount();
|
||||
PointerCoords[] r = new PointerCoords[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
r[i] = new PointerCoords();
|
||||
e.getPointerCoords(i, r[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static float transformAngle(Matrix m, float angleRadians) {
|
||||
// Construct and transform a vector oriented at the specified clockwise
|
||||
// angle from vertical. Coordinate system: down is increasing Y, right is
|
||||
// increasing X.
|
||||
float[] v = new float[2];
|
||||
v[0] = FloatMath.sin(angleRadians);
|
||||
v[1] = -FloatMath.cos(angleRadians);
|
||||
m.mapVectors(v);
|
||||
|
||||
// Derive the transformed vector's clockwise angle from vertical.
|
||||
float result = (float) Math.atan2(v[0], -v[1]);
|
||||
if (result < -Math.PI / 2) {
|
||||
result += Math.PI;
|
||||
} else if (result > Math.PI / 2) {
|
||||
result -= Math.PI;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.gallery.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Matrix;
|
||||
import android.util.FloatMath;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.MotionEvent.PointerCoords;
|
||||
|
||||
import com.hippo.ehviewer.gallery.common.ApiHelper;
|
||||
|
||||
public final class MotionEventHelper {
|
||||
private MotionEventHelper() {}
|
||||
|
||||
public static MotionEvent transformEvent(MotionEvent e, Matrix m) {
|
||||
// We try to use the new transform method if possible because it uses
|
||||
// less memory.
|
||||
if (ApiHelper.HAS_MOTION_EVENT_TRANSFORM) {
|
||||
return transformEventNew(e, m);
|
||||
} else {
|
||||
return transformEventOld(e, m);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
|
||||
private static MotionEvent transformEventNew(MotionEvent e, Matrix m) {
|
||||
MotionEvent newEvent = MotionEvent.obtain(e);
|
||||
newEvent.transform(m);
|
||||
return newEvent;
|
||||
}
|
||||
|
||||
// This is copied from Input.cpp in the android framework.
|
||||
private static MotionEvent transformEventOld(MotionEvent e, Matrix m) {
|
||||
long downTime = e.getDownTime();
|
||||
long eventTime = e.getEventTime();
|
||||
int action = e.getAction();
|
||||
int pointerCount = e.getPointerCount();
|
||||
int[] pointerIds = getPointerIds(e);
|
||||
PointerCoords[] pointerCoords = getPointerCoords(e);
|
||||
int metaState = e.getMetaState();
|
||||
float xPrecision = e.getXPrecision();
|
||||
float yPrecision = e.getYPrecision();
|
||||
int deviceId = e.getDeviceId();
|
||||
int edgeFlags = e.getEdgeFlags();
|
||||
int source = e.getSource();
|
||||
int flags = e.getFlags();
|
||||
|
||||
// Copy the x and y coordinates into an array, map them, and copy back.
|
||||
float[] xy = new float[pointerCoords.length * 2];
|
||||
for (int i = 0; i < pointerCount;i++) {
|
||||
xy[2 * i] = pointerCoords[i].x;
|
||||
xy[2 * i + 1] = pointerCoords[i].y;
|
||||
}
|
||||
m.mapPoints(xy);
|
||||
for (int i = 0; i < pointerCount;i++) {
|
||||
pointerCoords[i].x = xy[2 * i];
|
||||
pointerCoords[i].y = xy[2 * i + 1];
|
||||
pointerCoords[i].orientation = transformAngle(
|
||||
m, pointerCoords[i].orientation);
|
||||
}
|
||||
|
||||
MotionEvent n = MotionEvent.obtain(downTime, eventTime, action,
|
||||
pointerCount, pointerIds, pointerCoords, metaState, xPrecision,
|
||||
yPrecision, deviceId, edgeFlags, source, flags);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
private static int[] getPointerIds(MotionEvent e) {
|
||||
int n = e.getPointerCount();
|
||||
int[] r = new int[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
r[i] = e.getPointerId(i);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static PointerCoords[] getPointerCoords(MotionEvent e) {
|
||||
int n = e.getPointerCount();
|
||||
PointerCoords[] r = new PointerCoords[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
r[i] = new PointerCoords();
|
||||
e.getPointerCoords(i, r[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private static float transformAngle(Matrix m, float angleRadians) {
|
||||
// Construct and transform a vector oriented at the specified clockwise
|
||||
// angle from vertical. Coordinate system: down is increasing Y, right is
|
||||
// increasing X.
|
||||
float[] v = new float[2];
|
||||
v[0] = FloatMath.sin(angleRadians);
|
||||
v[1] = -FloatMath.cos(angleRadians);
|
||||
m.mapVectors(v);
|
||||
|
||||
// Derive the transformed vector's clockwise angle from vertical.
|
||||
float result = (float) Math.atan2(v[0], -v[1]);
|
||||
if (result < -Math.PI / 2) {
|
||||
result += Math.PI;
|
||||
} else if (result > Math.PI / 2) {
|
||||
result -= Math.PI;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.hippo.ehviewer.network;
|
||||
|
||||
public class Constant {
|
||||
|
||||
public static final int HTTP_TEMP_REDIRECT = 307;
|
||||
|
||||
public static String defaultUserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36";
|
||||
public static String userAgent = System.getProperty("http.agent", defaultUserAgent);
|
||||
public static final int DEFAULT_TIMEOUT = 5 * 1000;
|
||||
public static final int MAX_REDIRECTS = 5;
|
||||
|
||||
public static final int BUFFER_SIZE = 4096;
|
||||
|
||||
}
|
||||
package com.hippo.ehviewer.network;
|
||||
|
||||
public class Constant {
|
||||
|
||||
public static final int HTTP_TEMP_REDIRECT = 307;
|
||||
|
||||
public static String defaultUserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.76 Safari/537.36";
|
||||
public static String userAgent = System.getProperty("http.agent", defaultUserAgent);
|
||||
public static final int DEFAULT_TIMEOUT = 5 * 1000;
|
||||
public static final int MAX_REDIRECTS = 5;
|
||||
|
||||
public static final int BUFFER_SIZE = 4096;
|
||||
|
||||
}
|
||||
@@ -1,196 +1,196 @@
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
public class Crash {
|
||||
private static final String TAG = "Crash";
|
||||
|
||||
private static Context mContext;
|
||||
|
||||
private static String externalCrashFilePath = "/EhViewer/crash";
|
||||
private static String internalCrashFilePath = "/crash";
|
||||
|
||||
private static final String NEW_CRAHS = "new_crash";
|
||||
private static final String LAST_CRASH_POSITION = "last_crash_position";
|
||||
private static final String LAST_CRASH_NAME = "last_crash_name";
|
||||
|
||||
private static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
|
||||
private static boolean mInit = false;
|
||||
|
||||
/**
|
||||
* Init Crash
|
||||
*
|
||||
* @param context Application context
|
||||
*/
|
||||
public static void init(Context context) {
|
||||
if (mInit)
|
||||
return;
|
||||
mInit = true;
|
||||
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is init
|
||||
* @return True if init
|
||||
*/
|
||||
public static boolean isInit() {
|
||||
return mInit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save throwable infomation to file
|
||||
*
|
||||
* @param ex The throwable to store
|
||||
*/
|
||||
public static void saveCrashInfo2File(Throwable ex) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
collectDeviceInfo(sb);
|
||||
|
||||
Writer writer = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(writer);
|
||||
ex.printStackTrace(printWriter);
|
||||
Throwable cause = ex.getCause();
|
||||
while (cause != null) {
|
||||
cause.printStackTrace(printWriter);
|
||||
cause = cause.getCause();
|
||||
}
|
||||
printWriter.close();
|
||||
String result = writer.toString();
|
||||
Log.e(TAG, result);
|
||||
sb.append("======== CrashInfo ========\n");
|
||||
sb.append(result);
|
||||
try {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String time = formatter.format(new Date());
|
||||
String fileName = "crash-" + time + "-" + timestamp + ".log";
|
||||
String path = null;
|
||||
boolean position = false;
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
path = Environment.getExternalStorageDirectory() + externalCrashFilePath;
|
||||
position = true;
|
||||
}
|
||||
else {
|
||||
path = mContext.getFilesDir() + internalCrashFilePath;
|
||||
position = false;
|
||||
}
|
||||
File dir = new File(path);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(path, fileName));
|
||||
fos.write(sb.toString().getBytes());
|
||||
fos.close();
|
||||
// Record info for next time upload
|
||||
SharedPreferences configPre = mContext.getSharedPreferences("config", 0);
|
||||
SharedPreferences.Editor editor = configPre.edit();
|
||||
editor.putBoolean(NEW_CRAHS, true);
|
||||
editor.putBoolean(LAST_CRASH_POSITION, position);
|
||||
editor.putString(LAST_CRASH_NAME, fileName);
|
||||
editor.commit();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while writing crash file...", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectDeviceInfo(StringBuffer sb) {
|
||||
try {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
if (pi != null) {
|
||||
String versionName = pi.versionName == null ? "null" : pi.versionName;
|
||||
String versionCode = String.valueOf(pi.versionCode);
|
||||
sb.append("======== PackageInfo ========\n");
|
||||
sb.append("PackageName=" + pi.packageName + "\n");
|
||||
sb.append("VersionName=" + versionName + "\n");
|
||||
sb.append("VersionCode=" + versionCode + "\n");
|
||||
sb.append("\n");
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(TAG, "an error occured when collect package info", e);
|
||||
}
|
||||
sb.append("======== DeviceInfo ========\n");
|
||||
sb.append("BOARD=" + Build.BOARD + "\n");
|
||||
sb.append("BOOTLOADER=" + Build.BOOTLOADER + "\n");
|
||||
sb.append("CPU_ABI=" + Build.CPU_ABI + "\n");
|
||||
sb.append("CPU_ABI2=" + Build.CPU_ABI2 + "\n");
|
||||
sb.append("DEVICE=" + Build.DEVICE + "\n");
|
||||
sb.append("DISPLAY=" + Build.DISPLAY + "\n");
|
||||
sb.append("FINGERPRINT=" + Build.FINGERPRINT + "\n");
|
||||
sb.append("HARDWARE=" + Build.HARDWARE + "\n");
|
||||
sb.append("HOST=" + Build.HOST + "\n");
|
||||
sb.append("ID=" + Build.ID + "\n");
|
||||
sb.append("MANUFACTURER=" + Build.MANUFACTURER + "\n");
|
||||
sb.append("MODEL=" + Build.MODEL + "\n");
|
||||
sb.append("PRODUCT=" + Build.PRODUCT + "\n");
|
||||
sb.append("RADIO=" + Build.getRadioVersion() + "\n");
|
||||
sb.append("SERIAL=" + Build.SERIAL + "\n");
|
||||
sb.append("TAGS=" + Build.TAGS + "\n");
|
||||
sb.append("TYPE=" + Build.TYPE + "\n");
|
||||
sb.append("USER=" + Build.USER + "\n");
|
||||
sb.append("CODENAME=" + Build.VERSION.CODENAME + "\n");
|
||||
sb.append("INCREMENTAL=" + Build.VERSION.INCREMENTAL + "\n");
|
||||
sb.append("RELEASE=" + Build.VERSION.RELEASE + "\n");
|
||||
sb.append("SDK=" + Build.VERSION.SDK_INT + "\n");
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is there last crash
|
||||
*
|
||||
* @return Return the crash String or null if there is no
|
||||
*/
|
||||
public static String getLastCrash() {
|
||||
SharedPreferences configPre = mContext.getSharedPreferences("config", 0);
|
||||
if (!configPre.getBoolean(NEW_CRAHS, false))
|
||||
return null;
|
||||
|
||||
boolean position = configPre.getBoolean(LAST_CRASH_POSITION, false);
|
||||
String lastCrashName;
|
||||
if ((lastCrashName = configPre.getString(LAST_CRASH_NAME, null)) == null)
|
||||
return null;
|
||||
|
||||
File lastCrashFile;
|
||||
if (position) {
|
||||
if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
|
||||
return null;
|
||||
lastCrashFile = new File(Environment.getExternalStorageDirectory() + externalCrashFilePath, lastCrashName);
|
||||
}
|
||||
else {
|
||||
lastCrashFile = new File(mContext.getFilesDir() + internalCrashFilePath, lastCrashName);
|
||||
}
|
||||
if (!lastCrashFile.isFile())
|
||||
return null;
|
||||
|
||||
try {
|
||||
byte[] buffer = new byte[(int) lastCrashFile.length()];
|
||||
DataInputStream din = new DataInputStream(new FileInputStream(lastCrashFile));
|
||||
din.readFully(buffer);
|
||||
din.close();
|
||||
configPre.edit().putBoolean(NEW_CRAHS, false).commit();
|
||||
return new String(buffer);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while reading crash file...", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
public class Crash {
|
||||
private static final String TAG = "Crash";
|
||||
|
||||
private static Context mContext;
|
||||
|
||||
private static String externalCrashFilePath = "/EhViewer/crash";
|
||||
private static String internalCrashFilePath = "/crash";
|
||||
|
||||
private static final String NEW_CRAHS = "new_crash";
|
||||
private static final String LAST_CRASH_POSITION = "last_crash_position";
|
||||
private static final String LAST_CRASH_NAME = "last_crash_name";
|
||||
|
||||
private static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
|
||||
private static boolean mInit = false;
|
||||
|
||||
/**
|
||||
* Init Crash
|
||||
*
|
||||
* @param context Application context
|
||||
*/
|
||||
public static void init(Context context) {
|
||||
if (mInit)
|
||||
return;
|
||||
mInit = true;
|
||||
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is init
|
||||
* @return True if init
|
||||
*/
|
||||
public static boolean isInit() {
|
||||
return mInit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save throwable infomation to file
|
||||
*
|
||||
* @param ex The throwable to store
|
||||
*/
|
||||
public static void saveCrashInfo2File(Throwable ex) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
collectDeviceInfo(sb);
|
||||
|
||||
Writer writer = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(writer);
|
||||
ex.printStackTrace(printWriter);
|
||||
Throwable cause = ex.getCause();
|
||||
while (cause != null) {
|
||||
cause.printStackTrace(printWriter);
|
||||
cause = cause.getCause();
|
||||
}
|
||||
printWriter.close();
|
||||
String result = writer.toString();
|
||||
Log.e(TAG, result);
|
||||
sb.append("======== CrashInfo ========\n");
|
||||
sb.append(result);
|
||||
try {
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String time = formatter.format(new Date());
|
||||
String fileName = "crash-" + time + "-" + timestamp + ".log";
|
||||
String path = null;
|
||||
boolean position = false;
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
path = Environment.getExternalStorageDirectory() + externalCrashFilePath;
|
||||
position = true;
|
||||
}
|
||||
else {
|
||||
path = mContext.getFilesDir() + internalCrashFilePath;
|
||||
position = false;
|
||||
}
|
||||
File dir = new File(path);
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
FileOutputStream fos = new FileOutputStream(new File(path, fileName));
|
||||
fos.write(sb.toString().getBytes());
|
||||
fos.close();
|
||||
// Record info for next time upload
|
||||
SharedPreferences configPre = mContext.getSharedPreferences("config", 0);
|
||||
SharedPreferences.Editor editor = configPre.edit();
|
||||
editor.putBoolean(NEW_CRAHS, true);
|
||||
editor.putBoolean(LAST_CRASH_POSITION, position);
|
||||
editor.putString(LAST_CRASH_NAME, fileName);
|
||||
editor.commit();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while writing crash file...", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectDeviceInfo(StringBuffer sb) {
|
||||
try {
|
||||
PackageManager pm = mContext.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
if (pi != null) {
|
||||
String versionName = pi.versionName == null ? "null" : pi.versionName;
|
||||
String versionCode = String.valueOf(pi.versionCode);
|
||||
sb.append("======== PackageInfo ========\n");
|
||||
sb.append("PackageName=" + pi.packageName + "\n");
|
||||
sb.append("VersionName=" + versionName + "\n");
|
||||
sb.append("VersionCode=" + versionCode + "\n");
|
||||
sb.append("\n");
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
Log.e(TAG, "an error occured when collect package info", e);
|
||||
}
|
||||
sb.append("======== DeviceInfo ========\n");
|
||||
sb.append("BOARD=" + Build.BOARD + "\n");
|
||||
sb.append("BOOTLOADER=" + Build.BOOTLOADER + "\n");
|
||||
sb.append("CPU_ABI=" + Build.CPU_ABI + "\n");
|
||||
sb.append("CPU_ABI2=" + Build.CPU_ABI2 + "\n");
|
||||
sb.append("DEVICE=" + Build.DEVICE + "\n");
|
||||
sb.append("DISPLAY=" + Build.DISPLAY + "\n");
|
||||
sb.append("FINGERPRINT=" + Build.FINGERPRINT + "\n");
|
||||
sb.append("HARDWARE=" + Build.HARDWARE + "\n");
|
||||
sb.append("HOST=" + Build.HOST + "\n");
|
||||
sb.append("ID=" + Build.ID + "\n");
|
||||
sb.append("MANUFACTURER=" + Build.MANUFACTURER + "\n");
|
||||
sb.append("MODEL=" + Build.MODEL + "\n");
|
||||
sb.append("PRODUCT=" + Build.PRODUCT + "\n");
|
||||
sb.append("RADIO=" + Build.getRadioVersion() + "\n");
|
||||
sb.append("SERIAL=" + Build.SERIAL + "\n");
|
||||
sb.append("TAGS=" + Build.TAGS + "\n");
|
||||
sb.append("TYPE=" + Build.TYPE + "\n");
|
||||
sb.append("USER=" + Build.USER + "\n");
|
||||
sb.append("CODENAME=" + Build.VERSION.CODENAME + "\n");
|
||||
sb.append("INCREMENTAL=" + Build.VERSION.INCREMENTAL + "\n");
|
||||
sb.append("RELEASE=" + Build.VERSION.RELEASE + "\n");
|
||||
sb.append("SDK=" + Build.VERSION.SDK_INT + "\n");
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is there last crash
|
||||
*
|
||||
* @return Return the crash String or null if there is no
|
||||
*/
|
||||
public static String getLastCrash() {
|
||||
SharedPreferences configPre = mContext.getSharedPreferences("config", 0);
|
||||
if (!configPre.getBoolean(NEW_CRAHS, false))
|
||||
return null;
|
||||
|
||||
boolean position = configPre.getBoolean(LAST_CRASH_POSITION, false);
|
||||
String lastCrashName;
|
||||
if ((lastCrashName = configPre.getString(LAST_CRASH_NAME, null)) == null)
|
||||
return null;
|
||||
|
||||
File lastCrashFile;
|
||||
if (position) {
|
||||
if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
|
||||
return null;
|
||||
lastCrashFile = new File(Environment.getExternalStorageDirectory() + externalCrashFilePath, lastCrashName);
|
||||
}
|
||||
else {
|
||||
lastCrashFile = new File(mContext.getFilesDir() + internalCrashFilePath, lastCrashName);
|
||||
}
|
||||
if (!lastCrashFile.isFile())
|
||||
return null;
|
||||
|
||||
try {
|
||||
byte[] buffer = new byte[(int) lastCrashFile.length()];
|
||||
DataInputStream din = new DataInputStream(new FileInputStream(lastCrashFile));
|
||||
din.readFully(buffer);
|
||||
din.close();
|
||||
configPre.edit().putBoolean(NEW_CRAHS, false).commit();
|
||||
return new String(buffer);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An error occured while reading crash file...", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
// This Future differs from the java.util.concurrent.Future in these aspects:
|
||||
//
|
||||
// - Once cancel() is called, isCancelled() always returns true. It is a sticky
|
||||
// flag used to communicate to the implementation. The implmentation may
|
||||
// ignore that flag. Regardless whether the Future is cancelled, a return
|
||||
// value will be provided to get(). The implementation may choose to return
|
||||
// null if it finds the Future is cancelled.
|
||||
//
|
||||
// - get() does not throw exceptions.
|
||||
//
|
||||
public interface Future<T> {
|
||||
public void cancel();
|
||||
public boolean isCancelled();
|
||||
public boolean isDone();
|
||||
public T get();
|
||||
public void waitDone();
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
// This Future differs from the java.util.concurrent.Future in these aspects:
|
||||
//
|
||||
// - Once cancel() is called, isCancelled() always returns true. It is a sticky
|
||||
// flag used to communicate to the implementation. The implmentation may
|
||||
// ignore that flag. Regardless whether the Future is cancelled, a return
|
||||
// value will be provided to get(). The implementation may choose to return
|
||||
// null if it finds the Future is cancelled.
|
||||
//
|
||||
// - get() does not throw exceptions.
|
||||
//
|
||||
public interface Future<T> {
|
||||
public void cancel();
|
||||
public boolean isCancelled();
|
||||
public boolean isDone();
|
||||
public T get();
|
||||
public void waitDone();
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
public interface FutureListener<T> {
|
||||
public void onFutureDone(Future<T> future);
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
public interface FutureListener<T> {
|
||||
public void onFutureDone(Future<T> future);
|
||||
}
|
||||
@@ -1,60 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
public class IntArray {
|
||||
private static final int INIT_CAPACITY = 8;
|
||||
|
||||
private int mData[] = new int[INIT_CAPACITY];
|
||||
private int mSize = 0;
|
||||
|
||||
public void add(int value) {
|
||||
if (mData.length == mSize) {
|
||||
int temp[] = new int[mSize + mSize];
|
||||
System.arraycopy(mData, 0, temp, 0, mSize);
|
||||
mData = temp;
|
||||
}
|
||||
mData[mSize++] = value;
|
||||
}
|
||||
|
||||
public int removeLast() {
|
||||
mSize--;
|
||||
return mData[mSize];
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
// For testing only
|
||||
public int[] toArray(int[] result) {
|
||||
if (result == null || result.length < mSize) {
|
||||
result = new int[mSize];
|
||||
}
|
||||
System.arraycopy(mData, 0, result, 0, mSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int[] getInternalArray() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mSize = 0;
|
||||
if (mData.length != INIT_CAPACITY) mData = new int[INIT_CAPACITY];
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
public class IntArray {
|
||||
private static final int INIT_CAPACITY = 8;
|
||||
|
||||
private int mData[] = new int[INIT_CAPACITY];
|
||||
private int mSize = 0;
|
||||
|
||||
public void add(int value) {
|
||||
if (mData.length == mSize) {
|
||||
int temp[] = new int[mSize + mSize];
|
||||
System.arraycopy(mData, 0, temp, 0, mSize);
|
||||
mData = temp;
|
||||
}
|
||||
mData[mSize++] = value;
|
||||
}
|
||||
|
||||
public int removeLast() {
|
||||
mSize--;
|
||||
return mData[mSize];
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
// For testing only
|
||||
public int[] toArray(int[] result) {
|
||||
if (result == null || result.length < mSize) {
|
||||
result = new int[mSize];
|
||||
}
|
||||
System.arraycopy(mData, 0, result, 0, mSize);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int[] getInternalArray() {
|
||||
return mData;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mSize = 0;
|
||||
if (mData.length != INIT_CAPACITY) mData = new int[INIT_CAPACITY];
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
|
||||
import android.os.Process;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A thread factory that creates threads with a given thread priority.
|
||||
*/
|
||||
public class PriorityThreadFactory implements ThreadFactory {
|
||||
|
||||
private final int mPriority;
|
||||
private final AtomicInteger mNumber = new AtomicInteger();
|
||||
private final String mName;
|
||||
|
||||
public PriorityThreadFactory(String name, int priority) {
|
||||
mName = name;
|
||||
mPriority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, mName + '-' + mNumber.getAndIncrement()) {
|
||||
@Override
|
||||
public void run() {
|
||||
Process.setThreadPriority(mPriority);
|
||||
super.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
|
||||
import android.os.Process;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A thread factory that creates threads with a given thread priority.
|
||||
*/
|
||||
public class PriorityThreadFactory implements ThreadFactory {
|
||||
|
||||
private final int mPriority;
|
||||
private final AtomicInteger mNumber = new AtomicInteger();
|
||||
private final String mName;
|
||||
|
||||
public PriorityThreadFactory(String name, int priority) {
|
||||
mName = name;
|
||||
mPriority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, mName + '-' + mNumber.getAndIncrement()) {
|
||||
@Override
|
||||
public void run() {
|
||||
Process.setThreadPriority(mPriority);
|
||||
super.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,268 +1,268 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ThreadPool {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "ThreadPool";
|
||||
private static final int CORE_POOL_SIZE = 4;
|
||||
private static final int MAX_POOL_SIZE = 8;
|
||||
private static final int KEEP_ALIVE_TIME = 10; // 10 seconds
|
||||
|
||||
// Resource type
|
||||
public static final int MODE_NONE = 0;
|
||||
public static final int MODE_CPU = 1;
|
||||
public static final int MODE_NETWORK = 2;
|
||||
|
||||
public static final JobContext JOB_CONTEXT_STUB = new JobContextStub();
|
||||
|
||||
ResourceCounter mCpuCounter = new ResourceCounter(2);
|
||||
ResourceCounter mNetworkCounter = new ResourceCounter(2);
|
||||
|
||||
// A Job is like a Callable, but it has an addition JobContext parameter.
|
||||
public interface Job<T> {
|
||||
public T run(JobContext jc);
|
||||
}
|
||||
|
||||
public interface JobContext {
|
||||
boolean isCancelled();
|
||||
void setCancelListener(CancelListener listener);
|
||||
boolean setMode(int mode);
|
||||
}
|
||||
|
||||
private static class JobContextStub implements JobContext {
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelListener(CancelListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMode(int mode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public interface CancelListener {
|
||||
public void onCancel();
|
||||
}
|
||||
|
||||
private static class ResourceCounter {
|
||||
public int value;
|
||||
public ResourceCounter(int v) {
|
||||
value = v;
|
||||
}
|
||||
}
|
||||
|
||||
private final Executor mExecutor;
|
||||
|
||||
public ThreadPool() {
|
||||
this(CORE_POOL_SIZE, MAX_POOL_SIZE);
|
||||
}
|
||||
|
||||
public ThreadPool(int initPoolSize, int maxPoolSize) {
|
||||
mExecutor = new ThreadPoolExecutor(
|
||||
initPoolSize, maxPoolSize, KEEP_ALIVE_TIME,
|
||||
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
|
||||
new PriorityThreadFactory("thread-pool",
|
||||
android.os.Process.THREAD_PRIORITY_DEFAULT));
|
||||
}
|
||||
|
||||
// Submit a job to the thread pool. The listener will be called when the
|
||||
// job is finished (or cancelled).
|
||||
public <T> Future<T> submit(Job<T> job, FutureListener<T> listener) {
|
||||
Worker<T> w = new Worker<T>(job, listener);
|
||||
mExecutor.execute(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
public <T> Future<T> submit(Job<T> job) {
|
||||
return submit(job, null);
|
||||
}
|
||||
|
||||
private class Worker<T> implements Runnable, Future<T>, JobContext {
|
||||
@SuppressWarnings("hiding")
|
||||
private static final String TAG = "Worker";
|
||||
private Job<T> mJob;
|
||||
private FutureListener<T> mListener;
|
||||
private CancelListener mCancelListener;
|
||||
private ResourceCounter mWaitOnResource;
|
||||
private volatile boolean mIsCancelled;
|
||||
private boolean mIsDone;
|
||||
private T mResult;
|
||||
private int mMode;
|
||||
|
||||
public Worker(Job<T> job, FutureListener<T> listener) {
|
||||
mJob = job;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
// This is called by a thread in the thread pool.
|
||||
@Override
|
||||
public void run() {
|
||||
T result = null;
|
||||
|
||||
// A job is in CPU mode by default. setMode returns false
|
||||
// if the job is cancelled.
|
||||
if (setMode(MODE_CPU)) {
|
||||
try {
|
||||
result = mJob.run(this);
|
||||
} catch (Throwable ex) {
|
||||
Log.w(TAG, "Exception in running a job", ex);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
setMode(MODE_NONE);
|
||||
mResult = result;
|
||||
mIsDone = true;
|
||||
notifyAll();
|
||||
}
|
||||
if (mListener != null) mListener.onFutureDone(this);
|
||||
}
|
||||
|
||||
// Below are the methods for Future.
|
||||
@Override
|
||||
public synchronized void cancel() {
|
||||
if (mIsCancelled) return;
|
||||
mIsCancelled = true;
|
||||
if (mWaitOnResource != null) {
|
||||
synchronized (mWaitOnResource) {
|
||||
mWaitOnResource.notifyAll();
|
||||
}
|
||||
}
|
||||
if (mCancelListener != null) {
|
||||
mCancelListener.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return mIsCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isDone() {
|
||||
return mIsDone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T get() {
|
||||
while (!mIsDone) {
|
||||
try {
|
||||
wait();
|
||||
} catch (Exception ex) {
|
||||
Log.w(TAG, "ingore exception", ex);
|
||||
// ignore.
|
||||
}
|
||||
}
|
||||
return mResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitDone() {
|
||||
get();
|
||||
}
|
||||
|
||||
// Below are the methods for JobContext (only called from the
|
||||
// thread running the job)
|
||||
@Override
|
||||
public synchronized void setCancelListener(CancelListener listener) {
|
||||
mCancelListener = listener;
|
||||
if (mIsCancelled && mCancelListener != null) {
|
||||
mCancelListener.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMode(int mode) {
|
||||
// Release old resource
|
||||
ResourceCounter rc = modeToCounter(mMode);
|
||||
if (rc != null) releaseResource(rc);
|
||||
mMode = MODE_NONE;
|
||||
|
||||
// Acquire new resource
|
||||
rc = modeToCounter(mode);
|
||||
if (rc != null) {
|
||||
if (!acquireResource(rc)) {
|
||||
return false;
|
||||
}
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceCounter modeToCounter(int mode) {
|
||||
if (mode == MODE_CPU) {
|
||||
return mCpuCounter;
|
||||
} else if (mode == MODE_NETWORK) {
|
||||
return mNetworkCounter;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean acquireResource(ResourceCounter counter) {
|
||||
while (true) {
|
||||
synchronized (this) {
|
||||
if (mIsCancelled) {
|
||||
mWaitOnResource = null;
|
||||
return false;
|
||||
}
|
||||
mWaitOnResource = counter;
|
||||
}
|
||||
|
||||
synchronized (counter) {
|
||||
if (counter.value > 0) {
|
||||
counter.value--;
|
||||
break;
|
||||
} else {
|
||||
try {
|
||||
counter.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
// ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
mWaitOnResource = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void releaseResource(ResourceCounter counter) {
|
||||
synchronized (counter) {
|
||||
counter.value++;
|
||||
counter.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
import com.hippo.ehviewer.util.Log;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ThreadPool {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = "ThreadPool";
|
||||
private static final int CORE_POOL_SIZE = 4;
|
||||
private static final int MAX_POOL_SIZE = 8;
|
||||
private static final int KEEP_ALIVE_TIME = 10; // 10 seconds
|
||||
|
||||
// Resource type
|
||||
public static final int MODE_NONE = 0;
|
||||
public static final int MODE_CPU = 1;
|
||||
public static final int MODE_NETWORK = 2;
|
||||
|
||||
public static final JobContext JOB_CONTEXT_STUB = new JobContextStub();
|
||||
|
||||
ResourceCounter mCpuCounter = new ResourceCounter(2);
|
||||
ResourceCounter mNetworkCounter = new ResourceCounter(2);
|
||||
|
||||
// A Job is like a Callable, but it has an addition JobContext parameter.
|
||||
public interface Job<T> {
|
||||
public T run(JobContext jc);
|
||||
}
|
||||
|
||||
public interface JobContext {
|
||||
boolean isCancelled();
|
||||
void setCancelListener(CancelListener listener);
|
||||
boolean setMode(int mode);
|
||||
}
|
||||
|
||||
private static class JobContextStub implements JobContext {
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelListener(CancelListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMode(int mode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public interface CancelListener {
|
||||
public void onCancel();
|
||||
}
|
||||
|
||||
private static class ResourceCounter {
|
||||
public int value;
|
||||
public ResourceCounter(int v) {
|
||||
value = v;
|
||||
}
|
||||
}
|
||||
|
||||
private final Executor mExecutor;
|
||||
|
||||
public ThreadPool() {
|
||||
this(CORE_POOL_SIZE, MAX_POOL_SIZE);
|
||||
}
|
||||
|
||||
public ThreadPool(int initPoolSize, int maxPoolSize) {
|
||||
mExecutor = new ThreadPoolExecutor(
|
||||
initPoolSize, maxPoolSize, KEEP_ALIVE_TIME,
|
||||
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
|
||||
new PriorityThreadFactory("thread-pool",
|
||||
android.os.Process.THREAD_PRIORITY_DEFAULT));
|
||||
}
|
||||
|
||||
// Submit a job to the thread pool. The listener will be called when the
|
||||
// job is finished (or cancelled).
|
||||
public <T> Future<T> submit(Job<T> job, FutureListener<T> listener) {
|
||||
Worker<T> w = new Worker<T>(job, listener);
|
||||
mExecutor.execute(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
public <T> Future<T> submit(Job<T> job) {
|
||||
return submit(job, null);
|
||||
}
|
||||
|
||||
private class Worker<T> implements Runnable, Future<T>, JobContext {
|
||||
@SuppressWarnings("hiding")
|
||||
private static final String TAG = "Worker";
|
||||
private Job<T> mJob;
|
||||
private FutureListener<T> mListener;
|
||||
private CancelListener mCancelListener;
|
||||
private ResourceCounter mWaitOnResource;
|
||||
private volatile boolean mIsCancelled;
|
||||
private boolean mIsDone;
|
||||
private T mResult;
|
||||
private int mMode;
|
||||
|
||||
public Worker(Job<T> job, FutureListener<T> listener) {
|
||||
mJob = job;
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
// This is called by a thread in the thread pool.
|
||||
@Override
|
||||
public void run() {
|
||||
T result = null;
|
||||
|
||||
// A job is in CPU mode by default. setMode returns false
|
||||
// if the job is cancelled.
|
||||
if (setMode(MODE_CPU)) {
|
||||
try {
|
||||
result = mJob.run(this);
|
||||
} catch (Throwable ex) {
|
||||
Log.w(TAG, "Exception in running a job", ex);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
setMode(MODE_NONE);
|
||||
mResult = result;
|
||||
mIsDone = true;
|
||||
notifyAll();
|
||||
}
|
||||
if (mListener != null) mListener.onFutureDone(this);
|
||||
}
|
||||
|
||||
// Below are the methods for Future.
|
||||
@Override
|
||||
public synchronized void cancel() {
|
||||
if (mIsCancelled) return;
|
||||
mIsCancelled = true;
|
||||
if (mWaitOnResource != null) {
|
||||
synchronized (mWaitOnResource) {
|
||||
mWaitOnResource.notifyAll();
|
||||
}
|
||||
}
|
||||
if (mCancelListener != null) {
|
||||
mCancelListener.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return mIsCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isDone() {
|
||||
return mIsDone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized T get() {
|
||||
while (!mIsDone) {
|
||||
try {
|
||||
wait();
|
||||
} catch (Exception ex) {
|
||||
Log.w(TAG, "ingore exception", ex);
|
||||
// ignore.
|
||||
}
|
||||
}
|
||||
return mResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waitDone() {
|
||||
get();
|
||||
}
|
||||
|
||||
// Below are the methods for JobContext (only called from the
|
||||
// thread running the job)
|
||||
@Override
|
||||
public synchronized void setCancelListener(CancelListener listener) {
|
||||
mCancelListener = listener;
|
||||
if (mIsCancelled && mCancelListener != null) {
|
||||
mCancelListener.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setMode(int mode) {
|
||||
// Release old resource
|
||||
ResourceCounter rc = modeToCounter(mMode);
|
||||
if (rc != null) releaseResource(rc);
|
||||
mMode = MODE_NONE;
|
||||
|
||||
// Acquire new resource
|
||||
rc = modeToCounter(mode);
|
||||
if (rc != null) {
|
||||
if (!acquireResource(rc)) {
|
||||
return false;
|
||||
}
|
||||
mMode = mode;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ResourceCounter modeToCounter(int mode) {
|
||||
if (mode == MODE_CPU) {
|
||||
return mCpuCounter;
|
||||
} else if (mode == MODE_NETWORK) {
|
||||
return mNetworkCounter;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean acquireResource(ResourceCounter counter) {
|
||||
while (true) {
|
||||
synchronized (this) {
|
||||
if (mIsCancelled) {
|
||||
mWaitOnResource = null;
|
||||
return false;
|
||||
}
|
||||
mWaitOnResource = counter;
|
||||
}
|
||||
|
||||
synchronized (counter) {
|
||||
if (counter.value > 0) {
|
||||
counter.value--;
|
||||
break;
|
||||
} else {
|
||||
try {
|
||||
counter.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
// ignore.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
mWaitOnResource = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void releaseResource(ResourceCounter counter) {
|
||||
synchronized (counter) {
|
||||
counter.value++;
|
||||
counter.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +1,119 @@
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
public abstract class TimeRunner {
|
||||
|
||||
private static final int INTERVAL = 5;
|
||||
|
||||
private static final int RUNNING = 0;
|
||||
private static final int START = 1;
|
||||
private static final int END = 2;
|
||||
|
||||
private boolean mRunInThread = true;
|
||||
private boolean mListenerInThread = true;
|
||||
|
||||
private OnTimeListener mListener;
|
||||
|
||||
public interface OnTimeListener {
|
||||
public void onStart();
|
||||
public void onEnd();
|
||||
}
|
||||
|
||||
// TODO
|
||||
@SuppressLint("HandlerLeak")
|
||||
private Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case RUNNING:
|
||||
run((Float)msg.obj, msg.arg1);
|
||||
break;
|
||||
case START:
|
||||
mListener.onStart();
|
||||
break;
|
||||
case END:
|
||||
mListener.onEnd();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private int mDuration = 0;
|
||||
private int mDelay = 0;
|
||||
|
||||
public void setOnTimerListener(OnTimeListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
public void setRunInThread(boolean runInThread) {
|
||||
mRunInThread = runInThread;
|
||||
}
|
||||
|
||||
public void setListenerInThread(boolean listenerInThread) {
|
||||
mListenerInThread = listenerInThread;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
start(0);
|
||||
}
|
||||
|
||||
public void start(int delay) {
|
||||
mDelay = delay;
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(mDelay);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (mListener != null) {
|
||||
if (mListenerInThread) {
|
||||
mListener.onStart();
|
||||
} else {
|
||||
Message msg = new Message();
|
||||
msg.what = START;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int runningTime = 0; runningTime < mDuration; runningTime = (int)(System.currentTimeMillis() - startTime)) {
|
||||
if (mRunInThread) {
|
||||
TimeRunner.this.run(runningTime/(float)mDuration, runningTime);
|
||||
} else {
|
||||
Message msg = new Message();
|
||||
msg.what = RUNNING;
|
||||
msg.obj = runningTime/(float)mDuration;
|
||||
msg.arg1 = runningTime;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(INTERVAL);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
if (mListenerInThread) {
|
||||
mListener.onEnd();
|
||||
} else {
|
||||
Message msg = new Message();
|
||||
msg.what = END;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
protected abstract void run(float interpolatedTime, int runningTime);
|
||||
}
|
||||
package com.hippo.ehviewer.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
public abstract class TimeRunner {
|
||||
|
||||
private static final int INTERVAL = 5;
|
||||
|
||||
private static final int RUNNING = 0;
|
||||
private static final int START = 1;
|
||||
private static final int END = 2;
|
||||
|
||||
private boolean mRunInThread = true;
|
||||
private boolean mListenerInThread = true;
|
||||
|
||||
private OnTimeListener mListener;
|
||||
|
||||
public interface OnTimeListener {
|
||||
public void onStart();
|
||||
public void onEnd();
|
||||
}
|
||||
|
||||
// TODO
|
||||
@SuppressLint("HandlerLeak")
|
||||
private Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case RUNNING:
|
||||
run((Float)msg.obj, msg.arg1);
|
||||
break;
|
||||
case START:
|
||||
mListener.onStart();
|
||||
break;
|
||||
case END:
|
||||
mListener.onEnd();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private int mDuration = 0;
|
||||
private int mDelay = 0;
|
||||
|
||||
public void setOnTimerListener(OnTimeListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
public void setRunInThread(boolean runInThread) {
|
||||
mRunInThread = runInThread;
|
||||
}
|
||||
|
||||
public void setListenerInThread(boolean listenerInThread) {
|
||||
mListenerInThread = listenerInThread;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
start(0);
|
||||
}
|
||||
|
||||
public void start(int delay) {
|
||||
mDelay = delay;
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(mDelay);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (mListener != null) {
|
||||
if (mListenerInThread) {
|
||||
mListener.onStart();
|
||||
} else {
|
||||
Message msg = new Message();
|
||||
msg.what = START;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (int runningTime = 0; runningTime < mDuration; runningTime = (int)(System.currentTimeMillis() - startTime)) {
|
||||
if (mRunInThread) {
|
||||
TimeRunner.this.run(runningTime/(float)mDuration, runningTime);
|
||||
} else {
|
||||
Message msg = new Message();
|
||||
msg.what = RUNNING;
|
||||
msg.obj = runningTime/(float)mDuration;
|
||||
msg.arg1 = runningTime;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(INTERVAL);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (mListener != null) {
|
||||
if (mListenerInThread) {
|
||||
mListener.onEnd();
|
||||
} else {
|
||||
Message msg = new Message();
|
||||
msg.what = END;
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
protected abstract void run(float interpolatedTime, int runningTime);
|
||||
}
|
||||
@@ -1,201 +1,201 @@
|
||||
package com.hippo.ehviewer.widget;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* TODO disable pull refreshing when footer refreshing
|
||||
*
|
||||
* @author Hippo
|
||||
*
|
||||
*/
|
||||
public class HfListView extends SwipeRefreshLayout
|
||||
implements AbsListView.OnScrollListener {
|
||||
|
||||
private final static int FOOTER_REFRESHING = 0;
|
||||
private final static int FOOTER_SUCCESS = 1;
|
||||
private final static int FOOTER_FAIL = 2;
|
||||
|
||||
private Context mContext;
|
||||
private FswListView mListView;
|
||||
|
||||
private View mFooter;
|
||||
private TextView mFooterTipTextView;
|
||||
private ProgressBar mFooterProgressBar;
|
||||
private int footerState = FOOTER_SUCCESS;
|
||||
private OnFooterRefreshListener mFooterRefreshListener;
|
||||
|
||||
private OnFitSystemWindowsListener mListener;
|
||||
|
||||
// Footer String to show
|
||||
private String mFooterRefreshStr;
|
||||
private String mFooterSuccessStr;
|
||||
private String mFooterFailStr;
|
||||
|
||||
public HfListView(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
init();
|
||||
}
|
||||
|
||||
public HfListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mListView = new FswListView(mContext);
|
||||
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
addView(mListView, lp);
|
||||
|
||||
mListView.setOnScrollListener(this);
|
||||
mFooter = (LinearLayout)LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.pull_list_view_footer, null);
|
||||
mFooterTipTextView = (TextView)mFooter.findViewById(R.id.footer_tip_text);
|
||||
mFooterProgressBar = (ProgressBar)mFooter.findViewById(R.id.footer_progressBar);
|
||||
mFooter.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!isAnyRefreshing() && footerState == FOOTER_FAIL)
|
||||
footerRefresh();
|
||||
}
|
||||
});
|
||||
changeFooterViewByState();
|
||||
mListView.addFooterView(mFooter);
|
||||
}
|
||||
|
||||
public FswListView getListView() {
|
||||
return mListView;
|
||||
}
|
||||
|
||||
public void setOnFooterRefreshListener(OnFooterRefreshListener l) {
|
||||
mFooterRefreshListener = l;
|
||||
}
|
||||
|
||||
private void footerRefresh() {
|
||||
if (mFooterRefreshListener.onFooterRefresh()) {
|
||||
footerState = FOOTER_REFRESHING;
|
||||
changeFooterViewByState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set footer UI String
|
||||
*
|
||||
* @param refreshStr
|
||||
* @param successStr
|
||||
* @param failStr
|
||||
*/
|
||||
public void setFooterString(String refreshStr, String successStr, String failStr) {
|
||||
mFooterRefreshStr = refreshStr;
|
||||
mFooterSuccessStr = successStr;
|
||||
mFooterFailStr = failStr;
|
||||
|
||||
switch (footerState) {
|
||||
case FOOTER_REFRESHING:
|
||||
mFooterTipTextView.setText(mFooterRefreshStr);
|
||||
break;
|
||||
case FOOTER_SUCCESS:
|
||||
mFooterTipTextView.setText(mFooterSuccessStr);
|
||||
break;
|
||||
case FOOTER_FAIL:
|
||||
mFooterTipTextView.setText(mFooterFailStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh footer UI
|
||||
*/
|
||||
private void changeFooterViewByState() {
|
||||
switch (footerState) {
|
||||
case FOOTER_REFRESHING:
|
||||
mFooterTipTextView.setText(mFooterRefreshStr);
|
||||
mFooterProgressBar.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
case FOOTER_SUCCESS:
|
||||
mFooterTipTextView.setText(mFooterSuccessStr);
|
||||
mFooterProgressBar.setVisibility(View.GONE);
|
||||
break;
|
||||
case FOOTER_FAIL:
|
||||
mFooterTipTextView.setText(mFooterFailStr);
|
||||
mFooterProgressBar.setVisibility(View.GONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void setHeaderRefreshComplete() {
|
||||
super.setRefreshing(false);
|
||||
}
|
||||
|
||||
public void setFooterRefreshComplete(boolean isSuccess) {
|
||||
if (isSuccess)
|
||||
footerState = FOOTER_SUCCESS;
|
||||
else
|
||||
footerState = FOOTER_FAIL;
|
||||
changeFooterViewByState();
|
||||
}
|
||||
|
||||
public void setAnyRefreshComplete() {
|
||||
setAnyRefreshComplete(true);
|
||||
}
|
||||
|
||||
public void setAnyRefreshComplete(boolean isSuccess) {
|
||||
setHeaderRefreshComplete();
|
||||
setFooterRefreshComplete(isSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if actionbar is refreshing
|
||||
*/
|
||||
public boolean isHeaderRefreshing() {
|
||||
return super.isRefreshing();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if footer is refreshing
|
||||
*/
|
||||
public boolean isFooterRefreshing() {
|
||||
return footerState == FOOTER_REFRESHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if actionbar or footer is refreshing
|
||||
*/
|
||||
public boolean isAnyRefreshing() {
|
||||
return isHeaderRefreshing() | isFooterRefreshing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem,
|
||||
int visibleItemCount, int totalItemCount) {
|
||||
if (firstVisibleItem + visibleItemCount == totalItemCount
|
||||
&& !isAnyRefreshing() && totalItemCount > 1
|
||||
&& footerState != FOOTER_FAIL)
|
||||
footerRefresh();
|
||||
}
|
||||
|
||||
public interface OnFooterRefreshListener {
|
||||
/**
|
||||
* @return True if this refresh action is vaild
|
||||
*/
|
||||
boolean onFooterRefresh();
|
||||
}
|
||||
}
|
||||
package com.hippo.ehviewer.widget;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* TODO disable pull refreshing when footer refreshing
|
||||
*
|
||||
* @author Hippo
|
||||
*
|
||||
*/
|
||||
public class HfListView extends SwipeRefreshLayout
|
||||
implements AbsListView.OnScrollListener {
|
||||
|
||||
private final static int FOOTER_REFRESHING = 0;
|
||||
private final static int FOOTER_SUCCESS = 1;
|
||||
private final static int FOOTER_FAIL = 2;
|
||||
|
||||
private Context mContext;
|
||||
private FswListView mListView;
|
||||
|
||||
private View mFooter;
|
||||
private TextView mFooterTipTextView;
|
||||
private ProgressBar mFooterProgressBar;
|
||||
private int footerState = FOOTER_SUCCESS;
|
||||
private OnFooterRefreshListener mFooterRefreshListener;
|
||||
|
||||
private OnFitSystemWindowsListener mListener;
|
||||
|
||||
// Footer String to show
|
||||
private String mFooterRefreshStr;
|
||||
private String mFooterSuccessStr;
|
||||
private String mFooterFailStr;
|
||||
|
||||
public HfListView(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
init();
|
||||
}
|
||||
|
||||
public HfListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mListView = new FswListView(mContext);
|
||||
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
addView(mListView, lp);
|
||||
|
||||
mListView.setOnScrollListener(this);
|
||||
mFooter = (LinearLayout)LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.pull_list_view_footer, null);
|
||||
mFooterTipTextView = (TextView)mFooter.findViewById(R.id.footer_tip_text);
|
||||
mFooterProgressBar = (ProgressBar)mFooter.findViewById(R.id.footer_progressBar);
|
||||
mFooter.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!isAnyRefreshing() && footerState == FOOTER_FAIL)
|
||||
footerRefresh();
|
||||
}
|
||||
});
|
||||
changeFooterViewByState();
|
||||
mListView.addFooterView(mFooter);
|
||||
}
|
||||
|
||||
public FswListView getListView() {
|
||||
return mListView;
|
||||
}
|
||||
|
||||
public void setOnFooterRefreshListener(OnFooterRefreshListener l) {
|
||||
mFooterRefreshListener = l;
|
||||
}
|
||||
|
||||
private void footerRefresh() {
|
||||
if (mFooterRefreshListener.onFooterRefresh()) {
|
||||
footerState = FOOTER_REFRESHING;
|
||||
changeFooterViewByState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set footer UI String
|
||||
*
|
||||
* @param refreshStr
|
||||
* @param successStr
|
||||
* @param failStr
|
||||
*/
|
||||
public void setFooterString(String refreshStr, String successStr, String failStr) {
|
||||
mFooterRefreshStr = refreshStr;
|
||||
mFooterSuccessStr = successStr;
|
||||
mFooterFailStr = failStr;
|
||||
|
||||
switch (footerState) {
|
||||
case FOOTER_REFRESHING:
|
||||
mFooterTipTextView.setText(mFooterRefreshStr);
|
||||
break;
|
||||
case FOOTER_SUCCESS:
|
||||
mFooterTipTextView.setText(mFooterSuccessStr);
|
||||
break;
|
||||
case FOOTER_FAIL:
|
||||
mFooterTipTextView.setText(mFooterFailStr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh footer UI
|
||||
*/
|
||||
private void changeFooterViewByState() {
|
||||
switch (footerState) {
|
||||
case FOOTER_REFRESHING:
|
||||
mFooterTipTextView.setText(mFooterRefreshStr);
|
||||
mFooterProgressBar.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
case FOOTER_SUCCESS:
|
||||
mFooterTipTextView.setText(mFooterSuccessStr);
|
||||
mFooterProgressBar.setVisibility(View.GONE);
|
||||
break;
|
||||
case FOOTER_FAIL:
|
||||
mFooterTipTextView.setText(mFooterFailStr);
|
||||
mFooterProgressBar.setVisibility(View.GONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void setHeaderRefreshComplete() {
|
||||
super.setRefreshing(false);
|
||||
}
|
||||
|
||||
public void setFooterRefreshComplete(boolean isSuccess) {
|
||||
if (isSuccess)
|
||||
footerState = FOOTER_SUCCESS;
|
||||
else
|
||||
footerState = FOOTER_FAIL;
|
||||
changeFooterViewByState();
|
||||
}
|
||||
|
||||
public void setAnyRefreshComplete() {
|
||||
setAnyRefreshComplete(true);
|
||||
}
|
||||
|
||||
public void setAnyRefreshComplete(boolean isSuccess) {
|
||||
setHeaderRefreshComplete();
|
||||
setFooterRefreshComplete(isSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if actionbar is refreshing
|
||||
*/
|
||||
public boolean isHeaderRefreshing() {
|
||||
return super.isRefreshing();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if footer is refreshing
|
||||
*/
|
||||
public boolean isFooterRefreshing() {
|
||||
return footerState == FOOTER_REFRESHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if actionbar or footer is refreshing
|
||||
*/
|
||||
public boolean isAnyRefreshing() {
|
||||
return isHeaderRefreshing() | isFooterRefreshing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(AbsListView view, int scrollState) {}
|
||||
|
||||
@Override
|
||||
public void onScroll(AbsListView view, int firstVisibleItem,
|
||||
int visibleItemCount, int totalItemCount) {
|
||||
if (firstVisibleItem + visibleItemCount == totalItemCount
|
||||
&& !isAnyRefreshing() && totalItemCount > 1
|
||||
&& footerState != FOOTER_FAIL)
|
||||
footerRefresh();
|
||||
}
|
||||
|
||||
public interface OnFooterRefreshListener {
|
||||
/**
|
||||
* @return True if this refresh action is vaild
|
||||
*/
|
||||
boolean onFooterRefresh();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user