修改换行符为 LF

This commit is contained in:
seven332 committed 2014-05-15 20:23:11 +08:00
1 parent ed0702068c
commit f20bc2c954
51 files changed
+6729 -6729

No files matched your search

+9 -9
View File
@@ -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
View File
@@ -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>
+17 -17
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>
+306 -306
View File
@@ -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;
}
}
+53 -53
View File
@@ -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();
}
+447 -447
View File
@@ -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;
}
}
+14 -14
View File
@@ -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;
}
+196 -196
View File
@@ -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;
}
}
+35 -35
View File
@@ -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();
}
+21 -21
View File
@@ -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);
}
+60 -60
View File
@@ -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();
}
};
}
}
+268 -268
View File
@@ -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();
}
}
}
}
+119 -119
View File
@@ -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);
}
+201 -201
View File
@@ -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();
}
}