diff --git a/settings.gradle b/settings.gradle index c7290ea6f..c0bb430cf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,9 @@ -include ':twidere', ':twidere.wear', ':twidere.donate.nyanwp', ':twidere.nyan', ':twidere.donate.nyanwp.wear' +include ':twidere' +include ':twidere.wear' +include ':twidere.donate.nyanwp' +include ':twidere.donate.nyanwp.wear' +include ':twidere.component.nyan' +include ':twidere.component.viewer.media' include ':SlidingMenu', ':DragSortListView', ':MenuComponent', ':RefreshNow', ':PullToRefresh', ':MessageBubbleView' project(':SlidingMenu').projectDir = file('libraries/SlidingMenu/library') diff --git a/twidere.nyan/.gitignore b/twidere.component.nyan/.gitignore similarity index 100% rename from twidere.nyan/.gitignore rename to twidere.component.nyan/.gitignore diff --git a/twidere.nyan/build.gradle b/twidere.component.nyan/build.gradle similarity index 100% rename from twidere.nyan/build.gradle rename to twidere.component.nyan/build.gradle diff --git a/twidere.nyan/proguard-rules.pro b/twidere.component.nyan/proguard-rules.pro similarity index 100% rename from twidere.nyan/proguard-rules.pro rename to twidere.component.nyan/proguard-rules.pro diff --git a/twidere.nyan/src/androidTest/java/org/mariotaku/twidere/nyan/ApplicationTest.java b/twidere.component.nyan/src/androidTest/java/org/mariotaku/twidere/nyan/ApplicationTest.java similarity index 100% rename from twidere.nyan/src/androidTest/java/org/mariotaku/twidere/nyan/ApplicationTest.java rename to twidere.component.nyan/src/androidTest/java/org/mariotaku/twidere/nyan/ApplicationTest.java diff --git a/twidere.nyan/src/main/AndroidManifest.xml b/twidere.component.nyan/src/main/AndroidManifest.xml similarity index 100% rename from twidere.nyan/src/main/AndroidManifest.xml rename to twidere.component.nyan/src/main/AndroidManifest.xml diff --git a/twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanConstants.java b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanConstants.java similarity index 100% rename from twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanConstants.java rename to twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanConstants.java diff --git a/twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamService.java b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamService.java similarity index 100% rename from twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamService.java rename to twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamService.java diff --git a/twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamView.java b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamView.java similarity index 100% rename from twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamView.java rename to twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDaydreamView.java diff --git a/twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java similarity index 100% rename from twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java rename to twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanDrawingHelper.java diff --git a/twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanSurfaceHelper.java b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanSurfaceHelper.java similarity index 100% rename from twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanSurfaceHelper.java rename to twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanSurfaceHelper.java diff --git a/twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanWallpaperService.java b/twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanWallpaperService.java similarity index 100% rename from twidere.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanWallpaperService.java rename to twidere.component.nyan/src/main/java/org/mariotaku/twidere/nyan/NyanWallpaperService.java diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame00_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame00_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame00_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame00_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame01_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame01_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame01_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame01_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame02_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame02_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame02_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame02_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame03_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame03_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame03_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame03_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame04_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame04_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame04_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame04_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame05_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame05_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame05_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame05_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame06_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame06_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame06_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame06_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame07_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame07_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame07_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame07_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame08_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame08_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame08_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame08_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame09_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame09_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame09_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame09_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame10_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame10_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame10_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame10_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame11_tile.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame11_tile.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame11_tile.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_rainbow_frame11_tile.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame00.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame00.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame00.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame00.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame01.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame01.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame01.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame01.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame02.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame02.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame02.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame02.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame03.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame03.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame03.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame03.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame04.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame04.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame04.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame04.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame05.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame05.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame05.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame05.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame06.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame06.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame06.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame06.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame07.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame07.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame07.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame07.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame08.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame08.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame08.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame08.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame09.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame09.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame09.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame09.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame10.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame10.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame10.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame10.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame11.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame11.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame11.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_frame11.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame00.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame00.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame00.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame00.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame01.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame01.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame01.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame01.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame02.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame02.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame02.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame02.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame03.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame03.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame03.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame03.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame04.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame04.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame04.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame04.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame05.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame05.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame05.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame05.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame06.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame06.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame06.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame06.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame07.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame07.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame07.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame07.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame08.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame08.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame08.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame08.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame09.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame09.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame09.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame09.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame10.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame10.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame10.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame10.png diff --git a/twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame11.png b/twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame11.png similarity index 100% rename from twidere.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame11.png rename to twidere.component.nyan/src/main/res/drawable-nodpi/nyan_sakamoto_santa_frame11.png diff --git a/twidere.nyan/src/main/res/drawable/nyan_sakamoto.xml b/twidere.component.nyan/src/main/res/drawable/nyan_sakamoto.xml similarity index 100% rename from twidere.nyan/src/main/res/drawable/nyan_sakamoto.xml rename to twidere.component.nyan/src/main/res/drawable/nyan_sakamoto.xml diff --git a/twidere.nyan/src/main/res/drawable/nyan_sakamoto_santa.xml b/twidere.component.nyan/src/main/res/drawable/nyan_sakamoto_santa.xml similarity index 100% rename from twidere.nyan/src/main/res/drawable/nyan_sakamoto_santa.xml rename to twidere.component.nyan/src/main/res/drawable/nyan_sakamoto_santa.xml diff --git a/twidere.nyan/src/main/res/layout/nyan_daydream.xml b/twidere.component.nyan/src/main/res/layout/nyan_daydream.xml similarity index 100% rename from twidere.nyan/src/main/res/layout/nyan_daydream.xml rename to twidere.component.nyan/src/main/res/layout/nyan_daydream.xml diff --git a/twidere.nyan/src/main/res/values-land/integers.xml b/twidere.component.nyan/src/main/res/values-land/integers.xml similarity index 100% rename from twidere.nyan/src/main/res/values-land/integers.xml rename to twidere.component.nyan/src/main/res/values-land/integers.xml diff --git a/twidere.nyan/src/main/res/values-large-land/integers.xml b/twidere.component.nyan/src/main/res/values-large-land/integers.xml similarity index 100% rename from twidere.nyan/src/main/res/values-large-land/integers.xml rename to twidere.component.nyan/src/main/res/values-large-land/integers.xml diff --git a/twidere.nyan/src/main/res/values-large/integers.xml b/twidere.component.nyan/src/main/res/values-large/integers.xml similarity index 100% rename from twidere.nyan/src/main/res/values-large/integers.xml rename to twidere.component.nyan/src/main/res/values-large/integers.xml diff --git a/twidere.nyan/src/main/res/values-xlarge-land/integers.xml b/twidere.component.nyan/src/main/res/values-xlarge-land/integers.xml similarity index 100% rename from twidere.nyan/src/main/res/values-xlarge-land/integers.xml rename to twidere.component.nyan/src/main/res/values-xlarge-land/integers.xml diff --git a/twidere.nyan/src/main/res/values-xlarge/integers.xml b/twidere.component.nyan/src/main/res/values-xlarge/integers.xml similarity index 100% rename from twidere.nyan/src/main/res/values-xlarge/integers.xml rename to twidere.component.nyan/src/main/res/values-xlarge/integers.xml diff --git a/twidere.nyan/src/main/res/values/colors.xml b/twidere.component.nyan/src/main/res/values/colors.xml similarity index 100% rename from twidere.nyan/src/main/res/values/colors.xml rename to twidere.component.nyan/src/main/res/values/colors.xml diff --git a/twidere.nyan/src/main/res/values/integers.xml b/twidere.component.nyan/src/main/res/values/integers.xml similarity index 100% rename from twidere.nyan/src/main/res/values/integers.xml rename to twidere.component.nyan/src/main/res/values/integers.xml diff --git a/twidere.nyan/src/main/res/values/strings.xml b/twidere.component.nyan/src/main/res/values/strings.xml similarity index 100% rename from twidere.nyan/src/main/res/values/strings.xml rename to twidere.component.nyan/src/main/res/values/strings.xml diff --git a/twidere.component.viewer.media/.gitignore b/twidere.component.viewer.media/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/twidere.component.viewer.media/.gitignore @@ -0,0 +1 @@ +/build diff --git a/twidere.component.viewer.media/build.gradle b/twidere.component.viewer.media/build.gradle new file mode 100644 index 000000000..053589028 --- /dev/null +++ b/twidere.component.viewer.media/build.gradle @@ -0,0 +1,43 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + defaultConfig { + minSdkVersion 14 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile 'com.android.support:support-v4:21.0.3' + compile fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/twidere.component.viewer.media/libs/tileviewlib-1.0.13.jar b/twidere.component.viewer.media/libs/tileviewlib-1.0.13.jar new file mode 100644 index 000000000..999ecfd07 Binary files /dev/null and b/twidere.component.viewer.media/libs/tileviewlib-1.0.13.jar differ diff --git a/twidere.component.viewer.media/proguard-rules.pro b/twidere.component.viewer.media/proguard-rules.pro new file mode 100644 index 000000000..ee5b46f04 --- /dev/null +++ b/twidere.component.viewer.media/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/mariotaku/Tools/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/twidere.component.viewer.media/src/androidTest/java/org/mariotaku/twidere/viewer/imageviewer/ApplicationTest.java b/twidere.component.viewer.media/src/androidTest/java/org/mariotaku/twidere/viewer/imageviewer/ApplicationTest.java new file mode 100644 index 000000000..7bdc680b5 --- /dev/null +++ b/twidere.component.viewer.media/src/androidTest/java/org/mariotaku/twidere/viewer/imageviewer/ApplicationTest.java @@ -0,0 +1,32 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.viewer.imageviewer; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/twidere.component.viewer.media/src/main/AndroidManifest.xml b/twidere.component.viewer.media/src/main/AndroidManifest.xml new file mode 100644 index 000000000..1a3692113 --- /dev/null +++ b/twidere.component.viewer.media/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + diff --git a/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/AbsTileDecoder.java b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/AbsTileDecoder.java new file mode 100644 index 000000000..b40e463b1 --- /dev/null +++ b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/AbsTileDecoder.java @@ -0,0 +1,39 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.tileimageview.decoder; + +import android.graphics.BitmapRegionDecoder; + +import com.qozix.tileview.TileView; +import com.qozix.tileview.graphics.BitmapDecoder; + +/** + * Created by mariotaku on 15/1/5. + */ +public abstract class AbsTileDecoder implements BitmapDecoder { + + public abstract void attachToTileView(TileView tileView); + + public abstract boolean isRecycled(); + + public abstract boolean isSameDecoder(BitmapRegionDecoder decoder); + + public abstract void recycle(); +} diff --git a/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/BitmapRegionTileDecoder.java b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/BitmapRegionTileDecoder.java new file mode 100644 index 000000000..2bfe892de --- /dev/null +++ b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/BitmapRegionTileDecoder.java @@ -0,0 +1,188 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.tileimageview.decoder; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapRegionDecoder; +import android.graphics.Rect; + +import com.qozix.tileview.TileView; +import com.qozix.tileview.graphics.BitmapDecoder; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.InputStream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Display an image with {@link com.qozix.tileview.TileView} using {@link android.graphics.BitmapRegionDecoder}
+ *
+ * Usage:
BitmapRegionTileDecoder decoder = new + * BitmapRegionTileDecoder("/path/to/image.jpg", true);
+ * decoder.attachToTileView(tileView);
+ * + * @author mariotaku + */ +@SuppressWarnings("unused") +public final class BitmapRegionTileDecoder extends AbsTileDecoder { + + private static final Pattern PATTERN_TILE_FORMAT = Pattern.compile("(\\d+):(\\d+)\\-(\\d+)", + Pattern.CASE_INSENSITIVE); + + private final BitmapRegionDecoder mDecoder; + private final int mTileSize; + + private final DownSampleDecoder mDownSampleDecoder; + + public BitmapRegionTileDecoder(final BitmapRegionDecoder decoder) { + this(decoder, 128, 512); + } + + /** + * @param decoder {@link android.graphics.BitmapRegionDecoder} instance + * @param tileSize size of each tile + * @param maxDownSampleSize max size of downsample image + */ + public BitmapRegionTileDecoder(final BitmapRegionDecoder decoder, final int tileSize, final int maxDownSampleSize) { + if (decoder == null) throw new NullPointerException(); + mDecoder = decoder; + mTileSize = tileSize; + final int downsampleInSampleSize = nextPowerOfTwo(Math.max(1, Math.max(getWidth(), getHeight()) + / maxDownSampleSize)); + mDownSampleDecoder = new DownSampleDecoder(decoder, downsampleInSampleSize); + } + + public BitmapRegionTileDecoder(final FileDescriptor fd, final boolean isShareable) throws IOException { + this(BitmapRegionDecoder.newInstance(fd, isShareable)); + } + + public BitmapRegionTileDecoder(final InputStream is, final boolean isShareable) throws IOException { + this(BitmapRegionDecoder.newInstance(is, isShareable)); + } + + public BitmapRegionTileDecoder(final String pathName, final boolean isShareable) throws IOException { + this(BitmapRegionDecoder.newInstance(pathName, isShareable)); + } + + @Override + public void attachToTileView(final TileView tileView) { + tileView.resetDetailLevels(); + tileView.setTileDecoder(this); + tileView.setDownsampleDecoder(getDownSampleDecoder()); + final int width = getWidth(), height = getHeight(), tileSize = getTileSize(); + tileView.setSize(width, height); + for (int i = 0, j = Math.max(width, height) / tileSize; i < j; i++) { + final int s = i + 1; + if (isPowerOfTwo(s)) { + tileView.addDetailLevel(1f / s, BitmapRegionTileDecoder.getDecodeName(s), "sample", tileSize, tileSize); + } + } + } + + @Override + public Bitmap decode(final String fileName, final Context context) { + final Matcher m = PATTERN_TILE_FORMAT.matcher(fileName); + if (!m.matches()) return null; + final int inSampleSize = Integer.parseInt(m.group(1)); + final int col = Integer.parseInt(m.group(2)), row = Integer.parseInt(m.group(3)); + final int tileSize = inSampleSize * mTileSize; + final int left = col * tileSize, top = row * tileSize; + final int right = Math.min(left + tileSize, getWidth()); + final int bottom = Math.min(top + tileSize, getHeight()); + final Rect rect = new Rect(left, top, right, bottom); + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = inSampleSize; + return mDecoder.decodeRegion(rect, options); + } + + public BitmapDecoder getDownSampleDecoder() { + return mDownSampleDecoder; + } + + public int getHeight() { + return mDecoder.getHeight(); + } + + public int getTileSize() { + return mTileSize; + } + + public int getWidth() { + return mDecoder.getWidth(); + } + + @Override + public boolean isRecycled() { + return mDecoder.isRecycled(); + } + + @Override + public void recycle() { + mDecoder.recycle(); + } + + public boolean isSameDecoder(final BitmapRegionDecoder decoder) { + return mDecoder.equals(decoder); + } + + public static String getDecodeName(final int inSampleSize) { + return String.format("%d:%s-%s", inSampleSize, "%col%", "%row%"); + } + + private static boolean isPowerOfTwo(final int n) { + return n != 0 && (n & n - 1) == 0; + } + + private static int nextPowerOfTwo(final int i) { + int n = i; + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n; + } + + private static final class DownSampleDecoder implements BitmapDecoder { + + private final BitmapRegionDecoder mDecoder; + private final int mInSampleSize; + + private DownSampleDecoder(final BitmapRegionDecoder decoder, final int inSampleSize) { + mDecoder = decoder; + mInSampleSize = inSampleSize; + } + + @Override + public Bitmap decode(final String fileName, final Context context) { + final Rect rect = new Rect(0, 0, mDecoder.getWidth(), mDecoder.getHeight()); + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = mInSampleSize; + return mDecoder.decodeRegion(rect, options); + } + + } + +} \ No newline at end of file diff --git a/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/DummyTileDecoder.java b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/DummyTileDecoder.java new file mode 100644 index 000000000..97435340b --- /dev/null +++ b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/decoder/DummyTileDecoder.java @@ -0,0 +1,71 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.tileimageview.decoder; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapRegionDecoder; + +import com.qozix.tileview.TileView; + +public final class DummyTileDecoder extends AbsTileDecoder { + + private final Bitmap mFallback; + + public DummyTileDecoder(Bitmap fallback) { + mFallback = fallback; + } + + @Override + public void attachToTileView(final TileView tileView) { + tileView.setTileDecoder(this); + tileView.setDownsampleDecoder(this); + tileView.resetDetailLevels(); + if (mFallback != null) { + final int width = mFallback.getWidth(), height = mFallback.getHeight(); + tileView.setSize(width, height); + tileView.addDetailLevel(1, "", "sample", width, height); + } else { + tileView.setSize(0, 0); + } + } + + @Override + public Bitmap decode(final String fileName, final Context context) { + return null; + } + + @Override + public boolean isRecycled() { + return mFallback != null && mFallback.isRecycled(); + } + + @Override + public boolean isSameDecoder(BitmapRegionDecoder decoder) { + return false; + } + + @Override + public void recycle() { + if (mFallback != null) { + mFallback.recycle(); + } + } +} \ No newline at end of file diff --git a/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/widget/TileImageView.java b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/widget/TileImageView.java new file mode 100644 index 000000000..2fd6d31f8 --- /dev/null +++ b/twidere.component.viewer.media/src/main/java/org/mariotaku/tileimageview/widget/TileImageView.java @@ -0,0 +1,282 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.tileimageview.widget; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapRegionDecoder; +import android.graphics.Point; +import android.os.Build; +import android.support.annotation.NonNull; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.qozix.layouts.ZoomPanLayout.GestureListener; +import com.qozix.layouts.ZoomPanLayout.ZoomPanListener; +import com.qozix.tileview.TileView; +import com.qozix.tileview.TileView.TileViewEventListener; +import com.qozix.tileview.hotspots.HotSpot; +import com.qozix.tileview.hotspots.HotSpotEventListener; +import com.qozix.tileview.markers.MarkerEventListener; + +import org.mariotaku.tileimageview.decoder.AbsTileDecoder; +import org.mariotaku.tileimageview.decoder.BitmapRegionTileDecoder; +import org.mariotaku.tileimageview.decoder.DummyTileDecoder; + +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings("unused") +public final class TileImageView extends FrameLayout { + + private final TileView mTileView; + private AbsTileDecoder mTileDecoder; + + public TileImageView(final Context context) { + this(context, null); + } + + public TileImageView(final Context context, final AttributeSet attrs) { + this(context, attrs, 0); + } + + public TileImageView(final Context context, final AttributeSet attrs, final int defStyle) { + super(context, attrs, defStyle); + mTileView = new TileView(context); + super.addView(mTileView, 0, generateDefaultLayoutParams()); + } + + public View addCallout(final View view, final double x, final double y) { + return mTileView.addCallout(view, x, y); + } + + public View addCallout(final View view, final double x, final double y, final float anchorX, final float anchorY) { + return mTileView.addCallout(view, x, y, anchorX, anchorY); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + @Override + public void addChildrenForAccessibility(@NonNull final ArrayList childrenForAccessibility) { + mTileView.addChildrenForAccessibility(childrenForAccessibility); + } + + @Override + public void addFocusables(final ArrayList views, final int direction) { + mTileView.addFocusables(views, direction); + } + + @Override + public void addFocusables(@NonNull final ArrayList views, final int direction, final int focusableMode) { + mTileView.addFocusables(views, direction, focusableMode); + } + + public boolean addGestureListener(final GestureListener listener) { + return mTileView.addGestureListener(listener); + } + + public HotSpot addHotSpot(final HotSpot hotSpot) { + return mTileView.addHotSpot(hotSpot); + } + + public HotSpot addHotSpot(final List positions) { + return mTileView.addHotSpot(positions); + } + + public HotSpot addHotSpot(final List positions, final HotSpotEventListener listener) { + return mTileView.addHotSpot(positions, listener); + } + + public void addHotSpotEventListener(final HotSpotEventListener listener) { + mTileView.addHotSpotEventListener(listener); + } + + public View addMarker(final View view, final double x, final double y) { + return mTileView.addMarker(view, x, y); + } + + public View addMarker(final View view, final double x, final double y, final float anchorX, final float anchorY) { + return mTileView.addMarker(view, x, y, anchorX, anchorY); + } + + public void addMarkerEventListener(final MarkerEventListener listener) { + mTileView.addMarkerEventListener(listener); + } + + public void addTileViewEventListener(final TileViewEventListener listener) { + mTileView.addTileViewEventListener(listener); + } + + @Override + public void addView(@NonNull final View child) { + throw new UnsupportedOperationException(); + } + + @Override + public void addView(@NonNull final View child, final int index) { + throw new UnsupportedOperationException(); + } + + @Override + public void addView(@NonNull final View child, final int width, final int height) { + throw new UnsupportedOperationException(); + } + + @Override + public void addView(@NonNull final View child, final int index, final ViewGroup.LayoutParams params) { + throw new UnsupportedOperationException(); + } + + @Override + public void addView(@NonNull final View child, final ViewGroup.LayoutParams params) { + throw new UnsupportedOperationException(); + } + + public boolean addZoomPanListener(final ZoomPanListener listener) { + return mTileView.addZoomPanListener(listener); + } + + public void cancelRender() { + mTileView.cancelRender(); + } + + @Override + public boolean canScrollHorizontally(final int direction) { + final double scaledBoundaryX = mTileView.getScaledWidth(); + final int scrollX = mTileView.getScrollX(); + return scrollX + direction > 0 && scrollX + mTileView.getWidth() + direction < scaledBoundaryX; + } + + @Override + public boolean canScrollVertically(final int direction) { + final double scaledBoundaryY = mTileView.getScaledHeight(); + final int scrollY = mTileView.getScrollY(); + return scrollY + direction > 0 && scrollY + mTileView.getHeight() + direction < scaledBoundaryY; + } + + @Override + public boolean dispatchTouchEvent(@NonNull final MotionEvent ev) { + return mTileView.dispatchTouchEvent(ev); + } + + public int getBaseHeight() { + return mTileView.getBaseHeight(); + } + + public int getBaseWidth() { + return mTileView.getBaseWidth(); + } + + public Point getCenter() { + return new Point(mTileView.getScrollX(), mTileView.getScrollY()); + } + + public double getScale() { + return mTileView.getScale(); + } + + public boolean removeGestureListener(final GestureListener listener) { + return mTileView.removeGestureListener(listener); + } + + public void removeHotSpot(final HotSpot hotSpot) { + mTileView.removeHotSpot(hotSpot); + } + + public void removeHotSpotEventListener(final HotSpotEventListener listener) { + mTileView.removeHotSpotEventListener(listener); + } + + public void removeMarkerEventListener(final MarkerEventListener listener) { + mTileView.removeMarkerEventListener(listener); + } + + public void requestRender() { + mTileView.requestRender(); + } + + public void resume() { + mTileView.resume(); + } + + public void scrollToAndCenter(final Point point) { + mTileView.scrollToAndCenter(point); + } + + public void scrollToPoint(final Point point) { + mTileView.scrollToPoint(point); + } + + public void setBitmapRegionDecoder(final BitmapRegionDecoder decoder, final Bitmap fallback) { + if (mTileDecoder != null && mTileDecoder.isSameDecoder(decoder)) return; + if (mTileDecoder != null) { + mTileDecoder.recycle(); + mTileDecoder = null; + } + if (decoder == null) { + mTileDecoder = new DummyTileDecoder(fallback); + } else { + mTileDecoder = new BitmapRegionTileDecoder(decoder); + } + mTileDecoder.attachToTileView(mTileView); + } + + public void setDragStartThreshold(final int threshold) { + mTileView.setDragStartThreshold(threshold); + } + + public void setScale(final double d) { + mTileView.setScale(d); + } + + public void setScaleLimits(final double min, final double max) { + mTileView.setScaleLimits(min, max); + } + + public void setScaleToFit(final boolean shouldScaleToFit) { + mTileView.setScaleToFit(shouldScaleToFit); + } + + public void slideToAndCenter(final double x, final double y) { + mTileView.slideToAndCenter(x, y); + } + + public void slideToAndCenter(final Point point) { + mTileView.slideToAndCenter(point); + } + + public void slideToPoint(final Point point) { + mTileView.slideToPoint(point); + } + + public void smoothScaleTo(final double destination, final int duration) { + mTileView.smoothScaleTo(destination, duration); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER); + } + +} \ No newline at end of file diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAboutMeAdapter.java b/twidere.component.viewer.media/src/main/java/org/mariotaku/twidere/viewer/media/MediaViewerActivity.java similarity index 73% rename from twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAboutMeAdapter.java rename to twidere.component.viewer.media/src/main/java/org/mariotaku/twidere/viewer/media/MediaViewerActivity.java index d14162a69..e32e4617d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAboutMeAdapter.java +++ b/twidere.component.viewer.media/src/main/java/org/mariotaku/twidere/viewer/media/MediaViewerActivity.java @@ -17,15 +17,13 @@ * along with this program. If not, see . */ -package org.mariotaku.twidere.adapter; +package org.mariotaku.twidere.viewer.media; -import android.content.Context; +import android.app.Activity; +import android.support.v4.app.FragmentActivity; /** - * Created by mariotaku on 15/1/3. + * Created by mariotaku on 15/1/5. */ -public class ParcelableActivitiesAboutMeAdapter extends ParcelableActivitiesAdapter { - protected ParcelableActivitiesAboutMeAdapter(Context context) { - super(context); - } +public class MediaViewerActivity extends FragmentActivity { } diff --git a/twidere.component.viewer.media/src/main/res/values/strings.xml b/twidere.component.viewer.media/src/main/res/values/strings.xml new file mode 100644 index 000000000..c124e77df --- /dev/null +++ b/twidere.component.viewer.media/src/main/res/values/strings.xml @@ -0,0 +1,21 @@ + + + + diff --git a/twidere.donate.nyanwp.wear/build.gradle b/twidere.donate.nyanwp.wear/build.gradle index 6b62cddf7..f22318b42 100644 --- a/twidere.donate.nyanwp.wear/build.gradle +++ b/twidere.donate.nyanwp.wear/build.gradle @@ -74,7 +74,7 @@ android { } dependencies { - compile project(':twidere.nyan') - compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.android.support:wearable:1.1.0' + compile project(':twidere.component.nyan') + compile fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/twidere.donate.nyanwp/build.gradle b/twidere.donate.nyanwp/build.gradle index e2f3df84f..ea2e30beb 100644 --- a/twidere.donate.nyanwp/build.gradle +++ b/twidere.donate.nyanwp/build.gradle @@ -74,6 +74,6 @@ android { dependencies { wearApp project(':twidere.donate.nyanwp.wear') - compile project(':twidere.nyan') + compile project(':twidere.component.nyan') compile fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/twidere/build.gradle b/twidere/build.gradle index 5ed7d9b0a..c5f9f3195 100644 --- a/twidere/build.gradle +++ b/twidere/build.gradle @@ -104,11 +104,12 @@ dependencies { googleCompile 'com.google.android.gms:play-services:6.5.87' fdroidCompile 'org.osmdroid:osmdroid-android:4.2' fdroidCompile 'org.slf4j:slf4j-simple:1.7.9' + compile project(':twidere.component.nyan') + compile project(':twidere.component.viewer.media') compile project(':SlidingMenu') compile project(':DragSortListView') compile project(':MenuComponent') compile project(':MessageBubbleView') - compile project(':twidere.nyan') compile fileTree(dir: 'libs/main', include: ['*.jar']) googleCompile fileTree(dir: 'libs/google', include: ['*.jar']) } diff --git a/twidere/src/main/AndroidManifest.xml b/twidere/src/main/AndroidManifest.xml index d1835c4c2..9dbb3c06d 100644 --- a/twidere/src/main/AndroidManifest.xml +++ b/twidere/src/main/AndroidManifest.xml @@ -169,6 +169,17 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activity.support.HomeActivity"/> + + + + + + diff --git a/twidere/src/main/java/org/mariotaku/gallery3d/ImageViewerGLActivity.java b/twidere/src/main/java/org/mariotaku/gallery3d/ImageViewerGLActivityOld.java similarity index 91% rename from twidere/src/main/java/org/mariotaku/gallery3d/ImageViewerGLActivity.java rename to twidere/src/main/java/org/mariotaku/gallery3d/ImageViewerGLActivityOld.java index 7b2905f79..1fef8f970 100644 --- a/twidere/src/main/java/org/mariotaku/gallery3d/ImageViewerGLActivity.java +++ b/twidere/src/main/java/org/mariotaku/gallery3d/ImageViewerGLActivityOld.java @@ -1,17 +1,20 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Twidere - Twitter client for Android * - * 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 + * Copyright (C) 2012-2015 Mariotaku Lee * - * http://www.apache.org/licenses/LICENSE-2.0 + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ package org.mariotaku.gallery3d; @@ -49,14 +52,15 @@ import org.mariotaku.menucomponent.widget.MenuBar.MenuBarListener; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.support.BaseSupportActivity; +import org.mariotaku.twidere.loader.support.TileImageLoader; import org.mariotaku.twidere.util.SaveImageTask; import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.Utils; import java.io.File; -public final class ImageViewerGLActivity extends BaseSupportActivity implements Constants, PhotoView.Listener, - GLImageLoader.DownloadListener, LoaderManager.LoaderCallbacks, OnMenuVisibilityListener, +public final class ImageViewerGLActivityOld extends BaseSupportActivity implements Constants, PhotoView.Listener, + TileImageLoader.DownloadListener, LoaderManager.LoaderCallbacks, OnMenuVisibilityListener, MenuBarListener { private final GLView mRootPane = new GLView() { @@ -139,13 +143,13 @@ public final class ImageViewerGLActivity extends BaseSupportActivity implements } @Override - public Loader onCreateLoader(final int id, final Bundle args) { + public Loader onCreateLoader(final int id, final Bundle args) { mProgress.setVisibility(View.VISIBLE); mProgress.setIndeterminate(true); invalidateOptionsMenu(); final Uri uri = args.getParcelable(EXTRA_URI); final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); - return new GLImageLoader(this, this, accountId, uri); + return new TileImageLoader(this, this, accountId, uri); } @Override @@ -178,12 +182,12 @@ public final class ImageViewerGLActivity extends BaseSupportActivity implements @Override - public void onLoaderReset(final Loader loader) { + public void onLoaderReset(final Loader loader) { } @Override - public void onLoadFinished(final Loader loader, final GLImageLoader.Result data) { + public void onLoadFinished(final Loader loader, final TileImageLoader.Result data) { if (data != null && (data.decoder != null || data.bitmap != null)) { if (data.decoder != null) { mGLRootView.setVisibility(View.VISIBLE); @@ -525,9 +529,9 @@ public final class ImageViewerGLActivity extends BaseSupportActivity implements } private static class MyHandler extends SynchronizedHandler { - ImageViewerGLActivity activity; + ImageViewerGLActivityOld activity; - private MyHandler(final ImageViewerGLActivity activity) { + private MyHandler(final ImageViewerGLActivityOld activity) { super(activity.getGLRoot()); this.activity = activity; } diff --git a/twidere/src/main/java/org/mariotaku/gallery3d/ui/BasicTexture.java b/twidere/src/main/java/org/mariotaku/gallery3d/ui/BasicTexture.java index 0c1dc81a6..55e796ed6 100644 --- a/twidere/src/main/java/org/mariotaku/gallery3d/ui/BasicTexture.java +++ b/twidere/src/main/java/org/mariotaku/gallery3d/ui/BasicTexture.java @@ -18,7 +18,7 @@ package org.mariotaku.gallery3d.ui; import android.util.Log; -import org.mariotaku.gallery3d.util.GalleryUtils; +import org.mariotaku.twidere.util.MathUtils; import java.util.WeakHashMap; @@ -153,8 +153,8 @@ abstract class BasicTexture implements Texture { protected void setSize(final int width, final int height) { mWidth = width; mHeight = height; - mTextureWidth = GalleryUtils.nextPowerOf2(width); - mTextureHeight = GalleryUtils.nextPowerOf2(height); + mTextureWidth = MathUtils.nextPowerOf2(width); + mTextureHeight = MathUtils.nextPowerOf2(height); 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()); } diff --git a/twidere/src/main/java/org/mariotaku/gallery3d/ui/PhotoView.java b/twidere/src/main/java/org/mariotaku/gallery3d/ui/PhotoView.java index 0ce930e7c..ff17798cc 100644 --- a/twidere/src/main/java/org/mariotaku/gallery3d/ui/PhotoView.java +++ b/twidere/src/main/java/org/mariotaku/gallery3d/ui/PhotoView.java @@ -25,568 +25,568 @@ import android.os.Build; import android.os.Message; import android.view.MotionEvent; -import org.mariotaku.gallery3d.ImageViewerGLActivity; +import org.mariotaku.gallery3d.ImageViewerGLActivityOld; import org.mariotaku.gallery3d.util.BitmapPool; public class PhotoView extends GLView { - private static final int MSG_CANCEL_EXTRA_SCALING = 2; - private static final int MSG_CAPTURE_ANIMATION_DONE = 4; - - private static final int HOLD_TOUCH_DOWN = 1; - private static final int HOLD_CAPTURE_ANIMATION = 2; - - private final GestureListener mGestureListener; - - private final GestureRecognizer mGestureRecognizer; - private final PositionController mPositionController; - - private Listener mListener; - private ITileImageAdapter mModel; - - private final TileImageView mTileView; - private final EdgeView mEdgeView; - private final SynchronizedHandler mHandler; - private boolean mCancelExtraScalingPending; - private boolean mWantPictureCenterCallbacks = false; - - private int mDisplayRotation = 0; - - private int mCompensation = 0; - // This variable prevents us doing snapback until its values goes to 0. This - // happens if the user gesture is still in progress or we are in a capture - // animation. - private int mHolding; - - // This is the index of the last deleted item. This is only used as a hint - // to hide the undo button when we are too far away from the deleted - // item. The value Integer.MAX_VALUE means there is no such hint. - private final Context mContext; - private final FullPicture mPicture; - - public PhotoView(final ImageViewerGLActivity activity) { - mTileView = new TileImageView(activity); - addComponent(mTileView); - mContext = activity; - mEdgeView = new EdgeView(mContext); - addComponent(mEdgeView); - mHandler = new MyHandler(activity); - mPicture = new FullPicture(); - mGestureListener = new GestureListener(); - mGestureRecognizer = new GestureRecognizer(mContext, mGestureListener); - mPositionController = new PositionController(new EventListener()); - } - - public Rect getPhotoRect() { - return mPositionController.getPosition(); - } - - public void notifyImageChange() { - mListener.onCurrentImageUpdated(); - mPicture.reload(); - setPictureSize(); - invalidate(); - } - - public void pause() { - mPositionController.skipAnimation(); - mTileView.freeTextures(); - for (int i = -0; i <= 0; i++) { - mPicture.setScreenNail(null); - } - } - - public void resume() { - mTileView.prepareTextures(); - mPositionController.skipToFinalPosition(); - } - - public void setListener(final Listener listener) { - mListener = listener; - } - - public void setModel(final ITileImageAdapter model) { - mModel = model; - mTileView.setModel(mModel); - } - - public void setOpenAnimationRect(final Rect rect) { - mPositionController.setOpenAnimationRect(rect); - } - - public void setWantPictureCenterCallbacks(final boolean wanted) { - mWantPictureCenterCallbacks = wanted; - } - - @Override - protected void onLayout(final boolean changeSize, final int left, final int top, final int right, final int bottom) { - final int w = right - left; - final int h = bottom - top; - mTileView.layout(0, 0, w, h); - mEdgeView.layout(0, 0, w, h); - - final GLRoot root = getGLRoot(); - final int displayRotation = root.getDisplayRotation(); - final int compensation = root.getCompensation(); - if (mDisplayRotation != displayRotation || mCompensation != compensation) { - mDisplayRotation = displayRotation; - mCompensation = compensation; - } - - if (changeSize) { - mPositionController.setViewSize(getWidth(), getHeight()); - } - } - - // ////////////////////////////////////////////////////////////////////////// - // Pictures - // ////////////////////////////////////////////////////////////////////////// - - @Override - protected boolean onTouch(final MotionEvent event) { - mGestureRecognizer.onTouchEvent(event); - return true; - } - - @Override - protected void render(final GLCanvas canvas) { - - // Draw photos from back to front - final Rect r = mPositionController.getPosition(); - mPicture.draw(canvas, r); - - renderChild(canvas, mEdgeView); - - mPositionController.advanceAnimation(); - } - - private void captureAnimationDone(final int offset) { - mHolding &= ~HOLD_CAPTURE_ANIMATION; - if (offset == 1) { - // Now the capture animation is done, enable the action bar. - mListener.onActionBarAllowed(true); - mListener.onActionBarWanted(); - } - snapback(); - } - - private void setPictureSize() { - mPositionController.setImageSize(mPicture.getSize(), null); - } - - private void snapback() { - mPositionController.snapback(); - } - - // ////////////////////////////////////////////////////////////////////////// - // Film mode focus switching - // ////////////////////////////////////////////////////////////////////////// - - private static int getRotated(final int degree, final int original, final int theother) { - return degree % 180 == 0 ? original : theother; - } - - public interface ITileImageAdapter { - - public int getImageHeight(); - - // Returns the rotation for the specified picture. - public int getImageRotation(); - - public int getImageWidth(); - - public int getLevelCount(); - - public ScreenNail getScreenNail(); - - /** - * The tile returned by this method can be specified this way: Assuming - * the image size is (width, height), first take the intersection of (0, - * 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). Then - * extend this intersection region by borderSize pixels on each side. If - * in extending the region, we found some part of the region are outside - * the image, those pixels are filled with black.
- *
- * If level > 0, it does the same operation on a down-scaled version of - * the original image (down-scaled by a factor of 2^level), but (x, y) - * still refers to the coordinate on the original image.
- *
- * The method would be called in another thread. - */ - public Bitmap getTile(int level, int x, int y, int tileSize, int borderSize, BitmapPool pool); - - public void recycleScreenNail(); - - public boolean setData(BitmapRegionDecoder decoder, Bitmap bitmap, int orientation); - - } - - public interface Listener { - public void onActionBarAllowed(boolean allowed); - - public void onActionBarWanted(); - - public void onCurrentImageUpdated(); - - public void onPictureCenter(); - - public void onSingleTapUp(int x, int y); - } - - public static class Size { - public int width; - public int height; - } - - private class GestureListener implements GestureRecognizer.Listener { - private boolean mIgnoreUpEvent = false; - // If we can change mode for this scale gesture. - private boolean mCanChangeMode; - // If we have changed the film mode in this scaling gesture. - private boolean mModeChanged; - // If a scrolling has happened after a down gesture. - private boolean mScrolledAfterDown; - // The accumulated scaling change from a scaling gesture. - private float mAccScale; - - @Override - public boolean onDoubleTap(final float x, final float y) { - final PositionController controller = mPositionController; - final float scale = controller.getImageScale(); - // onDoubleTap happened on the second ACTION_DOWN. - // We need to ignore the next UP event. - mIgnoreUpEvent = true; - if (scale <= .75f || controller.isAtMinimalScale()) { - controller.zoomIn(x, y, Math.max(1.0f, scale * 1.5f)); - } else { - controller.resetToFullView(); - } - return true; - } - - @Override - public void onDown(final float x, final float y) { - - mModeChanged = false; - - mHolding |= HOLD_TOUCH_DOWN; - - mScrolledAfterDown = false; - } - - @Override - public boolean onFling(final float velocityX, final float velocityY) { - if (mModeChanged) return true; - flingImages(velocityX, velocityY); - return true; - } - - @Override - public boolean onScale(final float focusX, final float focusY, final float scale) { - if (mModeChanged) return true; - if (Float.isNaN(scale) || Float.isInfinite(scale)) return false; - - final int outOfRange = mPositionController.scaleBy(scale, focusX, focusY); - - // We wait for a large enough scale change before changing mode. - // Otherwise we may mistakenly treat a zoom-in gesture as zoom-out - // or vice versa. - mAccScale *= scale; - final boolean largeEnough = mAccScale < 0.97f || mAccScale > 1.03f; - - // If mode changes, we treat this scaling gesture has ended. - if (mCanChangeMode && largeEnough) { - if (outOfRange < 0 || outOfRange > 0) { - stopExtraScalingIfNeeded(); - - // Removing the touch down flag allows snapback to happen - // for film mode change. - mHolding &= ~HOLD_TOUCH_DOWN; - - // We need to call onScaleEnd() before setting mModeChanged - // to true. - onScaleEnd(); - mModeChanged = true; - return true; - } - } - - if (outOfRange != 0) { - startExtraScalingIfNeeded(); - } else { - stopExtraScalingIfNeeded(); - } - return true; - } - - @Override - public boolean onScaleBegin(final float focusX, final float focusY) { - // We ignore the scaling gesture if it is a camera preview. - mPositionController.beginScale(focusX, focusY); - // We can change mode if we are in film mode, or we are in page - // mode and at minimal scale. - mCanChangeMode = mPositionController.isAtMinimalScale(); - mAccScale = 1f; - return true; - } - - @Override - public void onScaleEnd() { - if (mModeChanged) return; - mPositionController.endScale(); - } - - @Override - public boolean onScroll(final float dx, final float dy, final float totalX, final float totalY) { - if (!mScrolledAfterDown) { - mScrolledAfterDown = true; - } - - final int dxi = (int) (-dx + 0.5f); - final int dyi = (int) (-dy + 0.5f); - mPositionController.scrollPage(dxi, dyi); - return true; - } - - @Override - public boolean onSingleTapUp(final float x, final float y) { - // On crespo running Android 2.3.6 (gingerbread), a pinch out - // gesture results in the - // following call sequence: onDown(), onUp() and then - // onSingleTapUp(). The correct - // sequence for a single-tap-up gesture should be: onDown(), - // onSingleTapUp() and onUp(). - // The call sequence for a pinch out gesture in JB is: onDown(), - // then onUp() and there's - // no onSingleTapUp(). Base on these observations, the following - // condition is added to - // filter out the false alarm where onSingleTapUp() is called within - // a pinch out - // gesture. The framework fix went into ICS. Refer to b/4588114. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - if ((mHolding & HOLD_TOUCH_DOWN) == 0) return true; - } - - // We do this in addition to onUp() because we want the snapback of - // setFilmMode to happen. - mHolding &= ~HOLD_TOUCH_DOWN; - - if (mListener != null) { - // Do the inverse transform of the touch coordinates. - final Matrix m = getGLRoot().getCompensationMatrix(); - final Matrix inv = new Matrix(); - m.invert(inv); - final float[] pts = new float[] { x, y }; - inv.mapPoints(pts); - mListener.onSingleTapUp((int) (pts[0] + 0.5f), (int) (pts[1] + 0.5f)); - } - return true; - } - - @Override - public void onUp() { - mHolding &= ~HOLD_TOUCH_DOWN; - mEdgeView.onRelease(); - - if (mIgnoreUpEvent) { - mIgnoreUpEvent = false; - return; - } - - snapback(); - } - - private boolean flingImages(final float velocityX, final float velocityY) { - final int vx = (int) (velocityX + 0.5f); - final int vy = (int) (velocityY + 0.5f); - return mPositionController.flingPage(vx, vy); - } - - private void startExtraScalingIfNeeded() { - if (!mCancelExtraScalingPending) { - mHandler.sendEmptyMessageDelayed(MSG_CANCEL_EXTRA_SCALING, 700); - mPositionController.setExtraScalingRange(true); - mCancelExtraScalingPending = true; - } - } - - private void stopExtraScalingIfNeeded() { - if (mCancelExtraScalingPending) { - mHandler.removeMessages(MSG_CANCEL_EXTRA_SCALING); - mPositionController.setExtraScalingRange(false); - mCancelExtraScalingPending = false; - } - } - } - - private class MyHandler extends SynchronizedHandler { - - private MyHandler(final ImageViewerGLActivity activity) { - super(activity.getGLRoot()); - } - - @Override - public void handleMessage(final Message message) { - switch (message.what) { - case MSG_CANCEL_EXTRA_SCALING: { - mGestureRecognizer.cancelScale(); - mPositionController.setExtraScalingRange(false); - mCancelExtraScalingPending = false; - break; - } - case MSG_CAPTURE_ANIMATION_DONE: { - // message.arg1 is the offset parameter passed to - // switchWithCaptureAnimation(). - captureAnimationDone(message.arg1); - break; - } - default: - throw new AssertionError(message.what); - } - } - } - - private interface Picture { - void draw(GLCanvas canvas, Rect r); - - void forceSize(); // called when mCompensation changes - - Size getSize(); - - void reload(); - - void setScreenNail(ScreenNail s); - } - - class EventListener implements PositionController.Listener { - - @Override - public boolean isHoldingDown() { - return (mHolding & HOLD_TOUCH_DOWN) != 0; - } - - @Override - public void onAbsorb(final int velocity, final int direction) { - mEdgeView.onAbsorb(velocity, direction); - } - - @Override - public void onInvalidate() { - invalidate(); - } - - @Override - public void onPull(final int offset, final int direction) { - mEdgeView.onPull(offset, direction); - } - } - - class FullPicture implements Picture { - private int mRotation; - private final Size mSize = new Size(); - - @Override - public void draw(final GLCanvas canvas, final Rect r) { - drawTileView(canvas, r); - - // We want to have the following transitions: - // (1) Move camera preview out of its place: switch to film mode - // (2) Move camera preview into its place: switch to page mode - // The extra mWasCenter check makes sure (1) does not apply if in - // page mode, we move _to_ the camera preview from another picture. - - // Holdings except touch-down prevent the transitions. - if ((mHolding & ~HOLD_TOUCH_DOWN) != 0) return; - - if (mWantPictureCenterCallbacks && mPositionController.isCenter()) { - mListener.onPictureCenter(); - } - } - - @Override - public void forceSize() { - updateSize(); - mPositionController.forceImageSize(mSize); - } - - @Override - public Size getSize() { - return mSize; - } - - @Override - public void reload() { - // mImageWidth and mImageHeight will get updated - mTileView.notifyModelInvalidated(); - setScreenNail(mModel.getScreenNail()); - updateSize(); - } - - @Override - public void setScreenNail(final ScreenNail s) { - mTileView.setScreenNail(s); - } - - private void drawTileView(final GLCanvas canvas, final Rect r) { - final float imageScale = mPositionController.getImageScale(); - final int viewW = getWidth(); - final int viewH = getHeight(); - final float cx = r.exactCenterX(); - final float cy = r.exactCenterY(); - - canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA); - // Draw the tile view. - setTileViewPosition(cx, cy, viewW, viewH, imageScale); - renderChild(canvas, mTileView); - - // Draw the play video icon and the message. - canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f)); - - canvas.restore(); - } - - // Set the position of the tile view - private void setTileViewPosition(final float cx, final float cy, final int viewW, final int viewH, - final float scale) { - // Find out the bitmap coordinates of the center of the view - final int imageW = mPositionController.getImageWidth(); - final int imageH = mPositionController.getImageHeight(); - final int centerX = (int) (imageW / 2f + (viewW / 2f - cx) / scale + 0.5f); - final int centerY = (int) (imageH / 2f + (viewH / 2f - cy) / scale + 0.5f); - - final int inverseX = imageW - centerX; - final int inverseY = imageH - centerY; - int x, y; - switch (mRotation) { - case 0: - x = centerX; - y = centerY; - break; - case 90: - x = centerY; - y = inverseX; - break; - case 180: - x = inverseX; - y = inverseY; - break; - case 270: - x = inverseY; - y = centerX; - break; - default: - throw new RuntimeException(String.valueOf(mRotation)); - } - mTileView.setPosition(x, y, scale, mRotation); - } - - private void updateSize() { - mRotation = mModel.getImageRotation(); - - final int w = mTileView.mImageWidth; - final int h = mTileView.mImageHeight; - mSize.width = getRotated(mRotation, w, h); - mSize.height = getRotated(mRotation, h, w); - } - } + private static final int MSG_CANCEL_EXTRA_SCALING = 2; + private static final int MSG_CAPTURE_ANIMATION_DONE = 4; + + private static final int HOLD_TOUCH_DOWN = 1; + private static final int HOLD_CAPTURE_ANIMATION = 2; + + private final GestureListener mGestureListener; + + private final GestureRecognizer mGestureRecognizer; + private final PositionController mPositionController; + + private Listener mListener; + private ITileImageAdapter mModel; + + private final TileImageView mTileView; + private final EdgeView mEdgeView; + private final SynchronizedHandler mHandler; + private boolean mCancelExtraScalingPending; + private boolean mWantPictureCenterCallbacks = false; + + private int mDisplayRotation = 0; + + private int mCompensation = 0; + // This variable prevents us doing snapback until its values goes to 0. This + // happens if the user gesture is still in progress or we are in a capture + // animation. + private int mHolding; + + // This is the index of the last deleted item. This is only used as a hint + // to hide the undo button when we are too far away from the deleted + // item. The value Integer.MAX_VALUE means there is no such hint. + private final Context mContext; + private final FullPicture mPicture; + + public PhotoView(final ImageViewerGLActivityOld activity) { + mTileView = new TileImageView(activity); + addComponent(mTileView); + mContext = activity; + mEdgeView = new EdgeView(mContext); + addComponent(mEdgeView); + mHandler = new MyHandler(activity); + mPicture = new FullPicture(); + mGestureListener = new GestureListener(); + mGestureRecognizer = new GestureRecognizer(mContext, mGestureListener); + mPositionController = new PositionController(new EventListener()); + } + + public Rect getPhotoRect() { + return mPositionController.getPosition(); + } + + public void notifyImageChange() { + mListener.onCurrentImageUpdated(); + mPicture.reload(); + setPictureSize(); + invalidate(); + } + + public void pause() { + mPositionController.skipAnimation(); + mTileView.freeTextures(); + for (int i = -0; i <= 0; i++) { + mPicture.setScreenNail(null); + } + } + + public void resume() { + mTileView.prepareTextures(); + mPositionController.skipToFinalPosition(); + } + + public void setListener(final Listener listener) { + mListener = listener; + } + + public void setModel(final ITileImageAdapter model) { + mModel = model; + mTileView.setModel(mModel); + } + + public void setOpenAnimationRect(final Rect rect) { + mPositionController.setOpenAnimationRect(rect); + } + + public void setWantPictureCenterCallbacks(final boolean wanted) { + mWantPictureCenterCallbacks = wanted; + } + + @Override + protected void onLayout(final boolean changeSize, final int left, final int top, final int right, final int bottom) { + final int w = right - left; + final int h = bottom - top; + mTileView.layout(0, 0, w, h); + mEdgeView.layout(0, 0, w, h); + + final GLRoot root = getGLRoot(); + final int displayRotation = root.getDisplayRotation(); + final int compensation = root.getCompensation(); + if (mDisplayRotation != displayRotation || mCompensation != compensation) { + mDisplayRotation = displayRotation; + mCompensation = compensation; + } + + if (changeSize) { + mPositionController.setViewSize(getWidth(), getHeight()); + } + } + + // ////////////////////////////////////////////////////////////////////////// + // Pictures + // ////////////////////////////////////////////////////////////////////////// + + @Override + protected boolean onTouch(final MotionEvent event) { + mGestureRecognizer.onTouchEvent(event); + return true; + } + + @Override + protected void render(final GLCanvas canvas) { + + // Draw photos from back to front + final Rect r = mPositionController.getPosition(); + mPicture.draw(canvas, r); + + renderChild(canvas, mEdgeView); + + mPositionController.advanceAnimation(); + } + + private void captureAnimationDone(final int offset) { + mHolding &= ~HOLD_CAPTURE_ANIMATION; + if (offset == 1) { + // Now the capture animation is done, enable the action bar. + mListener.onActionBarAllowed(true); + mListener.onActionBarWanted(); + } + snapback(); + } + + private void setPictureSize() { + mPositionController.setImageSize(mPicture.getSize(), null); + } + + private void snapback() { + mPositionController.snapback(); + } + + // ////////////////////////////////////////////////////////////////////////// + // Film mode focus switching + // ////////////////////////////////////////////////////////////////////////// + + private static int getRotated(final int degree, final int original, final int theother) { + return degree % 180 == 0 ? original : theother; + } + + public interface ITileImageAdapter { + + public int getImageHeight(); + + // Returns the rotation for the specified picture. + public int getImageRotation(); + + public int getImageWidth(); + + public int getLevelCount(); + + public ScreenNail getScreenNail(); + + /** + * The tile returned by this method can be specified this way: Assuming + * the image size is (width, height), first take the intersection of (0, + * 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). Then + * extend this intersection region by borderSize pixels on each side. If + * in extending the region, we found some part of the region are outside + * the image, those pixels are filled with black.
+ *
+ * If level > 0, it does the same operation on a down-scaled version of + * the original image (down-scaled by a factor of 2^level), but (x, y) + * still refers to the coordinate on the original image.
+ *
+ * The method would be called in another thread. + */ + public Bitmap getTile(int level, int x, int y, int tileSize, int borderSize, BitmapPool pool); + + public void recycleScreenNail(); + + public boolean setData(BitmapRegionDecoder decoder, Bitmap bitmap, int orientation); + + } + + public interface Listener { + public void onActionBarAllowed(boolean allowed); + + public void onActionBarWanted(); + + public void onCurrentImageUpdated(); + + public void onPictureCenter(); + + public void onSingleTapUp(int x, int y); + } + + public static class Size { + public int width; + public int height; + } + + private class GestureListener implements GestureRecognizer.Listener { + private boolean mIgnoreUpEvent = false; + // If we can change mode for this scale gesture. + private boolean mCanChangeMode; + // If we have changed the film mode in this scaling gesture. + private boolean mModeChanged; + // If a scrolling has happened after a down gesture. + private boolean mScrolledAfterDown; + // The accumulated scaling change from a scaling gesture. + private float mAccScale; + + @Override + public boolean onDoubleTap(final float x, final float y) { + final PositionController controller = mPositionController; + final float scale = controller.getImageScale(); + // onDoubleTap happened on the second ACTION_DOWN. + // We need to ignore the next UP event. + mIgnoreUpEvent = true; + if (scale <= .75f || controller.isAtMinimalScale()) { + controller.zoomIn(x, y, Math.max(1.0f, scale * 1.5f)); + } else { + controller.resetToFullView(); + } + return true; + } + + @Override + public void onDown(final float x, final float y) { + + mModeChanged = false; + + mHolding |= HOLD_TOUCH_DOWN; + + mScrolledAfterDown = false; + } + + @Override + public boolean onFling(final float velocityX, final float velocityY) { + if (mModeChanged) return true; + flingImages(velocityX, velocityY); + return true; + } + + @Override + public boolean onScale(final float focusX, final float focusY, final float scale) { + if (mModeChanged) return true; + if (Float.isNaN(scale) || Float.isInfinite(scale)) return false; + + final int outOfRange = mPositionController.scaleBy(scale, focusX, focusY); + + // We wait for a large enough scale change before changing mode. + // Otherwise we may mistakenly treat a zoom-in gesture as zoom-out + // or vice versa. + mAccScale *= scale; + final boolean largeEnough = mAccScale < 0.97f || mAccScale > 1.03f; + + // If mode changes, we treat this scaling gesture has ended. + if (mCanChangeMode && largeEnough) { + if (outOfRange < 0 || outOfRange > 0) { + stopExtraScalingIfNeeded(); + + // Removing the touch down flag allows snapback to happen + // for film mode change. + mHolding &= ~HOLD_TOUCH_DOWN; + + // We need to call onScaleEnd() before setting mModeChanged + // to true. + onScaleEnd(); + mModeChanged = true; + return true; + } + } + + if (outOfRange != 0) { + startExtraScalingIfNeeded(); + } else { + stopExtraScalingIfNeeded(); + } + return true; + } + + @Override + public boolean onScaleBegin(final float focusX, final float focusY) { + // We ignore the scaling gesture if it is a camera preview. + mPositionController.beginScale(focusX, focusY); + // We can change mode if we are in film mode, or we are in page + // mode and at minimal scale. + mCanChangeMode = mPositionController.isAtMinimalScale(); + mAccScale = 1f; + return true; + } + + @Override + public void onScaleEnd() { + if (mModeChanged) return; + mPositionController.endScale(); + } + + @Override + public boolean onScroll(final float dx, final float dy, final float totalX, final float totalY) { + if (!mScrolledAfterDown) { + mScrolledAfterDown = true; + } + + final int dxi = (int) (-dx + 0.5f); + final int dyi = (int) (-dy + 0.5f); + mPositionController.scrollPage(dxi, dyi); + return true; + } + + @Override + public boolean onSingleTapUp(final float x, final float y) { + // On crespo running Android 2.3.6 (gingerbread), a pinch out + // gesture results in the + // following call sequence: onDown(), onUp() and then + // onSingleTapUp(). The correct + // sequence for a single-tap-up gesture should be: onDown(), + // onSingleTapUp() and onUp(). + // The call sequence for a pinch out gesture in JB is: onDown(), + // then onUp() and there's + // no onSingleTapUp(). Base on these observations, the following + // condition is added to + // filter out the false alarm where onSingleTapUp() is called within + // a pinch out + // gesture. The framework fix went into ICS. Refer to b/4588114. + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + if ((mHolding & HOLD_TOUCH_DOWN) == 0) return true; + } + + // We do this in addition to onUp() because we want the snapback of + // setFilmMode to happen. + mHolding &= ~HOLD_TOUCH_DOWN; + + if (mListener != null) { + // Do the inverse transform of the touch coordinates. + final Matrix m = getGLRoot().getCompensationMatrix(); + final Matrix inv = new Matrix(); + m.invert(inv); + final float[] pts = new float[]{x, y}; + inv.mapPoints(pts); + mListener.onSingleTapUp((int) (pts[0] + 0.5f), (int) (pts[1] + 0.5f)); + } + return true; + } + + @Override + public void onUp() { + mHolding &= ~HOLD_TOUCH_DOWN; + mEdgeView.onRelease(); + + if (mIgnoreUpEvent) { + mIgnoreUpEvent = false; + return; + } + + snapback(); + } + + private boolean flingImages(final float velocityX, final float velocityY) { + final int vx = (int) (velocityX + 0.5f); + final int vy = (int) (velocityY + 0.5f); + return mPositionController.flingPage(vx, vy); + } + + private void startExtraScalingIfNeeded() { + if (!mCancelExtraScalingPending) { + mHandler.sendEmptyMessageDelayed(MSG_CANCEL_EXTRA_SCALING, 700); + mPositionController.setExtraScalingRange(true); + mCancelExtraScalingPending = true; + } + } + + private void stopExtraScalingIfNeeded() { + if (mCancelExtraScalingPending) { + mHandler.removeMessages(MSG_CANCEL_EXTRA_SCALING); + mPositionController.setExtraScalingRange(false); + mCancelExtraScalingPending = false; + } + } + } + + private class MyHandler extends SynchronizedHandler { + + private MyHandler(final ImageViewerGLActivityOld activity) { + super(activity.getGLRoot()); + } + + @Override + public void handleMessage(final Message message) { + switch (message.what) { + case MSG_CANCEL_EXTRA_SCALING: { + mGestureRecognizer.cancelScale(); + mPositionController.setExtraScalingRange(false); + mCancelExtraScalingPending = false; + break; + } + case MSG_CAPTURE_ANIMATION_DONE: { + // message.arg1 is the offset parameter passed to + // switchWithCaptureAnimation(). + captureAnimationDone(message.arg1); + break; + } + default: + throw new AssertionError(message.what); + } + } + } + + private interface Picture { + void draw(GLCanvas canvas, Rect r); + + void forceSize(); // called when mCompensation changes + + Size getSize(); + + void reload(); + + void setScreenNail(ScreenNail s); + } + + class EventListener implements PositionController.Listener { + + @Override + public boolean isHoldingDown() { + return (mHolding & HOLD_TOUCH_DOWN) != 0; + } + + @Override + public void onAbsorb(final int velocity, final int direction) { + mEdgeView.onAbsorb(velocity, direction); + } + + @Override + public void onInvalidate() { + invalidate(); + } + + @Override + public void onPull(final int offset, final int direction) { + mEdgeView.onPull(offset, direction); + } + } + + class FullPicture implements Picture { + private int mRotation; + private final Size mSize = new Size(); + + @Override + public void draw(final GLCanvas canvas, final Rect r) { + drawTileView(canvas, r); + + // We want to have the following transitions: + // (1) Move camera preview out of its place: switch to film mode + // (2) Move camera preview into its place: switch to page mode + // The extra mWasCenter check makes sure (1) does not apply if in + // page mode, we move _to_ the camera preview from another picture. + + // Holdings except touch-down prevent the transitions. + if ((mHolding & ~HOLD_TOUCH_DOWN) != 0) return; + + if (mWantPictureCenterCallbacks && mPositionController.isCenter()) { + mListener.onPictureCenter(); + } + } + + @Override + public void forceSize() { + updateSize(); + mPositionController.forceImageSize(mSize); + } + + @Override + public Size getSize() { + return mSize; + } + + @Override + public void reload() { + // mImageWidth and mImageHeight will get updated + mTileView.notifyModelInvalidated(); + setScreenNail(mModel.getScreenNail()); + updateSize(); + } + + @Override + public void setScreenNail(final ScreenNail s) { + mTileView.setScreenNail(s); + } + + private void drawTileView(final GLCanvas canvas, final Rect r) { + final float imageScale = mPositionController.getImageScale(); + final int viewW = getWidth(); + final int viewH = getHeight(); + final float cx = r.exactCenterX(); + final float cy = r.exactCenterY(); + + canvas.save(GLCanvas.SAVE_FLAG_MATRIX | GLCanvas.SAVE_FLAG_ALPHA); + // Draw the tile view. + setTileViewPosition(cx, cy, viewW, viewH, imageScale); + renderChild(canvas, mTileView); + + // Draw the play video icon and the message. + canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f)); + + canvas.restore(); + } + + // Set the position of the tile view + private void setTileViewPosition(final float cx, final float cy, final int viewW, final int viewH, + final float scale) { + // Find out the bitmap coordinates of the center of the view + final int imageW = mPositionController.getImageWidth(); + final int imageH = mPositionController.getImageHeight(); + final int centerX = (int) (imageW / 2f + (viewW / 2f - cx) / scale + 0.5f); + final int centerY = (int) (imageH / 2f + (viewH / 2f - cy) / scale + 0.5f); + + final int inverseX = imageW - centerX; + final int inverseY = imageH - centerY; + int x, y; + switch (mRotation) { + case 0: + x = centerX; + y = centerY; + break; + case 90: + x = centerY; + y = inverseX; + break; + case 180: + x = inverseX; + y = inverseY; + break; + case 270: + x = inverseY; + y = centerX; + break; + default: + throw new RuntimeException(String.valueOf(mRotation)); + } + mTileView.setPosition(x, y, scale, mRotation); + } + + private void updateSize() { + mRotation = mModel.getImageRotation(); + + final int w = mTileView.mImageWidth; + final int h = mTileView.mImageHeight; + mSize.width = getRotated(mRotation, w, h); + mSize.height = getRotated(mRotation, h, w); + } + } } diff --git a/twidere/src/main/java/org/mariotaku/gallery3d/ui/TileImageView.java b/twidere/src/main/java/org/mariotaku/gallery3d/ui/TileImageView.java index 8be0ff012..215d3c599 100644 --- a/twidere/src/main/java/org/mariotaku/gallery3d/ui/TileImageView.java +++ b/twidere/src/main/java/org/mariotaku/gallery3d/ui/TileImageView.java @@ -23,7 +23,7 @@ import android.support.v4.util.LongSparseArray; import android.util.FloatMath; import android.util.Log; -import org.mariotaku.gallery3d.ImageViewerGLActivity; +import org.mariotaku.gallery3d.ImageViewerGLActivityOld; import org.mariotaku.gallery3d.util.ApiHelper; import org.mariotaku.gallery3d.util.BitmapPool; import org.mariotaku.gallery3d.util.DecodeUtils; @@ -36,723 +36,724 @@ import org.mariotaku.gallery3d.util.ThreadPool.JobContext; import java.util.concurrent.atomic.AtomicBoolean; public class TileImageView extends GLView { - public static final int SIZE_UNKNOWN = -1; - - private static final String TAG = "TileImageView"; - - // TILE_SIZE must be 2^N - 2. We put one pixel border in each side of the - // texture to avoid seams between tiles. - private static int TILE_SIZE; - private static final int TILE_BORDER = 1; - private static int BITMAP_SIZE; - private static final int UPLOAD_LIMIT = 1; - - private static BitmapPool sTilePool; - - /* - * This is the tile state in the CPU side. Life of a Tile: ACTIVATED - * (initial state) --> IN_QUEUE - by queueForDecode() --> RECYCLED - by - * recycleTile() IN_QUEUE --> DECODING - by decodeTile() --> RECYCLED - by - * recycleTile) DECODING --> RECYCLING - by recycleTile() --> DECODED - by - * decodeTile() --> DECODE_FAIL - by decodeTile() RECYCLING --> RECYCLED - - * by decodeTile() DECODED --> ACTIVATED - (after the decoded bitmap is - * uploaded) DECODED --> RECYCLED - by recycleTile() DECODE_FAIL -> RECYCLED - * - by recycleTile() RECYCLED --> ACTIVATED - by obtainTile() - */ - private static final int STATE_ACTIVATED = 0x01; - private static final int STATE_IN_QUEUE = 0x02; - private static final int STATE_DECODING = 0x04; - private static final int STATE_DECODED = 0x08; - private static final int STATE_DECODE_FAIL = 0x10; - private static final int STATE_RECYCLING = 0x20; - private static final int STATE_RECYCLED = 0x40; - - private PhotoView.ITileImageAdapter mModel; - private ScreenNail mScreenNail; - protected int mLevelCount; // cache the value of mScaledBitmaps.length - - // The mLevel variable indicates which level of bitmap we should use. - // Level 0 means the original full-sized bitmap, and a larger value means - // a smaller scaled bitmap (The width and height of each scaled bitmap is - // half size of the previous one). If the value is in [0, mLevelCount), we - // use the bitmap in mScaledBitmaps[mLevel] for display, otherwise the value - // is mLevelCount, and that means we use mScreenNail for display. - private int mLevel = 0; - - // The offsets of the (left, top) of the upper-left tile to the (left, top) - // of the view. - private int mOffsetX; - private int mOffsetY; - - private int mUploadQuota; - private boolean mRenderComplete; - - private final RectF mSourceRect = new RectF(); - private final RectF mTargetRect = new RectF(); - - private final LongSparseArray mActiveTiles = new LongSparseArray(); - - // The following three queue is guarded by TileImageView.this - private final TileQueue mRecycledQueue = new TileQueue(); - private final TileQueue mUploadQueue = new TileQueue(); - private final TileQueue mDecodeQueue = new TileQueue(); - - // The width and height of the full-sized bitmap - protected int mImageWidth = SIZE_UNKNOWN; - protected int mImageHeight = SIZE_UNKNOWN; - - protected int mCenterX; - protected int mCenterY; - protected float mScale; - protected int mRotation; - - // Temp variables to avoid memory allocation - private final Rect mTileRange = new Rect(); - private final Rect mActiveRange[] = { new Rect(), new Rect() }; - - private final TileUploader mTileUploader = new TileUploader(); - private boolean mIsTextureFreed; - private Future mTileDecoder; - private final ThreadPool mThreadPool; - private boolean mBackgroundTileUploaded; - - public TileImageView(final ImageViewerGLActivity context) { - mThreadPool = context.getThreadPool(); - mTileDecoder = mThreadPool.submit(new TileDecoder()); - if (TILE_SIZE == 0) { - if (GalleryUtils.isHighResolution(context)) { - TILE_SIZE = 510; - } else { - TILE_SIZE = 254; - } - BITMAP_SIZE = TILE_SIZE + TILE_BORDER * 2; - sTilePool = ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER ? new BitmapPool(BITMAP_SIZE, - BITMAP_SIZE, 128) : null; - } - } - - public void freeTextures() { - mIsTextureFreed = true; - - if (mTileDecoder != null) { - mTileDecoder.cancel(); - mTileDecoder.get(); - mTileDecoder = null; - } - - final int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - final Tile texture = mActiveTiles.valueAt(i); - texture.recycle(); - } - mActiveTiles.clear(); - mTileRange.set(0, 0, 0, 0); - - synchronized (this) { - mUploadQueue.clean(); - mDecodeQueue.clean(); - Tile tile = mRecycledQueue.pop(); - while (tile != null) { - tile.recycle(); - tile = mRecycledQueue.pop(); - } - } - setScreenNail(null); - if (sTilePool != null) { - sTilePool.clear(); - } - } - - public void notifyModelInvalidated() { - invalidateTiles(); - if (mModel == null) { - mScreenNail = null; - mImageWidth = 0; - mImageHeight = 0; - mLevelCount = 0; - } else { - setScreenNail(mModel.getScreenNail()); - mImageWidth = mModel.getImageWidth(); - mImageHeight = mModel.getImageHeight(); - mLevelCount = mModel.getLevelCount(); - } - layoutTiles(mCenterX, mCenterY, mScale, mRotation); - invalidate(); - } - - public void prepareTextures() { - if (mTileDecoder == null) { - mTileDecoder = mThreadPool.submit(new TileDecoder()); - } - if (mIsTextureFreed) { - layoutTiles(mCenterX, mCenterY, mScale, mRotation); - mIsTextureFreed = false; - setScreenNail(mModel == null ? null : mModel.getScreenNail()); - } - } - - public void setModel(final PhotoView.ITileImageAdapter model) { - mModel = model; - if (model != null) { - notifyModelInvalidated(); - } - } - - public boolean setPosition(final int centerX, final int centerY, final float scale, final int rotation) { - if (mCenterX == centerX && mCenterY == centerY && mScale == scale && mRotation == rotation) return false; - mCenterX = centerX; - mCenterY = centerY; - mScale = scale; - mRotation = rotation; - layoutTiles(centerX, centerY, scale, rotation); - invalidate(); - return true; - } - - public void setScreenNail(final ScreenNail s) { - mScreenNail = s; - } - - @Override - protected void onLayout(final boolean changeSize, final int left, final int top, final int right, final int bottom) { - super.onLayout(changeSize, left, top, right, bottom); - if (changeSize) { - layoutTiles(mCenterX, mCenterY, mScale, mRotation); - } - } - - @Override - protected void render(final GLCanvas canvas) { - mUploadQuota = UPLOAD_LIMIT; - mRenderComplete = true; - - final int level = mLevel; - final int rotation = mRotation; - int flags = 0; - if (rotation != 0) { - flags |= GLCanvas.SAVE_FLAG_MATRIX; - } - - if (flags != 0) { - canvas.save(flags); - if (rotation != 0) { - final int centerX = getWidth() / 2, centerY = getHeight() / 2; - canvas.translate(centerX, centerY); - canvas.rotate(rotation, 0, 0, 1); - canvas.translate(-centerX, -centerY); - } - } - try { - if (level != mLevelCount && !isScreenNailAnimating()) { - if (mScreenNail != null) { - mScreenNail.noDraw(); - } - - final int size = TILE_SIZE << level; - final float length = size * mScale; - final Rect r = mTileRange; - - for (int ty = r.top, i = 0; ty < r.bottom; ty += size, i++) { - final float y = mOffsetY + i * length; - for (int tx = r.left, j = 0; tx < r.right; tx += size, j++) { - final float x = mOffsetX + j * length; - drawTile(canvas, tx, ty, level, x, y, length); - } - } - } else if (mScreenNail != null) { - mScreenNail.draw(canvas, mOffsetX, mOffsetY, Math.round(mImageWidth * mScale), - Math.round(mImageHeight * mScale)); - if (isScreenNailAnimating()) { - invalidate(); - } - } - } finally { - if (flags != 0) { - canvas.restore(); - } - } - - if (mRenderComplete) { - if (!mBackgroundTileUploaded) { - uploadBackgroundTiles(canvas); - } - } else { - invalidate(); - } - } - - private void activateTile(final int x, final int y, final int level) { - final long key = makeTileKey(x, y, level); - Tile tile = mActiveTiles.get(key); - if (tile != null) { - if (tile.mTileState == STATE_IN_QUEUE) { - tile.mTileState = STATE_ACTIVATED; - } - return; - } - tile = obtainTile(x, y, level); - mActiveTiles.put(key, tile); - } - - private boolean decodeTile(final Tile tile) { - synchronized (this) { - if (tile.mTileState != STATE_IN_QUEUE) return false; - tile.mTileState = STATE_DECODING; - } - final boolean decodeComplete = tile.decode(); - synchronized (this) { - if (tile.mTileState == STATE_RECYCLING) { - tile.mTileState = STATE_RECYCLED; - if (tile.mDecodedTile != null) { - if (sTilePool != null) { - sTilePool.recycle(tile.mDecodedTile); - } - tile.mDecodedTile = null; - } - mRecycledQueue.push(tile); - return false; - } - tile.mTileState = decodeComplete ? STATE_DECODED : STATE_DECODE_FAIL; - return decodeComplete; - } - } - - // Draw the tile to a square at canvas that locates at (x, y) and - // has a side length of length. - private void drawTile(final GLCanvas canvas, final int tx, final int ty, final int level, final float x, - final float y, final float length) { - final RectF source = mSourceRect; - final RectF target = mTargetRect; - target.set(x, y, x + length, y + length); - source.set(0, 0, TILE_SIZE, TILE_SIZE); - - final Tile tile = getTile(tx, ty, level); - if (tile != null) { - if (!tile.isContentValid()) { - if (tile.mTileState == STATE_DECODED) { - if (mUploadQuota > 0) { - --mUploadQuota; - tile.updateContent(canvas); - } else { - mRenderComplete = false; - } - } else if (tile.mTileState != STATE_DECODE_FAIL) { - mRenderComplete = false; - queueForDecode(tile); - } - } - if (drawTile(tile, canvas, source, target)) return; - } - if (mScreenNail != null) { - final int size = TILE_SIZE << level; - final float scaleX = (float) mScreenNail.getWidth() / mImageWidth; - final float scaleY = (float) mScreenNail.getHeight() / mImageHeight; - source.set(tx * scaleX, ty * scaleY, (tx + size) * scaleX, (ty + size) * scaleY); - mScreenNail.draw(canvas, source, target); - } - } - - // If the bitmap is scaled by the given factor "scale", return the - // rectangle containing visible range. The left-top coordinate returned is - // aligned to the tile boundary. - // - // (cX, cY) is the point on the original bitmap which will be put in the - // center of the ImageViewer. - private void getRange(final Rect out, final int cX, final int cY, final int level, final float scale, - final int rotation) { - - final double radians = Math.toRadians(-rotation); - final double w = getWidth(); - final double h = getHeight(); - - final double cos = Math.cos(radians); - final double sin = Math.sin(radians); - final int width = (int) Math.ceil(Math.max(Math.abs(cos * w - sin * h), Math.abs(cos * w + sin * h))); - final int height = (int) Math.ceil(Math.max(Math.abs(sin * w + cos * h), Math.abs(sin * w - cos * h))); - - int left = (int) FloatMath.floor(cX - width / (2f * scale)); - int top = (int) FloatMath.floor(cY - height / (2f * scale)); - int right = (int) FloatMath.ceil(left + width / scale); - int bottom = (int) FloatMath.ceil(top + height / scale); - - // align the rectangle to tile boundary - final int size = TILE_SIZE << level; - left = Math.max(0, size * (left / size)); - top = Math.max(0, size * (top / size)); - right = Math.min(mImageWidth, right); - bottom = Math.min(mImageHeight, bottom); - - out.set(left, top, right, bottom); - } - - private void getRange(final Rect out, final int cX, final int cY, final int level, final int rotation) { - getRange(out, cX, cY, level, 1f / (1 << level + 1), rotation); - } - - private Tile getTile(final int x, final int y, final int level) { - return mActiveTiles.get(makeTileKey(x, y, level)); - } - - private synchronized void invalidateTiles() { - mDecodeQueue.clean(); - mUploadQueue.clean(); - - // TODO disable decoder - final int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - final Tile tile = mActiveTiles.valueAt(i); - recycleTile(tile); - } - mActiveTiles.clear(); - } - - private boolean isScreenNailAnimating() { - return false; - } - - // Prepare the tiles we want to use for display. - // - // 1. Decide the tile level we want to use for display. - // 2. Decide the tile levels we want to keep as texture (in addition to - // the one we use for display). - // 3. Recycle unused tiles. - // 4. Activate the tiles we want. - private void layoutTiles(final int centerX, final int centerY, final float scale, final int rotation) { - // The width and height of this view. - final int width = getWidth(); - final int height = getHeight(); - - // The tile levels we want to keep as texture is in the range - // [fromLevel, endLevel). - int fromLevel; - int endLevel; - - // We want to use a texture larger than or equal to the display size. - mLevel = GalleryUtils.clamp(GalleryUtils.floorLog2(1f / scale), 0, mLevelCount); - - // We want to keep one more tile level as texture in addition to what - // we use for display. So it can be faster when the scale moves to the - // next level. We choose a level closer to the current scale. - if (mLevel != mLevelCount) { - final Rect range = mTileRange; - getRange(range, centerX, centerY, mLevel, scale, rotation); - mOffsetX = Math.round(width / 2f + (range.left - centerX) * scale); - mOffsetY = Math.round(height / 2f + (range.top - centerY) * scale); - fromLevel = scale * (1 << mLevel) > 0.75f ? mLevel - 1 : mLevel; - } else { - // Activate the tiles of the smallest two levels. - fromLevel = mLevel - 2; - mOffsetX = Math.round(width / 2f - centerX * scale); - mOffsetY = Math.round(height / 2f - centerY * scale); - } - - fromLevel = Math.max(0, Math.min(fromLevel, mLevelCount - 2)); - endLevel = Math.min(fromLevel + 2, mLevelCount); - - final Rect range[] = mActiveRange; - for (int i = fromLevel; i < endLevel; ++i) { - getRange(range[i - fromLevel], centerX, centerY, i, rotation); - } - - // If rotation is transient, don't update the tile. - if (rotation % 90 != 0) return; - - synchronized (this) { - mDecodeQueue.clean(); - mUploadQueue.clean(); - mBackgroundTileUploaded = false; - - // Recycle unused tiles: if the level of the active tile is outside - // the - // range [fromLevel, endLevel) or not in the visible range. - int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - final Tile tile = mActiveTiles.valueAt(i); - final int level = tile.mTileLevel; - if (level < fromLevel || level >= endLevel || !range[level - fromLevel].contains(tile.mX, tile.mY)) { - mActiveTiles.removeAt(i); - i--; - n--; - recycleTile(tile); - } - } - } - - for (int i = fromLevel; i < endLevel; ++i) { - final int size = TILE_SIZE << i; - final Rect r = range[i - fromLevel]; - for (int y = r.top, bottom = r.bottom; y < bottom; y += size) { - for (int x = r.left, right = r.right; x < right; x += size) { - activateTile(x, y, i); - } - } - } - invalidate(); - } - - private synchronized Tile obtainTile(final int x, final int y, final int level) { - final Tile tile = mRecycledQueue.pop(); - if (tile != null) { - tile.mTileState = STATE_ACTIVATED; - tile.update(x, y, level); - return tile; - } - return new Tile(x, y, level); - } - - private synchronized void queueForDecode(final Tile tile) { - if (tile.mTileState == STATE_ACTIVATED) { - tile.mTileState = STATE_IN_QUEUE; - if (mDecodeQueue.push(tile)) { - notifyAll(); - } - } - } - - private void queueForUpload(final Tile tile) { - synchronized (this) { - mUploadQueue.push(tile); - } - if (mTileUploader.mActive.compareAndSet(false, true)) { - getGLRoot().addOnGLIdleListener(mTileUploader); - } - } - - private synchronized void recycleTile(final Tile tile) { - if (tile.mTileState == STATE_DECODING) { - tile.mTileState = STATE_RECYCLING; - return; - } - tile.mTileState = STATE_RECYCLED; - if (tile.mDecodedTile != null) { - if (sTilePool != null) { - sTilePool.recycle(tile.mDecodedTile); - } - tile.mDecodedTile = null; - } - mRecycledQueue.push(tile); - } - - private void uploadBackgroundTiles(final GLCanvas canvas) { - mBackgroundTileUploaded = true; - final int n = mActiveTiles.size(); - for (int i = 0; i < n; i++) { - final Tile tile = mActiveTiles.valueAt(i); - if (!tile.isContentValid()) { - queueForDecode(tile); - } - } - } - - private static boolean drawTile(Tile tile, final GLCanvas canvas, final RectF source, final RectF target) { - while (true) { - if (tile.isContentValid()) { - // offset source rectangle for the texture border. - source.offset(TILE_BORDER, TILE_BORDER); - canvas.drawTexture(tile, source, target); - return true; - } - - // Parent can be divided to four quads and tile is one of the four. - final Tile parent = tile.getParentTile(); - if (parent == null) return false; - if (tile.mX == parent.mX) { - source.left /= 2f; - source.right /= 2f; - } else { - source.left = (TILE_SIZE + source.left) / 2f; - source.right = (TILE_SIZE + source.right) / 2f; - } - if (tile.mY == parent.mY) { - source.top /= 2f; - source.bottom /= 2f; - } else { - source.top = (TILE_SIZE + source.top) / 2f; - source.bottom = (TILE_SIZE + source.bottom) / 2f; - } - tile = parent; - } - } - - private static long makeTileKey(final int x, final int y, final int level) { - long result = x; - result = result << 16 | y; - result = result << 16 | level; - return result; - } - - private class Tile extends UploadedTexture { - public int mX; - public int mY; - public int mTileLevel; - public Tile mNext; - public Bitmap mDecodedTile; - public volatile int mTileState = STATE_ACTIVATED; - - public Tile(final int x, final int y, final int level) { - mX = x; - mY = y; - mTileLevel = level; - } - - public Tile getParentTile() { - if (mTileLevel + 1 == mLevelCount) return null; - final int size = TILE_SIZE << mTileLevel + 1; - final int x = size * (mX / size); - final int y = size * (mY / size); - return getTile(x, y, mTileLevel + 1); - } - - @Override - public int getTextureHeight() { - return TILE_SIZE + TILE_BORDER * 2; - } - - // We override getTextureWidth() and getTextureHeight() here, so the - // texture can be re-used for different tiles regardless of the actual - // size of the tile (which may be small because it is a tile at the - // boundary). - @Override - public int getTextureWidth() { - return TILE_SIZE + TILE_BORDER * 2; - } - - @Override - public String toString() { - return String.format("tile(%s, %s, %s / %s)", mX / TILE_SIZE, mY / TILE_SIZE, mLevel, mLevelCount); - } - - public void update(final int x, final int y, final int level) { - mX = x; - mY = y; - mTileLevel = level; - invalidateContent(); - } - - @Override - protected void onFreeBitmap(final Bitmap bitmap) { - if (sTilePool != null) { - sTilePool.recycle(bitmap); - } - } - - @Override - protected Bitmap onGetBitmap() { - GalleryUtils.assertTrue(mTileState == STATE_DECODED); - - // We need to override the width and height, so that we won't - // draw beyond the boundaries. - final int rightEdge = (mImageWidth - mX >> mTileLevel) + TILE_BORDER; - final int bottomEdge = (mImageHeight - mY >> mTileLevel) + TILE_BORDER; - setSize(Math.min(BITMAP_SIZE, rightEdge), Math.min(BITMAP_SIZE, bottomEdge)); - - final Bitmap bitmap = mDecodedTile; - mDecodedTile = null; - mTileState = STATE_ACTIVATED; - return bitmap; - } - - boolean decode() { - // Get a tile from the original image. The tile is down-scaled - // by (1 << mTilelevel) from a region in the original image. - try { - mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(mTileLevel, mX, mY, TILE_SIZE, - TILE_BORDER, sTilePool)); - } catch (final Throwable t) { - Log.w(TAG, "fail to decode tile", t); - } - return mDecodedTile != null; - } - } - - private class TileDecoder implements ThreadPool.Job { - - private final CancelListener mNotifier = new CancelListener() { - @Override - public void onCancel() { - synchronized (TileImageView.this) { - TileImageView.this.notifyAll(); - } - } - }; - - @Override - public Void run(final JobContext jc) { - jc.setMode(ThreadPool.MODE_NONE); - jc.setCancelListener(mNotifier); - while (!jc.isCancelled()) { - Tile tile = null; - synchronized (TileImageView.this) { - tile = mDecodeQueue.pop(); - if (tile == null && !jc.isCancelled()) { - GalleryUtils.waitWithoutInterrupt(TileImageView.this); - } - } - if (tile == null) { - continue; - } - if (decodeTile(tile)) { - queueForUpload(tile); - } - } - return null; - } - } - - private static class TileQueue { - private Tile mHead; - - public void clean() { - mHead = null; - } - - public Tile pop() { - final Tile tile = mHead; - if (tile != null) { - mHead = tile.mNext; - } - return tile; - } - - public boolean push(final Tile tile) { - final boolean wasEmpty = mHead == null; - tile.mNext = mHead; - mHead = tile; - return wasEmpty; - } - } - - private class TileUploader implements GLRoot.OnGLIdleListener { - AtomicBoolean mActive = new AtomicBoolean(false); - - @Override - public boolean onGLIdle(final GLCanvas canvas, final boolean renderRequested) { - // Skips uploading if there is a pending rendering request. - // Returns true to keep uploading in next rendering loop. - if (renderRequested) return true; - int quota = UPLOAD_LIMIT; - Tile tile = null; - while (quota > 0) { - synchronized (TileImageView.this) { - tile = mUploadQueue.pop(); - } - if (tile == null) { - break; - } - if (!tile.isContentValid()) { - final boolean hasBeenLoaded = tile.isLoaded(); - if (tile.mTileState != STATE_DECODED) return false; - tile.updateContent(canvas); - if (!hasBeenLoaded) { - tile.draw(canvas, 0, 0); - } - --quota; - } - } - if (tile == null) { - mActive.set(false); - } - return tile != null; - } - } + public static final int SIZE_UNKNOWN = -1; + + private static final String TAG = "TileImageView"; + + // TILE_SIZE must be 2^N - 2. We put one pixel border in each side of the + // texture to avoid seams between tiles. + private static int TILE_SIZE; + private static final int TILE_BORDER = 1; + private static int BITMAP_SIZE; + private static final int UPLOAD_LIMIT = 1; + + private static BitmapPool sTilePool; + + /* + * This is the tile state in the CPU side. Life of a Tile: ACTIVATED + * (initial state) --> IN_QUEUE - by queueForDecode() --> RECYCLED - by + * recycleTile() IN_QUEUE --> DECODING - by decodeTile() --> RECYCLED - by + * recycleTile) DECODING --> RECYCLING - by recycleTile() --> DECODED - by + * decodeTile() --> DECODE_FAIL - by decodeTile() RECYCLING --> RECYCLED - + * by decodeTile() DECODED --> ACTIVATED - (after the decoded bitmap is + * uploaded) DECODED --> RECYCLED - by recycleTile() DECODE_FAIL -> RECYCLED + * - by recycleTile() RECYCLED --> ACTIVATED - by obtainTile() + */ + private static final int STATE_ACTIVATED = 0x01; + private static final int STATE_IN_QUEUE = 0x02; + private static final int STATE_DECODING = 0x04; + private static final int STATE_DECODED = 0x08; + private static final int STATE_DECODE_FAIL = 0x10; + private static final int STATE_RECYCLING = 0x20; + private static final int STATE_RECYCLED = 0x40; + + private PhotoView.ITileImageAdapter mModel; + private ScreenNail mScreenNail; + protected int mLevelCount; // cache the value of mScaledBitmaps.length + + // The mLevel variable indicates which level of bitmap we should use. + // Level 0 means the original full-sized bitmap, and a larger value means + // a smaller scaled bitmap (The width and height of each scaled bitmap is + // half size of the previous one). If the value is in [0, mLevelCount), we + // use the bitmap in mScaledBitmaps[mLevel] for display, otherwise the value + // is mLevelCount, and that means we use mScreenNail for display. + private int mLevel = 0; + + // The offsets of the (left, top) of the upper-left tile to the (left, top) + // of the view. + private int mOffsetX; + private int mOffsetY; + + private int mUploadQuota; + private boolean mRenderComplete; + + private final RectF mSourceRect = new RectF(); + private final RectF mTargetRect = new RectF(); + + private final LongSparseArray mActiveTiles = new LongSparseArray(); + + // The following three queue is guarded by TileImageView.this + private final TileQueue mRecycledQueue = new TileQueue(); + private final TileQueue mUploadQueue = new TileQueue(); + private final TileQueue mDecodeQueue = new TileQueue(); + + // The width and height of the full-sized bitmap + protected int mImageWidth = SIZE_UNKNOWN; + protected int mImageHeight = SIZE_UNKNOWN; + + protected int mCenterX; + protected int mCenterY; + protected float mScale; + protected int mRotation; + + // Temp variables to avoid memory allocation + private final Rect mTileRange = new Rect(); + private final Rect mActiveRange[] = {new Rect(), new Rect()}; + + private final TileUploader mTileUploader = new TileUploader(); + private boolean mIsTextureFreed; + private Future mTileDecoder; + private final ThreadPool mThreadPool; + private boolean mBackgroundTileUploaded; + + public TileImageView(final ImageViewerGLActivityOld context) { + mThreadPool = context.getThreadPool(); + mTileDecoder = mThreadPool.submit(new TileDecoder()); + if (TILE_SIZE == 0) { + if (GalleryUtils.isHighResolution(context)) { + TILE_SIZE = 510; + } else { + TILE_SIZE = 254; + } + BITMAP_SIZE = TILE_SIZE + TILE_BORDER * 2; + sTilePool = ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER ? new BitmapPool(BITMAP_SIZE, + BITMAP_SIZE, 128) : null; + } + } + + public void freeTextures() { + mIsTextureFreed = true; + + if (mTileDecoder != null) { + mTileDecoder.cancel(); + mTileDecoder.get(); + mTileDecoder = null; + } + + final int n = mActiveTiles.size(); + for (int i = 0; i < n; i++) { + final Tile texture = mActiveTiles.valueAt(i); + texture.recycle(); + } + mActiveTiles.clear(); + mTileRange.set(0, 0, 0, 0); + + synchronized (this) { + mUploadQueue.clean(); + mDecodeQueue.clean(); + Tile tile = mRecycledQueue.pop(); + while (tile != null) { + tile.recycle(); + tile = mRecycledQueue.pop(); + } + } + setScreenNail(null); + if (sTilePool != null) { + sTilePool.clear(); + } + } + + public void notifyModelInvalidated() { + invalidateTiles(); + if (mModel == null) { + mScreenNail = null; + mImageWidth = 0; + mImageHeight = 0; + mLevelCount = 0; + } else { + setScreenNail(mModel.getScreenNail()); + mImageWidth = mModel.getImageWidth(); + mImageHeight = mModel.getImageHeight(); + mLevelCount = mModel.getLevelCount(); + } + layoutTiles(mCenterX, mCenterY, mScale, mRotation); + invalidate(); + } + + public void prepareTextures() { + if (mTileDecoder == null) { + mTileDecoder = mThreadPool.submit(new TileDecoder()); + } + if (mIsTextureFreed) { + layoutTiles(mCenterX, mCenterY, mScale, mRotation); + mIsTextureFreed = false; + setScreenNail(mModel == null ? null : mModel.getScreenNail()); + } + } + + public void setModel(final PhotoView.ITileImageAdapter model) { + mModel = model; + if (model != null) { + notifyModelInvalidated(); + } + } + + public boolean setPosition(final int centerX, final int centerY, final float scale, final int rotation) { + if (mCenterX == centerX && mCenterY == centerY && mScale == scale && mRotation == rotation) + return false; + mCenterX = centerX; + mCenterY = centerY; + mScale = scale; + mRotation = rotation; + layoutTiles(centerX, centerY, scale, rotation); + invalidate(); + return true; + } + + public void setScreenNail(final ScreenNail s) { + mScreenNail = s; + } + + @Override + protected void onLayout(final boolean changeSize, final int left, final int top, final int right, final int bottom) { + super.onLayout(changeSize, left, top, right, bottom); + if (changeSize) { + layoutTiles(mCenterX, mCenterY, mScale, mRotation); + } + } + + @Override + protected void render(final GLCanvas canvas) { + mUploadQuota = UPLOAD_LIMIT; + mRenderComplete = true; + + final int level = mLevel; + final int rotation = mRotation; + int flags = 0; + if (rotation != 0) { + flags |= GLCanvas.SAVE_FLAG_MATRIX; + } + + if (flags != 0) { + canvas.save(flags); + if (rotation != 0) { + final int centerX = getWidth() / 2, centerY = getHeight() / 2; + canvas.translate(centerX, centerY); + canvas.rotate(rotation, 0, 0, 1); + canvas.translate(-centerX, -centerY); + } + } + try { + if (level != mLevelCount && !isScreenNailAnimating()) { + if (mScreenNail != null) { + mScreenNail.noDraw(); + } + + final int size = TILE_SIZE << level; + final float length = size * mScale; + final Rect r = mTileRange; + + for (int ty = r.top, i = 0; ty < r.bottom; ty += size, i++) { + final float y = mOffsetY + i * length; + for (int tx = r.left, j = 0; tx < r.right; tx += size, j++) { + final float x = mOffsetX + j * length; + drawTile(canvas, tx, ty, level, x, y, length); + } + } + } else if (mScreenNail != null) { + mScreenNail.draw(canvas, mOffsetX, mOffsetY, Math.round(mImageWidth * mScale), + Math.round(mImageHeight * mScale)); + if (isScreenNailAnimating()) { + invalidate(); + } + } + } finally { + if (flags != 0) { + canvas.restore(); + } + } + + if (mRenderComplete) { + if (!mBackgroundTileUploaded) { + uploadBackgroundTiles(canvas); + } + } else { + invalidate(); + } + } + + private void activateTile(final int x, final int y, final int level) { + final long key = makeTileKey(x, y, level); + Tile tile = mActiveTiles.get(key); + if (tile != null) { + if (tile.mTileState == STATE_IN_QUEUE) { + tile.mTileState = STATE_ACTIVATED; + } + return; + } + tile = obtainTile(x, y, level); + mActiveTiles.put(key, tile); + } + + private boolean decodeTile(final Tile tile) { + synchronized (this) { + if (tile.mTileState != STATE_IN_QUEUE) return false; + tile.mTileState = STATE_DECODING; + } + final boolean decodeComplete = tile.decode(); + synchronized (this) { + if (tile.mTileState == STATE_RECYCLING) { + tile.mTileState = STATE_RECYCLED; + if (tile.mDecodedTile != null) { + if (sTilePool != null) { + sTilePool.recycle(tile.mDecodedTile); + } + tile.mDecodedTile = null; + } + mRecycledQueue.push(tile); + return false; + } + tile.mTileState = decodeComplete ? STATE_DECODED : STATE_DECODE_FAIL; + return decodeComplete; + } + } + + // Draw the tile to a square at canvas that locates at (x, y) and + // has a side length of length. + private void drawTile(final GLCanvas canvas, final int tx, final int ty, final int level, final float x, + final float y, final float length) { + final RectF source = mSourceRect; + final RectF target = mTargetRect; + target.set(x, y, x + length, y + length); + source.set(0, 0, TILE_SIZE, TILE_SIZE); + + final Tile tile = getTile(tx, ty, level); + if (tile != null) { + if (!tile.isContentValid()) { + if (tile.mTileState == STATE_DECODED) { + if (mUploadQuota > 0) { + --mUploadQuota; + tile.updateContent(canvas); + } else { + mRenderComplete = false; + } + } else if (tile.mTileState != STATE_DECODE_FAIL) { + mRenderComplete = false; + queueForDecode(tile); + } + } + if (drawTile(tile, canvas, source, target)) return; + } + if (mScreenNail != null) { + final int size = TILE_SIZE << level; + final float scaleX = (float) mScreenNail.getWidth() / mImageWidth; + final float scaleY = (float) mScreenNail.getHeight() / mImageHeight; + source.set(tx * scaleX, ty * scaleY, (tx + size) * scaleX, (ty + size) * scaleY); + mScreenNail.draw(canvas, source, target); + } + } + + // If the bitmap is scaled by the given factor "scale", return the + // rectangle containing visible range. The left-top coordinate returned is + // aligned to the tile boundary. + // + // (cX, cY) is the point on the original bitmap which will be put in the + // center of the ImageViewer. + private void getRange(final Rect out, final int cX, final int cY, final int level, final float scale, + final int rotation) { + + final double radians = Math.toRadians(-rotation); + final double w = getWidth(); + final double h = getHeight(); + + final double cos = Math.cos(radians); + final double sin = Math.sin(radians); + final int width = (int) Math.ceil(Math.max(Math.abs(cos * w - sin * h), Math.abs(cos * w + sin * h))); + final int height = (int) Math.ceil(Math.max(Math.abs(sin * w + cos * h), Math.abs(sin * w - cos * h))); + + int left = (int) FloatMath.floor(cX - width / (2f * scale)); + int top = (int) FloatMath.floor(cY - height / (2f * scale)); + int right = (int) FloatMath.ceil(left + width / scale); + int bottom = (int) FloatMath.ceil(top + height / scale); + + // align the rectangle to tile boundary + final int size = TILE_SIZE << level; + left = Math.max(0, size * (left / size)); + top = Math.max(0, size * (top / size)); + right = Math.min(mImageWidth, right); + bottom = Math.min(mImageHeight, bottom); + + out.set(left, top, right, bottom); + } + + private void getRange(final Rect out, final int cX, final int cY, final int level, final int rotation) { + getRange(out, cX, cY, level, 1f / (1 << level + 1), rotation); + } + + private Tile getTile(final int x, final int y, final int level) { + return mActiveTiles.get(makeTileKey(x, y, level)); + } + + private synchronized void invalidateTiles() { + mDecodeQueue.clean(); + mUploadQueue.clean(); + + // TODO disable decoder + final int n = mActiveTiles.size(); + for (int i = 0; i < n; i++) { + final Tile tile = mActiveTiles.valueAt(i); + recycleTile(tile); + } + mActiveTiles.clear(); + } + + private boolean isScreenNailAnimating() { + return false; + } + + // Prepare the tiles we want to use for display. + // + // 1. Decide the tile level we want to use for display. + // 2. Decide the tile levels we want to keep as texture (in addition to + // the one we use for display). + // 3. Recycle unused tiles. + // 4. Activate the tiles we want. + private void layoutTiles(final int centerX, final int centerY, final float scale, final int rotation) { + // The width and height of this view. + final int width = getWidth(); + final int height = getHeight(); + + // The tile levels we want to keep as texture is in the range + // [fromLevel, endLevel). + int fromLevel; + int endLevel; + + // We want to use a texture larger than or equal to the display size. + mLevel = GalleryUtils.clamp(GalleryUtils.floorLog2(1f / scale), 0, mLevelCount); + + // We want to keep one more tile level as texture in addition to what + // we use for display. So it can be faster when the scale moves to the + // next level. We choose a level closer to the current scale. + if (mLevel != mLevelCount) { + final Rect range = mTileRange; + getRange(range, centerX, centerY, mLevel, scale, rotation); + mOffsetX = Math.round(width / 2f + (range.left - centerX) * scale); + mOffsetY = Math.round(height / 2f + (range.top - centerY) * scale); + fromLevel = scale * (1 << mLevel) > 0.75f ? mLevel - 1 : mLevel; + } else { + // Activate the tiles of the smallest two levels. + fromLevel = mLevel - 2; + mOffsetX = Math.round(width / 2f - centerX * scale); + mOffsetY = Math.round(height / 2f - centerY * scale); + } + + fromLevel = Math.max(0, Math.min(fromLevel, mLevelCount - 2)); + endLevel = Math.min(fromLevel + 2, mLevelCount); + + final Rect range[] = mActiveRange; + for (int i = fromLevel; i < endLevel; ++i) { + getRange(range[i - fromLevel], centerX, centerY, i, rotation); + } + + // If rotation is transient, don't update the tile. + if (rotation % 90 != 0) return; + + synchronized (this) { + mDecodeQueue.clean(); + mUploadQueue.clean(); + mBackgroundTileUploaded = false; + + // Recycle unused tiles: if the level of the active tile is outside + // the + // range [fromLevel, endLevel) or not in the visible range. + int n = mActiveTiles.size(); + for (int i = 0; i < n; i++) { + final Tile tile = mActiveTiles.valueAt(i); + final int level = tile.mTileLevel; + if (level < fromLevel || level >= endLevel || !range[level - fromLevel].contains(tile.mX, tile.mY)) { + mActiveTiles.removeAt(i); + i--; + n--; + recycleTile(tile); + } + } + } + + for (int i = fromLevel; i < endLevel; ++i) { + final int size = TILE_SIZE << i; + final Rect r = range[i - fromLevel]; + for (int y = r.top, bottom = r.bottom; y < bottom; y += size) { + for (int x = r.left, right = r.right; x < right; x += size) { + activateTile(x, y, i); + } + } + } + invalidate(); + } + + private synchronized Tile obtainTile(final int x, final int y, final int level) { + final Tile tile = mRecycledQueue.pop(); + if (tile != null) { + tile.mTileState = STATE_ACTIVATED; + tile.update(x, y, level); + return tile; + } + return new Tile(x, y, level); + } + + private synchronized void queueForDecode(final Tile tile) { + if (tile.mTileState == STATE_ACTIVATED) { + tile.mTileState = STATE_IN_QUEUE; + if (mDecodeQueue.push(tile)) { + notifyAll(); + } + } + } + + private void queueForUpload(final Tile tile) { + synchronized (this) { + mUploadQueue.push(tile); + } + if (mTileUploader.mActive.compareAndSet(false, true)) { + getGLRoot().addOnGLIdleListener(mTileUploader); + } + } + + private synchronized void recycleTile(final Tile tile) { + if (tile.mTileState == STATE_DECODING) { + tile.mTileState = STATE_RECYCLING; + return; + } + tile.mTileState = STATE_RECYCLED; + if (tile.mDecodedTile != null) { + if (sTilePool != null) { + sTilePool.recycle(tile.mDecodedTile); + } + tile.mDecodedTile = null; + } + mRecycledQueue.push(tile); + } + + private void uploadBackgroundTiles(final GLCanvas canvas) { + mBackgroundTileUploaded = true; + final int n = mActiveTiles.size(); + for (int i = 0; i < n; i++) { + final Tile tile = mActiveTiles.valueAt(i); + if (!tile.isContentValid()) { + queueForDecode(tile); + } + } + } + + private static boolean drawTile(Tile tile, final GLCanvas canvas, final RectF source, final RectF target) { + while (true) { + if (tile.isContentValid()) { + // offset source rectangle for the texture border. + source.offset(TILE_BORDER, TILE_BORDER); + canvas.drawTexture(tile, source, target); + return true; + } + + // Parent can be divided to four quads and tile is one of the four. + final Tile parent = tile.getParentTile(); + if (parent == null) return false; + if (tile.mX == parent.mX) { + source.left /= 2f; + source.right /= 2f; + } else { + source.left = (TILE_SIZE + source.left) / 2f; + source.right = (TILE_SIZE + source.right) / 2f; + } + if (tile.mY == parent.mY) { + source.top /= 2f; + source.bottom /= 2f; + } else { + source.top = (TILE_SIZE + source.top) / 2f; + source.bottom = (TILE_SIZE + source.bottom) / 2f; + } + tile = parent; + } + } + + private static long makeTileKey(final int x, final int y, final int level) { + long result = x; + result = result << 16 | y; + result = result << 16 | level; + return result; + } + + private class Tile extends UploadedTexture { + public int mX; + public int mY; + public int mTileLevel; + public Tile mNext; + public Bitmap mDecodedTile; + public volatile int mTileState = STATE_ACTIVATED; + + public Tile(final int x, final int y, final int level) { + mX = x; + mY = y; + mTileLevel = level; + } + + public Tile getParentTile() { + if (mTileLevel + 1 == mLevelCount) return null; + final int size = TILE_SIZE << mTileLevel + 1; + final int x = size * (mX / size); + final int y = size * (mY / size); + return getTile(x, y, mTileLevel + 1); + } + + @Override + public int getTextureHeight() { + return TILE_SIZE + TILE_BORDER * 2; + } + + // We override getTextureWidth() and getTextureHeight() here, so the + // texture can be re-used for different tiles regardless of the actual + // size of the tile (which may be small because it is a tile at the + // boundary). + @Override + public int getTextureWidth() { + return TILE_SIZE + TILE_BORDER * 2; + } + + @Override + public String toString() { + return String.format("tile(%s, %s, %s / %s)", mX / TILE_SIZE, mY / TILE_SIZE, mLevel, mLevelCount); + } + + public void update(final int x, final int y, final int level) { + mX = x; + mY = y; + mTileLevel = level; + invalidateContent(); + } + + @Override + protected void onFreeBitmap(final Bitmap bitmap) { + if (sTilePool != null) { + sTilePool.recycle(bitmap); + } + } + + @Override + protected Bitmap onGetBitmap() { + GalleryUtils.assertTrue(mTileState == STATE_DECODED); + + // We need to override the width and height, so that we won't + // draw beyond the boundaries. + final int rightEdge = (mImageWidth - mX >> mTileLevel) + TILE_BORDER; + final int bottomEdge = (mImageHeight - mY >> mTileLevel) + TILE_BORDER; + setSize(Math.min(BITMAP_SIZE, rightEdge), Math.min(BITMAP_SIZE, bottomEdge)); + + final Bitmap bitmap = mDecodedTile; + mDecodedTile = null; + mTileState = STATE_ACTIVATED; + return bitmap; + } + + boolean decode() { + // Get a tile from the original image. The tile is down-scaled + // by (1 << mTilelevel) from a region in the original image. + try { + mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(mTileLevel, mX, mY, TILE_SIZE, + TILE_BORDER, sTilePool)); + } catch (final Throwable t) { + Log.w(TAG, "fail to decode tile", t); + } + return mDecodedTile != null; + } + } + + private class TileDecoder implements ThreadPool.Job { + + private final CancelListener mNotifier = new CancelListener() { + @Override + public void onCancel() { + synchronized (TileImageView.this) { + TileImageView.this.notifyAll(); + } + } + }; + + @Override + public Void run(final JobContext jc) { + jc.setMode(ThreadPool.MODE_NONE); + jc.setCancelListener(mNotifier); + while (!jc.isCancelled()) { + Tile tile = null; + synchronized (TileImageView.this) { + tile = mDecodeQueue.pop(); + if (tile == null && !jc.isCancelled()) { + GalleryUtils.waitWithoutInterrupt(TileImageView.this); + } + } + if (tile == null) { + continue; + } + if (decodeTile(tile)) { + queueForUpload(tile); + } + } + return null; + } + } + + private static class TileQueue { + private Tile mHead; + + public void clean() { + mHead = null; + } + + public Tile pop() { + final Tile tile = mHead; + if (tile != null) { + mHead = tile.mNext; + } + return tile; + } + + public boolean push(final Tile tile) { + final boolean wasEmpty = mHead == null; + tile.mNext = mHead; + mHead = tile; + return wasEmpty; + } + } + + private class TileUploader implements GLRoot.OnGLIdleListener { + AtomicBoolean mActive = new AtomicBoolean(false); + + @Override + public boolean onGLIdle(final GLCanvas canvas, final boolean renderRequested) { + // Skips uploading if there is a pending rendering request. + // Returns true to keep uploading in next rendering loop. + if (renderRequested) return true; + int quota = UPLOAD_LIMIT; + Tile tile = null; + while (quota > 0) { + synchronized (TileImageView.this) { + tile = mUploadQueue.pop(); + } + if (tile == null) { + break; + } + if (!tile.isContentValid()) { + final boolean hasBeenLoaded = tile.isLoaded(); + if (tile.mTileState != STATE_DECODED) return false; + tile.updateContent(canvas); + if (!hasBeenLoaded) { + tile.draw(canvas, 0, 0); + } + --quota; + } + } + if (tile == null) { + mActive.set(false); + } + return tile != null; + } + } } diff --git a/twidere/src/main/java/org/mariotaku/gallery3d/util/GalleryUtils.java b/twidere/src/main/java/org/mariotaku/gallery3d/util/GalleryUtils.java index ec4c3dbf7..eba45eccd 100644 --- a/twidere/src/main/java/org/mariotaku/gallery3d/util/GalleryUtils.java +++ b/twidere/src/main/java/org/mariotaku/gallery3d/util/GalleryUtils.java @@ -110,30 +110,7 @@ public class GalleryUtils { return color >>> 24 == 0xFF; } - // Returns the next power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 or - // the answer overflows. - public static int nextPowerOf2(int n) { - if (n <= 0 || n > 1 << 30) throw new IllegalArgumentException("n is invalid: " + n); - n -= 1; - n |= n >> 16; - n |= n >> 8; - n |= n >> 4; - n |= n >> 2; - n |= n >> 1; - return n + 1; - } - - // Returns the previous power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 - public static int prevPowerOf2(final int n) { - if (n <= 0) throw new IllegalArgumentException(); - return Integer.highestOneBit(n); - } - - public static void waitWithoutInterrupt(final Object object) { + public static void waitWithoutInterrupt(final Object object) { try { object.wait(); } catch (final InterruptedException e) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java index f6ad8e302..15d4b9eae 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/SettingsActivity.java @@ -53,7 +53,7 @@ import org.mariotaku.twidere.activity.support.DataImportActivity; import org.mariotaku.twidere.graphic.EmptyDrawable; import org.mariotaku.twidere.util.CompareUtils; import org.mariotaku.twidere.util.ThemeUtils; -import org.mariotaku.twidere.view.holder.ListViewHolder; +import org.mariotaku.twidere.view.holder.ViewListHolder; import java.util.ArrayList; import java.util.List; @@ -427,7 +427,7 @@ public class SettingsActivity extends BasePreferenceActivity { } - private static class HeaderViewHolder extends ListViewHolder { + private static class HeaderViewHolder extends ViewListHolder { private final TextView title, summary; private final ImageView icon; private final LinearLayout content; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportActivity.java index 3b8f39c1c..0d7bb312b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportActivity.java @@ -38,7 +38,7 @@ import org.mariotaku.twidere.view.MainFrameLayout.FitSystemWindowsCallback; import java.util.ArrayList; @SuppressLint("Registered") -public class BaseSupportActivity extends BaseSupportThemedActivity implements Constants, +public class BaseSupportActivity extends ThemedFragmentActivity implements Constants, FitSystemWindowsCallback, SystemWindowsInsetsCallback, IControlBarActivity { private boolean mInstanceStateSaved, mIsVisible, mIsOnTop; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportDialogActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportDialogActivity.java index c22d7de4a..c922e0950 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportDialogActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportDialogActivity.java @@ -28,7 +28,7 @@ import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.util.ThemeUtils; @SuppressLint("Registered") -public class BaseSupportDialogActivity extends BaseSupportThemedActivity implements Constants, IThemedActivity { +public class BaseSupportDialogActivity extends ThemedFragmentActivity implements Constants, IThemedActivity { private boolean mInstanceStateSaved; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/GlobalSearchBoxActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/GlobalSearchBoxActivity.java new file mode 100644 index 000000000..5d306b69d --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/GlobalSearchBoxActivity.java @@ -0,0 +1,85 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.activity.support; + +import android.content.Intent; +import android.os.Bundle; +import android.view.Gravity; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Spinner; + +import org.mariotaku.twidere.R; +import org.mariotaku.twidere.adapter.AccountsSpinnerAdapter; +import org.mariotaku.twidere.model.ParcelableAccount; +import org.mariotaku.twidere.util.ThemeUtils; + +import java.util.List; + +/** + * Created by mariotaku on 15/1/6. + */ +public class GlobalSearchBoxActivity extends BaseSupportActivity { + + private Spinner mAccountSpinner; + + @Override + public int getThemeResourceId() { + return ThemeUtils.getGlobalSearchThemeResource(this); + } + + @Override + public void onContentChanged() { + super.onContentChanged(); + mAccountSpinner = (Spinner) findViewById(R.id.account_spinner); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_global_search_box); + final List accounts = ParcelableAccount.getAccountsList(this, false); + final AccountsSpinnerAdapter accountsSpinnerAdapter = new AccountsSpinnerAdapter(this, R.layout.spinner_item_account_icon); + accountsSpinnerAdapter.setDropDownViewResource(R.layout.list_item_user); + accountsSpinnerAdapter.addAll(accounts); + mAccountSpinner.setAdapter(accountsSpinnerAdapter); + if (savedInstanceState == null) { + final Intent intent = getIntent(); + final int index = accountsSpinnerAdapter.findItemPosition(intent.getLongExtra(EXTRA_ACCOUNT_ID, -1)); + if (index != -1) { + mAccountSpinner.setSelection(index); + } + } + } + + @Override + protected void onResume() { + super.onResume(); + updateWindowAttributes(); + } + + private void updateWindowAttributes() { + final Window window = getWindow(); + final WindowManager.LayoutParams attributes = window.getAttributes(); + attributes.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + window.setAttributes(attributes); + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ImagePickerActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ImagePickerActivity.java index 7d844edae..ec95af140 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/ImagePickerActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ImagePickerActivity.java @@ -35,7 +35,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -public class ImagePickerActivity extends BaseSupportThemedActivity { +public class ImagePickerActivity extends ThemedFragmentActivity { public static final int REQUEST_PICK_IMAGE = 101; public static final int REQUEST_TAKE_PHOTO = 102; diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/MediaViewerActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/MediaViewerActivity.java new file mode 100644 index 000000000..51dd740d6 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/MediaViewerActivity.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2009 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 org.mariotaku.twidere.activity.support; + +import android.app.ActionBar; +import android.app.ActionBar.OnMenuVisibilityListener; +import android.content.ActivityNotFoundException; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.Loader; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SubMenu; +import android.view.View; +import android.view.View.OnLayoutChangeListener; +import android.widget.ImageView; +import android.widget.ProgressBar; + +import org.mariotaku.menucomponent.widget.MenuBar; +import org.mariotaku.menucomponent.widget.MenuBar.MenuBarListener; +import org.mariotaku.tileimageview.widget.TileImageView; +import org.mariotaku.twidere.Constants; +import org.mariotaku.twidere.R; +import org.mariotaku.twidere.loader.support.TileImageLoader; +import org.mariotaku.twidere.util.SaveImageTask; +import org.mariotaku.twidere.util.ThemeUtils; +import org.mariotaku.twidere.util.Utils; + +import java.io.File; + +public final class MediaViewerActivity extends BaseSupportActivity implements Constants, + TileImageLoader.DownloadListener, LoaderManager.LoaderCallbacks, + OnMenuVisibilityListener, MenuBarListener { + + + private ActionBar mActionBar; + + private ProgressBar mProgress; + private ImageView mImageView; + private MenuBar mMenuBar; + + private long mContentLength; + + private File mImageFile; + private boolean mLoaderInitialized; + + @Override + public int getThemeResourceId() { + return ThemeUtils.getViewerThemeResource(this); + } + + + @Override + public void onContentChanged() { + super.onContentChanged(); + mImageView = (ImageView) findViewById(R.id.image_viewer); + mProgress = (ProgressBar) findViewById(R.id.progress); + mMenuBar = (MenuBar) findViewById(R.id.menu_bar); + } + + @Override + public Loader onCreateLoader(final int id, final Bundle args) { + mProgress.setVisibility(View.VISIBLE); + mProgress.setIndeterminate(true); + invalidateOptionsMenu(); + final Uri uri = args.getParcelable(EXTRA_URI); + final long accountId = args.getLong(EXTRA_ACCOUNT_ID, -1); + return new TileImageLoader(this, this, accountId, uri); + } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.menu_image_viewer_action_bar, menu); + return true; + } + + + @Override + public void onDownloadError(final Throwable t) { + mContentLength = 0; + } + + @Override + public void onDownloadFinished() { + mContentLength = 0; + } + + @Override + public void onDownloadStart(final long total) { + mContentLength = total; + mProgress.setIndeterminate(total <= 0); + mProgress.setMax(total > 0 ? (int) (total / 1024) : 0); + } + + + @Override + public void onLoaderReset(final Loader loader) { + + } + + @Override + public void onLoadFinished(final Loader loader, final TileImageLoader.Result data) { + if (data.hasData()) { + mImageView.setVisibility(View.VISIBLE); +// mImageView.setBitmapRegionDecoder(data.decoder, data.bitmap); +// mImageView.setScale(1); + mImageView.setImageBitmap(data.bitmap); + mImageFile = data.file; + } else { + mImageView.setVisibility(View.GONE); + mImageFile = null; + Utils.showErrorMessage(this, null, data.exception, true); + } + mProgress.setVisibility(View.GONE); + mProgress.setProgress(0); + invalidateOptionsMenu(); + updateShareIntent(); + } + + @Override + public boolean onMenuItemClick(final MenuItem item) { + switch (item.getItemId()) { + case MENU_SAVE: { + if (mImageFile != null) { + new SaveImageTask(this, mImageFile).execute(); + } + break; + } + case MENU_OPEN_IN_BROWSER: { + final Intent intent = getIntent(); + intent.setExtrasClassLoader(getClassLoader()); + final Uri uri = intent.getData(); + final Uri orig = intent.getParcelableExtra(EXTRA_URI_ORIG); + final Uri uriPreferred = orig != null ? orig : uri; + if (uriPreferred == null) return false; + final String scheme = uriPreferred.getScheme(); + if ("http".equals(scheme) || "https".equals(scheme)) { + final Intent open_intent = new Intent(Intent.ACTION_VIEW, uriPreferred); + open_intent.addCategory(Intent.CATEGORY_BROWSABLE); + try { + startActivity(open_intent); + } catch (final ActivityNotFoundException e) { + // Ignore. + } + } + break; + } + default: { + final Intent intent = item.getIntent(); + if (intent != null) { + try { + startActivity(intent); + } catch (final ActivityNotFoundException e) { + // Ignore. + } + return true; + } + return false; + } + } + return true; + } + + @Override + public void onMenuVisibilityChanged(final boolean isVisible) { + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + switch (item.getItemId()) { + case MENU_HOME: { + onBackPressed(); + break; + } + case MENU_REFRESH: { + loadImage(); + break; + } + } + return true; + } + + + @Override + public boolean onPrepareOptionsMenu(final Menu menu) { + final LoaderManager lm = getSupportLoaderManager(); + Utils.setMenuItemAvailability(menu, MENU_REFRESH, !lm.hasRunningLoaders()); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public void onProgressUpdate(final long downloaded) { + if (mContentLength == 0) { + mProgress.setIndeterminate(true); + return; + } + mProgress.setIndeterminate(false); + mProgress.setProgress((int) (downloaded / 1024)); + } + + + public void showProgress() { + mProgress.setVisibility(View.VISIBLE); + mProgress.setIndeterminate(true); + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.image_viewer); + mActionBar = getActionBar(); + mActionBar.setDisplayHomeAsUpEnabled(true); + mActionBar.addOnMenuVisibilityListener(this); + if (savedInstanceState == null) { + loadImage(); + } + +// mImageView.setScaleToFit(false); + mImageView.addOnLayoutChangeListener(new TileImageViewLayoutListener()); + + mMenuBar.setMenuBarListener(this); + mMenuBar.inflate(R.menu.menu_image_viewer); + mMenuBar.setIsBottomBar(true); + mMenuBar.show(); + } + + + private static class TileImageViewLayoutListener implements OnLayoutChangeListener { + @Override + public void onLayoutChange(final View v, final int left, final int top, final int right, final int bottom, + final int oldLeft, final int oldTop, final int oldRight, final int oldBottom) { + if (!(v instanceof TileImageView)) return; + final TileImageView tileView = (TileImageView) v; + final int baseWidth = tileView.getBaseWidth(), baseHeight = tileView.getBaseHeight(); + final double scaleMin = getMinScale(left, top, right, bottom, baseWidth, baseHeight); + tileView.setScaleLimits(scaleMin, Math.max(scaleMin, 2.0)); + final double oldScaleMin = getMinScale(oldLeft, oldTop, oldRight, oldBottom, baseWidth, baseHeight); + final double oldScale = tileView.getScale(); + tileView.setScaleLimits(scaleMin, Math.max(scaleMin, 2.0)); + if (oldScale == oldScaleMin) { + tileView.setScale(scaleMin); + } + } + + private static double getMinScale(final int left, final int top, final int right, final int bottom, + final int baseWidth, final int baseHeight) { + final double viewWidth = right - left, viewHeight = bottom - top; + if (viewWidth <= 0 || viewHeight <= 0) return 0; + final double widthScale = Math.min(1, baseWidth / viewWidth), heightScale = Math.min(1, baseHeight + / viewHeight); + return Math.min(widthScale, heightScale); + } + + } + + @Override + protected void onDestroy() { + mActionBar.removeOnMenuVisibilityListener(this); + super.onDestroy(); + + } + + @Override + protected void onNewIntent(final Intent intent) { + setIntent(intent); + loadImage(); + } + + @Override + protected void onPause() { + super.onPause(); + + } + + @Override + protected void onResume() { + super.onResume(); + + } + + + private void loadImage() { + getSupportLoaderManager().destroyLoader(0); + final Intent intent = getIntent(); + final Uri uri = intent.getData(); + final long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1); + if (uri == null) { + finish(); + return; + } + final Bundle args = new Bundle(); + args.putParcelable(EXTRA_URI, uri); + args.putLong(EXTRA_ACCOUNT_ID, accountId); + if (!mLoaderInitialized) { + getSupportLoaderManager().initLoader(0, args, this); + mLoaderInitialized = true; + } else { + getSupportLoaderManager().restartLoader(0, args, this); + } + } + + + void updateShareIntent() { + final MenuItem item = mMenuBar.getMenu().findItem(MENU_SHARE); + if (item == null || !item.hasSubMenu()) return; + final SubMenu subMenu = item.getSubMenu(); + subMenu.clear(); + final Intent intent = getIntent(); + final Uri uri = intent.getData(); + final Intent shareIntent = new Intent(Intent.ACTION_SEND); + if (mImageFile != null && mImageFile.exists()) { + shareIntent.setType("image/*"); + shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(mImageFile)); + } else { + shareIntent.setType("text/plain"); + shareIntent.putExtra(Intent.EXTRA_TEXT, uri.toString()); + } + Utils.addIntentToMenu(this, subMenu, shareIntent); + } + + @Override + public void onPreShowMenu(Menu menu) { + + } + + @Override + public void onPostShowMenu(Menu menu) { + + } + +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportThemedActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ThemedFragmentActivity.java similarity index 89% rename from twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportThemedActivity.java rename to twidere/src/main/java/org/mariotaku/twidere/activity/support/ThemedFragmentActivity.java index ddd3bf312..5248d267c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/BaseSupportThemedActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/ThemedFragmentActivity.java @@ -36,12 +36,16 @@ import org.mariotaku.twidere.activity.iface.IThemedActivity; import org.mariotaku.twidere.util.StrictModeUtils; import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.Utils; +import org.mariotaku.twidere.view.ShapedImageView; +import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle; import static org.mariotaku.twidere.util.Utils.restartActivity; -public abstract class BaseSupportThemedActivity extends FragmentActivity implements Constants, IThemedActivity { +public abstract class ThemedFragmentActivity extends FragmentActivity implements Constants, IThemedActivity { private int mCurrentThemeResource, mCurrentThemeColor, mCurrentThemeBackgroundAlpha; + @ShapeStyle + private int mProfileImageStyle; @Override public Resources getDefaultResources() { @@ -112,6 +116,10 @@ public abstract class BaseSupportThemedActivity extends FragmentActivity impleme @Override public View onCreateView(String name, @NonNull Context context, @NonNull AttributeSet attrs) { final View view = ThemeUtils.createView(name, context, attrs, mCurrentThemeColor); + if (view instanceof ShapedImageView) { + final ShapedImageView shapedImageView = (ShapedImageView) view; + shapedImageView.setStyle(mProfileImageStyle); + } if (view != null) return view; return super.onCreateView(name, context, attrs); } @@ -129,6 +137,7 @@ public abstract class BaseSupportThemedActivity extends FragmentActivity impleme mCurrentThemeResource = getThemeResourceId(); mCurrentThemeColor = getThemeColor(); mCurrentThemeBackgroundAlpha = getThemeBackgroundAlpha(); + mProfileImageStyle = Utils.getProfileImageStyle(this); ThemeUtils.notifyStatusBarColorChanged(this, mCurrentThemeResource, mCurrentThemeColor, mCurrentThemeBackgroundAlpha); setTheme(mCurrentThemeResource); diff --git a/twidere/src/main/java/org/mariotaku/twidere/activity/support/UserListSelectorActivity.java b/twidere/src/main/java/org/mariotaku/twidere/activity/support/UserListSelectorActivity.java index 037e094f1..dd127571d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/activity/support/UserListSelectorActivity.java +++ b/twidere/src/main/java/org/mariotaku/twidere/activity/support/UserListSelectorActivity.java @@ -19,11 +19,6 @@ package org.mariotaku.twidere.activity.support; -import static android.text.TextUtils.isEmpty; -import static org.mariotaku.twidere.util.ParseUtils.parseString; -import static org.mariotaku.twidere.util.Utils.getAccountScreenName; -import static org.mariotaku.twidere.util.Utils.getTwitterInstance; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -49,6 +44,9 @@ import org.mariotaku.twidere.model.ParcelableUserList; import org.mariotaku.twidere.model.SingleResponse; import org.mariotaku.twidere.task.TwidereAsyncTask; +import java.util.ArrayList; +import java.util.List; + import twitter4j.ResponseList; import twitter4j.Twitter; import twitter4j.TwitterException; @@ -56,284 +54,286 @@ import twitter4j.User; import twitter4j.UserList; import twitter4j.http.HttpResponseCode; -import java.util.ArrayList; -import java.util.List; +import static android.text.TextUtils.isEmpty; +import static org.mariotaku.twidere.util.ParseUtils.parseString; +import static org.mariotaku.twidere.util.Utils.getAccountScreenName; +import static org.mariotaku.twidere.util.Utils.getTwitterInstance; public class UserListSelectorActivity extends BaseSupportDialogActivity implements OnClickListener, OnItemClickListener { - private AutoCompleteTextView mEditScreenName; - private ListView mUserListsListView, mUsersListView; - private SimpleParcelableUserListsAdapter mUserListsAdapter; - private SimpleParcelableUsersAdapter mUsersAdapter; - private View mUsersListContainer, mUserListsContainer, mCreateUserListContainer; + private AutoCompleteTextView mEditScreenName; + private ListView mUserListsListView, mUsersListView; + private SimpleParcelableUserListsAdapter mUserListsAdapter; + private SimpleParcelableUsersAdapter mUsersAdapter; + private View mUsersListContainer, mUserListsContainer, mCreateUserListContainer; - private String mScreenName; + private String mScreenName; - private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mStatusReceiver = new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - final String action = intent.getAction(); - if (BROADCAST_USER_LIST_CREATED.equals(action)) { - getUserLists(mScreenName); - } - } - }; + @Override + public void onReceive(final Context context, final Intent intent) { + final String action = intent.getAction(); + if (BROADCAST_USER_LIST_CREATED.equals(action)) { + getUserLists(mScreenName); + } + } + }; - @Override - public void onClick(final View v) { - switch (v.getId()) { - case R.id.screen_name_confirm: { - final String screen_name = parseString(mEditScreenName.getText()); - if (isEmpty(screen_name)) return; - searchUser(screen_name); - break; - } - case R.id.create_list: { - final DialogFragment f = new CreateUserListDialogFragment(); - final Bundle args = new Bundle(); - args.putLong(EXTRA_ACCOUNT_ID, getAccountId()); - f.setArguments(args); - f.show(getSupportFragmentManager(), null); - break; - } - } - } + @Override + public void onClick(final View v) { + switch (v.getId()) { + case R.id.screen_name_confirm: { + final String screen_name = parseString(mEditScreenName.getText()); + if (isEmpty(screen_name)) return; + searchUser(screen_name); + break; + } + case R.id.create_list: { + final DialogFragment f = new CreateUserListDialogFragment(); + final Bundle args = new Bundle(); + args.putLong(EXTRA_ACCOUNT_ID, getAccountId()); + f.setArguments(args); + f.show(getSupportFragmentManager(), null); + break; + } + } + } - @Override - public void onContentChanged() { - super.onContentChanged(); - mUsersListContainer = findViewById(R.id.users_list_container); - mUserListsContainer = findViewById(R.id.user_lists_container); - mEditScreenName = (AutoCompleteTextView) findViewById(R.id.edit_screen_name); - mUserListsListView = (ListView) findViewById(R.id.user_lists_list); - mUsersListView = (ListView) findViewById(R.id.users_list); - mCreateUserListContainer = findViewById(R.id.create_list_container); - } + @Override + public void onContentChanged() { + super.onContentChanged(); + mUsersListContainer = findViewById(R.id.users_list_container); + mUserListsContainer = findViewById(R.id.user_lists_container); + mEditScreenName = (AutoCompleteTextView) findViewById(R.id.edit_screen_name); + mUserListsListView = (ListView) findViewById(R.id.user_lists_list); + mUsersListView = (ListView) findViewById(R.id.users_list); + mCreateUserListContainer = findViewById(R.id.create_list_container); + } - @Override - public void onItemClick(final AdapterView view, final View child, final int position, final long id) { - final int view_id = view.getId(); - final ListView list = (ListView) view; - if (view_id == R.id.users_list) { - final ParcelableUser user = mUsersAdapter.getItem(position - list.getHeaderViewsCount()); - if (user == null) return; - if (isSelectingUser()) { - final Intent data = new Intent(); - data.setExtrasClassLoader(getClassLoader()); - data.putExtra(EXTRA_USER, user); - setResult(RESULT_OK, data); - finish(); - } else { - getUserLists(user.screen_name); - } - } else if (view_id == R.id.user_lists_list) { - final Intent data = new Intent(); - data.putExtra(EXTRA_USER_LIST, mUserListsAdapter.getItem(position - list.getHeaderViewsCount())); - setResult(RESULT_OK, data); - finish(); - } - } + @Override + public void onItemClick(final AdapterView view, final View child, final int position, final long id) { + final int view_id = view.getId(); + final ListView list = (ListView) view; + if (view_id == R.id.users_list) { + final ParcelableUser user = mUsersAdapter.getItem(position - list.getHeaderViewsCount()); + if (user == null) return; + if (isSelectingUser()) { + final Intent data = new Intent(); + data.setExtrasClassLoader(getClassLoader()); + data.putExtra(EXTRA_USER, user); + setResult(RESULT_OK, data); + finish(); + } else { + getUserLists(user.screen_name); + } + } else if (view_id == R.id.user_lists_list) { + final Intent data = new Intent(); + data.putExtra(EXTRA_USER_LIST, mUserListsAdapter.getItem(position - list.getHeaderViewsCount())); + setResult(RESULT_OK, data); + finish(); + } + } - public void setUsersData(final List data) { - mUsersAdapter.setData(data, true); - mUsersListContainer.setVisibility(View.VISIBLE); - mUserListsContainer.setVisibility(View.GONE); - } + public void setUsersData(final List data) { + mUsersAdapter.setData(data, true); + mUsersListContainer.setVisibility(View.VISIBLE); + mUserListsContainer.setVisibility(View.GONE); + } - @Override - protected void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - final Intent intent = getIntent(); - if (!intent.hasExtra(EXTRA_ACCOUNT_ID)) { - finish(); - return; - } - setContentView(R.layout.activity_user_list_selector); - if (savedInstanceState == null) { - mScreenName = intent.getStringExtra(EXTRA_SCREEN_NAME); - } else { - mScreenName = savedInstanceState.getString(EXTRA_SCREEN_NAME); - } + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Intent intent = getIntent(); + if (!intent.hasExtra(EXTRA_ACCOUNT_ID)) { + finish(); + return; + } + setContentView(R.layout.activity_user_list_selector); + if (savedInstanceState == null) { + mScreenName = intent.getStringExtra(EXTRA_SCREEN_NAME); + } else { + mScreenName = savedInstanceState.getString(EXTRA_SCREEN_NAME); + } - final boolean selecting_user = isSelectingUser(); - setTitle(selecting_user ? R.string.select_user : R.string.select_user_list); - if (!isEmpty(mScreenName)) { - if (selecting_user) { - searchUser(mScreenName); - } else { - getUserLists(mScreenName); - } - } - mEditScreenName.setAdapter(new UserHashtagAutoCompleteAdapter(this)); - mEditScreenName.setText(mScreenName); - mUserListsListView.setAdapter(mUserListsAdapter = new SimpleParcelableUserListsAdapter(this)); - mUsersListView.setAdapter(mUsersAdapter = new SimpleParcelableUsersAdapter(this)); - mUserListsListView.setOnItemClickListener(this); - mUsersListView.setOnItemClickListener(this); - if (selecting_user) { - mUsersListContainer.setVisibility(View.VISIBLE); - mUserListsContainer.setVisibility(View.GONE); - } else { - mUsersListContainer.setVisibility(isEmpty(mScreenName) ? View.VISIBLE : View.GONE); - mUserListsContainer.setVisibility(isEmpty(mScreenName) ? View.GONE : View.VISIBLE); - } - } + final boolean selecting_user = isSelectingUser(); + setTitle(selecting_user ? R.string.select_user : R.string.select_user_list); + if (!isEmpty(mScreenName)) { + if (selecting_user) { + searchUser(mScreenName); + } else { + getUserLists(mScreenName); + } + } + mEditScreenName.setAdapter(new UserHashtagAutoCompleteAdapter(this)); + mEditScreenName.setText(mScreenName); + mUserListsListView.setAdapter(mUserListsAdapter = new SimpleParcelableUserListsAdapter(this)); + mUsersListView.setAdapter(mUsersAdapter = new SimpleParcelableUsersAdapter(this)); + mUserListsListView.setOnItemClickListener(this); + mUsersListView.setOnItemClickListener(this); + if (selecting_user) { + mUsersListContainer.setVisibility(View.VISIBLE); + mUserListsContainer.setVisibility(View.GONE); + } else { + mUsersListContainer.setVisibility(isEmpty(mScreenName) ? View.VISIBLE : View.GONE); + mUserListsContainer.setVisibility(isEmpty(mScreenName) ? View.GONE : View.VISIBLE); + } + } - @Override - protected void onSaveInstanceState(final Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(EXTRA_SCREEN_NAME, mScreenName); - } + @Override + protected void onSaveInstanceState(final Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(EXTRA_SCREEN_NAME, mScreenName); + } - @Override - protected void onStart() { - super.onStart(); - final IntentFilter filter = new IntentFilter(BROADCAST_USER_LIST_CREATED); - registerReceiver(mStatusReceiver, filter); - } + @Override + protected void onStart() { + super.onStart(); + final IntentFilter filter = new IntentFilter(BROADCAST_USER_LIST_CREATED); + registerReceiver(mStatusReceiver, filter); + } - @Override - protected void onStop() { - unregisterReceiver(mStatusReceiver); - super.onStop(); - } + @Override + protected void onStop() { + unregisterReceiver(mStatusReceiver); + super.onStop(); + } - private long getAccountId() { - return getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1); - } + private long getAccountId() { + return getIntent().getLongExtra(EXTRA_ACCOUNT_ID, -1); + } - private void getUserLists(final String screen_name) { - if (screen_name == null) return; - mScreenName = screen_name; - final GetUserListsTask task = new GetUserListsTask(this, getAccountId(), screen_name); - task.executeTask(); - } + private void getUserLists(final String screen_name) { + if (screen_name == null) return; + mScreenName = screen_name; + final GetUserListsTask task = new GetUserListsTask(this, getAccountId(), screen_name); + task.executeTask(); + } - private boolean isSelectingUser() { - return INTENT_ACTION_SELECT_USER.equals(getIntent().getAction()); - } + private boolean isSelectingUser() { + return INTENT_ACTION_SELECT_USER.equals(getIntent().getAction()); + } - private void searchUser(final String name) { - final SearchUsersTask task = new SearchUsersTask(this, getAccountId(), name); - task.executeTask(); - } + private void searchUser(final String name) { + final SearchUsersTask task = new SearchUsersTask(this, getAccountId(), name); + task.executeTask(); + } - private void setUserListsData(final List data, final boolean is_my_account) { - mUserListsAdapter.setData(data, true); - mUsersListContainer.setVisibility(View.GONE); - mUserListsContainer.setVisibility(View.VISIBLE); - mCreateUserListContainer.setVisibility(is_my_account ? View.VISIBLE : View.GONE); - } + private void setUserListsData(final List data, final boolean is_my_account) { + mUserListsAdapter.setData(data, true); + mUsersListContainer.setVisibility(View.GONE); + mUserListsContainer.setVisibility(View.VISIBLE); + mCreateUserListContainer.setVisibility(is_my_account ? View.VISIBLE : View.GONE); + } - private static class GetUserListsTask extends TwidereAsyncTask>> { + private static class GetUserListsTask extends TwidereAsyncTask>> { - private static final String FRAGMENT_TAG_GET_USER_LISTS = "get_user_lists"; - private final UserListSelectorActivity mActivity; - private final long mAccountId; - private final String mScreenName; + private static final String FRAGMENT_TAG_GET_USER_LISTS = "get_user_lists"; + private final UserListSelectorActivity mActivity; + private final long mAccountId; + private final String mScreenName; - GetUserListsTask(final UserListSelectorActivity activity, final long account_id, final String screen_name) { - mActivity = activity; - mAccountId = account_id; - mScreenName = screen_name; - } + GetUserListsTask(final UserListSelectorActivity activity, final long account_id, final String screen_name) { + mActivity = activity; + mAccountId = account_id; + mScreenName = screen_name; + } - @Override - protected SingleResponse> doInBackground(final Void... params) { - final Twitter twitter = getTwitterInstance(mActivity, mAccountId, false); - if (twitter == null) return SingleResponse.getInstance(); - try { - final ResponseList lists = twitter.getUserLists(mScreenName); - final List data = new ArrayList(); - boolean is_my_account = mScreenName.equalsIgnoreCase(getAccountScreenName(mActivity, mAccountId)); - for (final UserList item : lists) { - final User user = item.getUser(); - if (user != null && mScreenName.equalsIgnoreCase(user.getScreenName())) { - if (!is_my_account && user.getId() == mAccountId) { - is_my_account = true; - } - data.add(new ParcelableUserList(item, mAccountId)); - } - } - final SingleResponse> result = SingleResponse.getInstance(data); - result.getExtras().putBoolean(EXTRA_IS_MY_ACCOUNT, is_my_account); - return result; - } catch (final TwitterException e) { - e.printStackTrace(); - return SingleResponse.getInstance(e); - } - } + @Override + protected SingleResponse> doInBackground(final Void... params) { + final Twitter twitter = getTwitterInstance(mActivity, mAccountId, false); + if (twitter == null) return SingleResponse.getInstance(); + try { + final ResponseList lists = twitter.getUserLists(mScreenName, true); + final List data = new ArrayList(); + boolean is_my_account = mScreenName.equalsIgnoreCase(getAccountScreenName(mActivity, mAccountId)); + for (final UserList item : lists) { + final User user = item.getUser(); + if (user != null && mScreenName.equalsIgnoreCase(user.getScreenName())) { + if (!is_my_account && user.getId() == mAccountId) { + is_my_account = true; + } + data.add(new ParcelableUserList(item, mAccountId)); + } + } + final SingleResponse> result = SingleResponse.getInstance(data); + result.getExtras().putBoolean(EXTRA_IS_MY_ACCOUNT, is_my_account); + return result; + } catch (final TwitterException e) { + e.printStackTrace(); + return SingleResponse.getInstance(e); + } + } - @Override - protected void onPostExecute(final SingleResponse> result) { - final Fragment f = mActivity.getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_GET_USER_LISTS); - if (f instanceof DialogFragment) { - ((DialogFragment) f).dismiss(); - } - if (result.getData() != null) { - mActivity.setUserListsData(result.getData(), result.getExtras().getBoolean(EXTRA_IS_MY_ACCOUNT)); - } else if (result.getException() instanceof TwitterException) { - final TwitterException te = (TwitterException) result.getException(); - if (te.getStatusCode() == HttpResponseCode.NOT_FOUND) { - mActivity.searchUser(mScreenName); - } - } - } + @Override + protected void onPostExecute(final SingleResponse> result) { + final Fragment f = mActivity.getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_GET_USER_LISTS); + if (f instanceof DialogFragment) { + ((DialogFragment) f).dismiss(); + } + if (result.getData() != null) { + mActivity.setUserListsData(result.getData(), result.getExtras().getBoolean(EXTRA_IS_MY_ACCOUNT)); + } else if (result.getException() instanceof TwitterException) { + final TwitterException te = (TwitterException) result.getException(); + if (te.getStatusCode() == HttpResponseCode.NOT_FOUND) { + mActivity.searchUser(mScreenName); + } + } + } - @Override - protected void onPreExecute() { - SupportProgressDialogFragment.show(mActivity, FRAGMENT_TAG_GET_USER_LISTS).setCancelable(false); - } + @Override + protected void onPreExecute() { + SupportProgressDialogFragment.show(mActivity, FRAGMENT_TAG_GET_USER_LISTS).setCancelable(false); + } - } + } - private static class SearchUsersTask extends TwidereAsyncTask>> { + private static class SearchUsersTask extends TwidereAsyncTask>> { - private static final String FRAGMENT_TAG_SEARCH_USERS = "search_users"; - private final UserListSelectorActivity mActivity; - private final long mAccountId; - private final String mName; + private static final String FRAGMENT_TAG_SEARCH_USERS = "search_users"; + private final UserListSelectorActivity mActivity; + private final long mAccountId; + private final String mName; - SearchUsersTask(final UserListSelectorActivity activity, final long account_id, final String name) { - mActivity = activity; - mAccountId = account_id; - mName = name; - } + SearchUsersTask(final UserListSelectorActivity activity, final long account_id, final String name) { + mActivity = activity; + mAccountId = account_id; + mName = name; + } - @Override - protected SingleResponse> doInBackground(final Void... params) { - final Twitter twitter = getTwitterInstance(mActivity, mAccountId, false); - if (twitter == null) return SingleResponse.getInstance(); - try { - final ResponseList lists = twitter.searchUsers(mName, 1); - final List data = new ArrayList(); - for (final User item : lists) { - data.add(new ParcelableUser(item, mAccountId)); - } - return SingleResponse.getInstance(data); - } catch (final TwitterException e) { - e.printStackTrace(); - return SingleResponse.getInstance(e); - } - } + @Override + protected SingleResponse> doInBackground(final Void... params) { + final Twitter twitter = getTwitterInstance(mActivity, mAccountId, false); + if (twitter == null) return SingleResponse.getInstance(); + try { + final ResponseList lists = twitter.searchUsers(mName, 1); + final List data = new ArrayList<>(); + for (final User item : lists) { + data.add(new ParcelableUser(item, mAccountId)); + } + return SingleResponse.getInstance(data); + } catch (final TwitterException e) { + e.printStackTrace(); + return SingleResponse.getInstance(e); + } + } - @Override - protected void onPostExecute(final SingleResponse> result) { - final Fragment f = mActivity.getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_SEARCH_USERS); - if (f instanceof DialogFragment) { - ((DialogFragment) f).dismiss(); - } - if (result.getData() != null) { - mActivity.setUsersData(result.getData()); - } - } + @Override + protected void onPostExecute(final SingleResponse> result) { + final Fragment f = mActivity.getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG_SEARCH_USERS); + if (f instanceof DialogFragment) { + ((DialogFragment) f).dismiss(); + } + if (result.getData() != null) { + mActivity.setUsersData(result.getData()); + } + } - @Override - protected void onPreExecute() { - SupportProgressDialogFragment.show(mActivity, FRAGMENT_TAG_SEARCH_USERS).setCancelable(false); - } + @Override + protected void onPreExecute() { + SupportProgressDialogFragment.show(mActivity, FRAGMENT_TAG_SEARCH_USERS).setCancelable(false); + } - } + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java index 2c7631945..b3a0c7cf8 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsActivitiesAdapter.java @@ -20,11 +20,14 @@ package org.mariotaku.twidere.adapter; import android.content.Context; +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.util.Pair; +import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; @@ -32,6 +35,7 @@ import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.adapter.iface.IActivitiesAdapter; import org.mariotaku.twidere.app.TwidereApplication; +import org.mariotaku.twidere.fragment.support.UserFragment; import org.mariotaku.twidere.model.ParcelableActivity; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.util.AsyncTwitterWrapper; @@ -44,12 +48,13 @@ import org.mariotaku.twidere.view.holder.ActivityTitleSummaryViewHolder; import org.mariotaku.twidere.view.holder.GapViewHolder; import org.mariotaku.twidere.view.holder.LoadIndicatorViewHolder; import org.mariotaku.twidere.view.holder.StatusViewHolder; +import org.mariotaku.twidere.view.holder.StatusViewHolder.StatusClickListener; /** * Created by mariotaku on 15/1/3. */ public abstract class AbsActivitiesAdapter extends Adapter implements Constants, - IActivitiesAdapter, OnClickListener { + IActivitiesAdapter, StatusClickListener { private static final int ITEM_VIEW_TYPE_STUB = 0; private static final int ITEM_VIEW_TYPE_GAP = 1; @@ -65,9 +70,10 @@ public abstract class AbsActivitiesAdapter extends Adapter imp private final int mCardBackgroundColor; private final int mTextSize; private final int mProfileImageStyle, mMediaPreviewStyle; + private final boolean mCompactCards; private boolean mLoadMoreIndicatorEnabled; - protected AbsActivitiesAdapter(final Context context) { + protected AbsActivitiesAdapter(final Context context, boolean compact) { mContext = context; final TwidereApplication app = TwidereApplication.getInstance(context); mCardBackgroundColor = ThemeUtils.getCardBackgroundColor(context); @@ -78,6 +84,7 @@ public abstract class AbsActivitiesAdapter extends Adapter imp final SharedPreferencesWrapper preferences = SharedPreferencesWrapper.getInstance(context, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); mTextSize = preferences.getInt(KEY_TEXT_SIZE, context.getResources().getInteger(R.integer.default_text_size)); + mCompactCards = compact; mProfileImageStyle = Utils.getProfileImageStyle(preferences.getString(KEY_PROFILE_IMAGE_STYLE, null)); mMediaPreviewStyle = Utils.getMediaPreviewStyle(preferences.getString(KEY_MEDIA_PREVIEW_STYLE, null)); } @@ -86,19 +93,39 @@ public abstract class AbsActivitiesAdapter extends Adapter imp public abstract int getActivityCount(); - @Override - public void onClick(View v) { - - } @Override public void onStatusClick(StatusViewHolder holder, int position) { - + final ParcelableActivity activity = getActivity(position); + final ParcelableStatus status; + if (activity.action == ParcelableActivity.ACTION_MENTION) { + status = activity.target_object_statuses[0]; + } else { + status = activity.target_statuses[0]; + } + Utils.openStatus(getContext(), status, null); } @Override public void onUserProfileClick(StatusViewHolder holder, int position) { - + final Context context = getContext(); + final ParcelableActivity activity = getActivity(position); + final ParcelableStatus status; + if (activity.action == ParcelableActivity.ACTION_MENTION) { + status = activity.target_object_statuses[0]; + } else { + status = activity.target_statuses[0]; + } + final View profileImageView = holder.getProfileImageView(); + final View profileTypeView = holder.getProfileTypeView(); + if (context instanceof FragmentActivity) { + final Bundle options = Utils.makeSceneTransitionOption((FragmentActivity) context, + new Pair<>(profileImageView, UserFragment.TRANSITION_NAME_PROFILE_IMAGE), + new Pair<>(profileTypeView, UserFragment.TRANSITION_NAME_PROFILE_TYPE)); + Utils.openUserProfile(context, status.account_id, status.user_id, status.user_screen_name, options); + } else { + Utils.openUserProfile(context, status.account_id, status.user_id, status.user_screen_name, null); + } } public abstract Data getData(); @@ -144,18 +171,42 @@ public abstract class AbsActivitiesAdapter extends Adapter imp return mLoadMoreIndicatorEnabled; } + @Override + public void onItemMenuClick(ViewHolder holder, int position) { + + } + + @Override + public void onItemActionClick(ViewHolder holder, int id, int position) { + + } + @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType) { case ITEM_VIEW_TYPE_STATUS: { - final View view = mInflater.inflate(R.layout.card_item_status_compat, parent, false); + final View view; + if (mCompactCards) { + view = mInflater.inflate(R.layout.card_item_status_compact, parent, false); + } else { + view = mInflater.inflate(R.layout.card_item_status, parent, false); + final CardView cardView = (CardView) view.findViewById(R.id.card); + cardView.setCardBackgroundColor(mCardBackgroundColor); + } final StatusViewHolder holder = new StatusViewHolder(view); holder.setTextSize(getTextSize()); - holder.setOnClickListeners(this); + holder.setStatusClickListener(this); return holder; } case ITEM_VIEW_TYPE_TITLE_SUMMARY: { - final View view = mInflater.inflate(R.layout.list_item_activity_title_summary, parent, false); + final View view; + if (mCompactCards) { + view = mInflater.inflate(R.layout.card_item_activity_summary_compact, parent, false); + } else { + view = mInflater.inflate(R.layout.card_item_activity_summary, parent, false); + final CardView cardView = (CardView) view.findViewById(R.id.card); + cardView.setCardBackgroundColor(mCardBackgroundColor); + } final ActivityTitleSummaryViewHolder holder = new ActivityTitleSummaryViewHolder(this, view); holder.setTextSize(getTextSize()); return holder; @@ -207,7 +258,7 @@ public abstract class AbsActivitiesAdapter extends Adapter imp @Override public int getItemViewType(int position) { - if (position == getItemCount() - 1) { + if (position == getActivityCount()) { return ITEM_VIEW_TYPE_LOAD_INDICATOR; } else if (isGapItem(position)) { return ITEM_VIEW_TYPE_GAP; diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java index 0de496484..22278546c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/AbsStatusesAdapter.java @@ -61,7 +61,7 @@ public abstract class AbsStatusesAdapter extends Adapter implemen SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); mTextSize = preferences.getInt(KEY_TEXT_SIZE, context.getResources().getInteger(R.integer.default_text_size)); if (compact) { - mCardLayoutResource = R.layout.card_item_status_compat; + mCardLayoutResource = R.layout.card_item_status_compact; } else { mCardLayoutResource = R.layout.card_item_status; } @@ -173,7 +173,7 @@ public abstract class AbsStatusesAdapter extends Adapter implemen @Override public int getItemViewType(int position) { - if (position == getItemCount() - 1) { + if (position == getStatusCount()) { return ITEM_VIEW_TYPE_LOAD_INDICATOR; } else if (isGapItem(position)) { return ITEM_VIEW_TYPE_GAP; diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.java index d24e49718..7d2866a47 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/ParcelableActivitiesAdapter.java @@ -34,8 +34,8 @@ public class ParcelableActivitiesAdapter extends AbsActivitiesAdapter mData; - public ParcelableActivitiesAdapter(Context context) { - super(context); + public ParcelableActivitiesAdapter(Context context, boolean compact) { + super(context,compact); } @Override @@ -57,7 +57,7 @@ public class ParcelableActivitiesAdapter extends AbsActivitiesAdapter implements IBaseCardAdapter, +public class ParcelableUserListsListAdapter extends BaseArrayAdapter implements IBaseCardAdapter, OnClickListener { private final Context mContext; @@ -52,11 +51,11 @@ public class ParcelableUserListsAdapter extends BaseArrayAdapter imp public View getView(final int position, final View convertView, final ViewGroup parent) { final View view = super.getView(position, convertView, parent); final Object tag = view.getTag(); - final UserListViewHolder holder; - if (tag instanceof UserListViewHolder) { - holder = (UserListViewHolder) tag; + final UserViewListHolder holder; + if (tag instanceof UserViewListHolder) { + holder = (UserViewListHolder) tag; } else { - holder = new UserListViewHolder(view); + holder = new UserViewListHolder(view); // holder.content.setOnOverflowIconClickListener(this); view.setTag(holder); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/decorator/DividerItemDecoration.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/decorator/DividerItemDecoration.java index ce92addc7..1c0af43a7 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/decorator/DividerItemDecoration.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/decorator/DividerItemDecoration.java @@ -42,6 +42,7 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration { private int mOrientation; private Rect mPadding; + private int mDecorationStart = -1, mDecorationEnd = -1, mDecorationEndOffset; public DividerItemDecoration(Context context, int orientation) { mPadding = new Rect(); @@ -51,6 +52,21 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration { setOrientation(orientation); } + + public void setDecorationStart(int start) { + mDecorationStart = start; + } + + public void setDecorationEnd(int end) { + mDecorationEnd = end; + mDecorationEndOffset = -1; + } + + public void setDecorationEndOffset(int endOffset) { + mDecorationEndOffset = endOffset; + mDecorationEnd = -1; + } + public void setOrientation(int orientation) { if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) { throw new IllegalArgumentException("invalid orientation"); @@ -78,6 +94,9 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); + final int childPos = parent.getChildPosition(child); + final int start = getDecorationStart(), end = getDecorationEnd(parent); + if (start >= 0 && end >= 0 && (childPos < start || childPos > end)) continue; final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int top = child.getBottom() + params.bottomMargin + @@ -96,6 +115,9 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration { final int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { final View child = parent.getChildAt(i); + final int childPos = parent.getChildPosition(child); + final int start = getDecorationStart(), end = getDecorationEnd(parent); + if (start >= 0 && end >= 0 && (childPos < start || childPos > end)) continue; final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child .getLayoutParams(); final int left = child.getRight() + params.rightMargin + @@ -109,8 +131,9 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration { @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) { - final Adapter adapter = parent.getAdapter(); - if (adapter != null && parent.getChildPosition(view) == adapter.getItemCount() - 1) { + final int childPos = parent.getChildPosition(view); + final int start = getDecorationStart(), end = getDecorationEnd(parent); + if (start >= 0 && end >= 0 && childPos < start && childPos > end) { outRect.setEmpty(); return; } @@ -120,4 +143,17 @@ public class DividerItemDecoration extends RecyclerView.ItemDecoration { outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0); } } + + private int getDecorationEnd(RecyclerView parent) { + if (mDecorationEnd != -1) return mDecorationEnd; + if (mDecorationEndOffset != -1) { + final Adapter adapter = parent.getAdapter(); + return adapter.getItemCount() - 1 - mDecorationEndOffset; + } + return -1; + } + + private int getDecorationStart() { + return mDecorationStart; + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/ICardSupportedAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/ContentCardClickListener.java similarity index 96% rename from twidere/src/main/java/org/mariotaku/twidere/adapter/iface/ICardSupportedAdapter.java rename to twidere/src/main/java/org/mariotaku/twidere/adapter/iface/ContentCardClickListener.java index 9a003054f..589ec1cee 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/ICardSupportedAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/ContentCardClickListener.java @@ -24,7 +24,7 @@ import android.support.v7.widget.RecyclerView.ViewHolder; /** * Created by mariotaku on 14/12/3. */ -public interface ICardSupportedAdapter { +public interface ContentCardClickListener { void onItemActionClick(ViewHolder holder, int id, int position); void onItemMenuClick(ViewHolder holder, int position); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java index 1bb8ffb7c..f0b6c348a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IActivitiesAdapter.java @@ -20,22 +20,16 @@ package org.mariotaku.twidere.adapter.iface; import org.mariotaku.twidere.model.ParcelableActivity; -import org.mariotaku.twidere.view.holder.StatusViewHolder; /** * Created by mariotaku on 14/11/18. */ public interface IActivitiesAdapter extends IContentCardAdapter { - ParcelableActivity getActivity(int position); int getActivityCount(); - void onStatusClick(StatusViewHolder holder, int position); - - void onUserProfileClick(StatusViewHolder holder, int position); - void setData(Data data); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IContentCardAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IContentCardAdapter.java index e69a4a496..d153cbfb7 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IContentCardAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IContentCardAdapter.java @@ -20,7 +20,6 @@ package org.mariotaku.twidere.adapter.iface; import android.content.Context; -import android.support.v7.widget.RecyclerView.ViewHolder; import org.mariotaku.twidere.util.AsyncTwitterWrapper; import org.mariotaku.twidere.util.ImageLoaderWrapper; @@ -29,7 +28,7 @@ import org.mariotaku.twidere.util.ImageLoadingHandler; /** * Created by mariotaku on 15/1/3. */ -public interface IContentCardAdapter extends IGapSupportedAdapter, ICardSupportedAdapter { +public interface IContentCardAdapter extends IGapSupportedAdapter, ContentCardClickListener { ImageLoaderWrapper getImageLoader(); Context getContext(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java index 738d143d0..311652390 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java +++ b/twidere/src/main/java/org/mariotaku/twidere/adapter/iface/IStatusesAdapter.java @@ -2,11 +2,12 @@ package org.mariotaku.twidere.adapter.iface; import org.mariotaku.twidere.model.ParcelableStatus; import org.mariotaku.twidere.view.holder.StatusViewHolder; +import org.mariotaku.twidere.view.holder.StatusViewHolder.StatusClickListener; /** * Created by mariotaku on 14/11/18. */ -public interface IStatusesAdapter extends IContentCardAdapter { +public interface IStatusesAdapter extends IContentCardAdapter, StatusClickListener { ParcelableStatus getStatus(int position); @@ -14,7 +15,4 @@ public interface IStatusesAdapter extends IContentCardAdapter { void setData(Data data); - void onUserProfileClick(StatusViewHolder holder, int position); - - void onStatusClick(StatusViewHolder holder, int position); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseWebViewFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseWebViewFragment.java index 46690390d..7aba261d6 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseWebViewFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/BaseWebViewFragment.java @@ -23,6 +23,7 @@ import android.annotation.SuppressLint; import android.os.Bundle; import android.webkit.WebSettings; import android.webkit.WebView; +import android.webkit.WebViewClient; import android.webkit.WebViewFragment; import org.mariotaku.twidere.Constants; @@ -36,10 +37,15 @@ public class BaseWebViewFragment extends WebViewFragment implements Constants { public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final WebView view = getWebView(); - view.setWebViewClient(new DefaultWebViewClient(getActivity())); + view.setWebViewClient(createWebViewClient()); final WebSettings settings = view.getSettings(); settings.setBuiltInZoomControls(true); settings.setJavaScriptEnabled(true); WebSettingsAccessor.setAllowUniversalAccessFromFileURLs(settings, true); } + + + protected WebViewClient createWebViewClient() { + return new DefaultWebViewClient(getActivity()); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsActivitiesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsActivitiesFragment.java index 82e834984..e55ce3047 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsActivitiesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsActivitiesFragment.java @@ -163,7 +163,9 @@ public abstract class AbsActivitiesFragment extends BaseSupportFragment im final LinearLayoutManager layoutManager = new LinearLayoutManager(context); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); mRecyclerView.setLayoutManager(layoutManager); - mRecyclerView.addItemDecoration(new DividerItemDecoration(context, layoutManager.getOrientation())); + if (compact) { + mRecyclerView.addItemDecoration(new DividerItemDecoration(context, layoutManager.getOrientation())); + } mRecyclerView.setAdapter(mAdapter); mRecyclerView.setOnScrollListener(mOnScrollListener); getLoaderManager().initLoader(0, getArguments(), this); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java index 6db0f03d7..818c9ba01 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AbsStatusesFragment.java @@ -57,16 +57,20 @@ public abstract class AbsStatusesFragment extends BaseSupportFragment impl private AbsStatusesAdapter mAdapter; private SimpleDrawerCallback mDrawerCallback; private OnScrollListener mOnScrollListener = new OnScrollListener() { + + private int mScrollState; + @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); + mScrollState = newState; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); if (isRefreshing()) return; - if (mAdapter.hasLoadMoreIndicator() + if (mAdapter.hasLoadMoreIndicator() && mScrollState != RecyclerView.SCROLL_STATE_IDLE && layoutManager.findLastVisibleItemPosition() == mAdapter.getItemCount() - 1) { onLoadMoreStatuses(); } @@ -174,9 +178,12 @@ public abstract class AbsStatusesFragment extends BaseSupportFragment impl public void onLoadFinished(Loader loader, Data data) { setRefreshing(false); mAdapter.setData(data); + mAdapter.setLoadMoreIndicatorEnabled(hasMoreData(data)); setListShown(true); } + protected abstract boolean hasMoreData(Data data); + @Override public void onLoaderReset(Loader loader) { } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java index e0b8e222c..51cdd7a4e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/AccountsDashboardFragment.java @@ -65,6 +65,7 @@ import org.mariotaku.twidere.activity.iface.IThemedActivity; import org.mariotaku.twidere.activity.support.AccountsManagerActivity; import org.mariotaku.twidere.activity.support.ComposeActivity; import org.mariotaku.twidere.activity.support.DraftsActivity; +import org.mariotaku.twidere.activity.support.GlobalSearchBoxActivity; import org.mariotaku.twidere.activity.support.HomeActivity; import org.mariotaku.twidere.activity.support.UserProfileEditorActivity; import org.mariotaku.twidere.adapter.ArrayAdapter; @@ -187,12 +188,15 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement final OptionItem option = (OptionItem) item; switch (option.id) { case MENU_SEARCH: { - final FragmentActivity a = getActivity(); - if (a instanceof HomeActivity) { - ((HomeActivity) a).openSearchView(account); - } else { - getActivity().onSearchRequested(); - } +// final FragmentActivity a = getActivity(); +// if (a instanceof HomeActivity) { +// ((HomeActivity) a).openSearchView(account); +// } else { +// getActivity().onSearchRequested(); +// } + final Intent intent = new Intent(getActivity(), GlobalSearchBoxActivity.class); + intent.putExtra(EXTRA_ACCOUNT_ID, account.account_id); + startActivity(intent); closeAccountsDrawer(); break; } @@ -294,7 +298,9 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement super.onActivityCreated(savedInstanceState); mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); mResolver = getContentResolver(); - final Context context = getView().getContext(); + final View view = getView(); + if (view == null) throw new AssertionError(); + final Context context = view.getContext(); mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper(); final LayoutInflater inflater = LayoutInflater.from(context); final ListView listView = getListView(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportWebViewFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportWebViewFragment.java index 51ab1b3e7..5bf3c7fbe 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportWebViewFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseSupportWebViewFragment.java @@ -23,6 +23,7 @@ import android.annotation.SuppressLint; import android.os.Bundle; import android.webkit.WebSettings; import android.webkit.WebView; +import android.webkit.WebViewClient; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.util.accessor.WebSettingsAccessor; @@ -35,11 +36,15 @@ public class BaseSupportWebViewFragment extends SupportWebViewFragment implement public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final WebView view = getWebView(); - view.setWebViewClient(new DefaultWebViewClient(getActivity())); + view.setWebViewClient(createWebViewClient()); final WebSettings settings = view.getSettings(); settings.setBuiltInZoomControls(true); settings.setJavaScriptEnabled(true); WebSettingsAccessor.setAllowUniversalAccessFromFileURLs(settings, true); } + + protected WebViewClient createWebViewClient() { + return new DefaultWebViewClient(getActivity()); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseUserListsListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseUserListsListFragment.java index 13f6217ea..99857f766 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseUserListsListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/BaseUserListsListFragment.java @@ -33,7 +33,7 @@ import android.view.View; import android.widget.AbsListView; import android.widget.ListView; -import org.mariotaku.twidere.adapter.ParcelableUserListsAdapter; +import org.mariotaku.twidere.adapter.ParcelableUserListsListAdapter; import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter.MenuButtonClickListener; import org.mariotaku.twidere.loader.support.BaseUserListsLoader; import org.mariotaku.twidere.model.ParcelableUserList; @@ -49,7 +49,7 @@ import static org.mariotaku.twidere.util.Utils.openUserListDetails; abstract class BaseUserListsListFragment extends BasePullToRefreshListFragment implements LoaderCallbacks>, OnMenuItemClickListener, MenuButtonClickListener { - private ParcelableUserListsAdapter mAdapter; + private ParcelableUserListsListAdapter mAdapter; private SharedPreferences mPreferences; private ListView mListView; @@ -77,7 +77,7 @@ abstract class BaseUserListsListFragment extends BasePullToRefreshListFragment i } @Override - public ParcelableUserListsAdapter getListAdapter() { + public ParcelableUserListsListAdapter getListAdapter() { return mAdapter; } @@ -116,7 +116,7 @@ abstract class BaseUserListsListFragment extends BasePullToRefreshListFragment i mUserId = args.getLong(EXTRA_USER_ID, -1); mScreenName = args.getString(EXTRA_SCREEN_NAME); } - mAdapter = new ParcelableUserListsAdapter(getActivity()); + mAdapter = new ParcelableUserListsListAdapter(getActivity()); mListView = getListView(); mListView.setDivider(null); mListView.setSelector(android.R.color.transparent); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CardBrowserFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CardBrowserFragment.java new file mode 100644 index 000000000..826ff61e0 --- /dev/null +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CardBrowserFragment.java @@ -0,0 +1,45 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.mariotaku.twidere.fragment.support; + +import android.os.Bundle; +import android.webkit.WebSettings; +import android.webkit.WebView; + +/** + * Created by mariotaku on 15/1/6. + */ +public class CardBrowserFragment extends SupportBrowserFragment { + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + final WebView view = getWebView(); + final WebSettings settings = view.getSettings(); + settings.setBuiltInZoomControls(false); + } + + public static CardBrowserFragment show(String uri) { + final Bundle args = new Bundle(); + args.putString(EXTRA_URI, uri); + final CardBrowserFragment fragment = new CardBrowserFragment(); + fragment.setArguments(args); + return fragment; + } +} diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java index d81e3d27c..dd57705f5 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/CursorStatusesFragment.java @@ -88,6 +88,11 @@ public abstract class CursorStatusesFragment extends AbsStatusesFragment return true; } + @Override + protected boolean hasMoreData(Cursor cursor) { + return true; + } + @Override protected long[] getAccountIds() { final Bundle args = getArguments(); diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java index 2ca4a3f36..147954aad 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/DirectMessagesConversationFragment.java @@ -33,6 +33,7 @@ import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +import android.support.v4.util.Pair; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; @@ -48,7 +49,6 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.ImageView; import android.widget.ImageView.ScaleType; @@ -125,6 +125,8 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl private EditText mUserQuery; private View mUsersSearchProgress; private View mQueryButton; + private View mUsersSearchEmpty; + private TextView mUsersSearchEmptyText; private PopupMenu mPopupMenu; @@ -160,6 +162,7 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl mUsersSearchList.setVisibility(View.VISIBLE); mUsersSearchProgress.setVisibility(View.GONE); mUsersSearchAdapter.setData(data, true); + updateEmptyText(); } @Override @@ -238,6 +241,7 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl mUsersSearchAdapter = new SimpleParcelableUsersAdapter(activity); mUsersSearchList.setAdapter(mUsersSearchAdapter); + mUsersSearchList.setEmptyView(mUsersSearchEmpty); mUsersSearchList.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { @@ -361,14 +365,6 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl sendDirectMessage(); break; } -// case R.id.recipient_selector: { -// if (mAccountId <= 0) return; -// final Intent intent = new Intent(INTENT_ACTION_SELECT_USER); -// intent.setClass(getActivity(), UserListSelectorActivity.class); -// intent.putExtra(EXTRA_ACCOUNT_ID, mAccountId); -// startActivityForResult(intent, REQUEST_SELECT_USER); -// break; -// } case R.id.add_image: { final Intent intent = new Intent(getActivity(), ImagePickerActivity.class); startActivityForResult(intent, REQUEST_PICK_IMAGE); @@ -377,8 +373,10 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl case R.id.item_profile_image: { final ParcelableUser recipient = mRecipient; if (recipient == null) return; + final Bundle options = Utils.makeSceneTransitionOption(getActivity(), + new Pair<>(view, UserFragment.TRANSITION_NAME_PROFILE_IMAGE)); Utils.openUserProfile(getActivity(), recipient.account_id, recipient.id, - recipient.screen_name, null); + recipient.screen_name, options); break; } case R.id.query_button: { @@ -554,6 +552,7 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl final Bus bus = TwidereApplication.getInstance(getActivity()).getMessageBus(); bus.register(this); updateTextCount(); + updateEmptyText(); } @Override @@ -582,11 +581,22 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl mSendButton.setEnabled(mValidator.isValidTweet(s.toString())); } + private void updateEmptyText() { + final boolean noQuery = mUserQuery.length() <= 0; + if (noQuery) { + mUsersSearchEmptyText.setText(R.string.type_name_to_search); + } else { + mUsersSearchEmptyText.setText(R.string.no_user_found); + } + } + @Override public void onViewCreated(final View view, final Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mUsersSearchProgress = view.findViewById(R.id.users_search_progress); mUsersSearchList = (ListView) view.findViewById(R.id.users_search_list); + mUsersSearchEmpty = view.findViewById(R.id.users_search_empty); + mUsersSearchEmptyText = (TextView) view.findViewById(R.id.users_search_empty_text); mMessagesListView = (ListView) view.findViewById(android.R.id.list); final View inputSendContainer = view.findViewById(R.id.input_send_container); mConversationContainer = view.findViewById(R.id.conversation_container); @@ -709,86 +719,6 @@ public class DirectMessagesConversationFragment extends BaseSupportFragment impl mTextCountView.setTextCount(count); } - private static class UsersSearchAdapter extends BaseAdapter { - - private final LayoutInflater mInflater; - private Object mUsers; - private int mScreenNameIdx; - private long mAccountId; - - public UsersSearchAdapter(Context context) { - mInflater = LayoutInflater.from(context); - } - - public void setUsers(List users) { - mUsers = users; - notifyDataSetChanged(); - } - - public void setUsers(Cursor users) { - mUsers = users; - notifyDataSetChanged(); - } - - @Override - public int getCount() { - if (mUsers instanceof Cursor) { - final Cursor c = (Cursor) mUsers; - mScreenNameIdx = c.getColumnIndex(CachedUsers.SCREEN_NAME); - return c.getCount(); - } else if (mUsers instanceof List) { - return ((List) mUsers).size(); - } - return 0; - } - - public void setAccountId(long accountId) { - mAccountId = accountId; - } - - @Override - public ParcelableUser getItem(int position) { - if (mUsers instanceof Cursor) { - final Cursor c = (Cursor) mUsers; - return new ParcelableUser(c, mAccountId); - } else if (mUsers instanceof List) { - return (ParcelableUser) ((List) mUsers).get(position); - } - throw new IllegalStateException(); - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view; - if (convertView != null) { - view = convertView; - } else { - view = mInflater.inflate(R.layout.list_item_user, parent, false); - } - if (mUsers instanceof Cursor) { - final Cursor c = (Cursor) mUsers; - c.moveToPosition(position); - bindUser(view, c); - } else if (mUsers instanceof List) { - bindUser(view, getItem(position)); - } - return view; - } - - private void bindUser(View view, ParcelableUser user) { - - } - - private void bindUser(View view, Cursor cursor) { - - } - } - private static class MyUserSearchLoader extends UserSearchLoader { private final boolean mFromCache; diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableActivitiesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableActivitiesFragment.java index 35459499b..027291d19 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableActivitiesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableActivitiesFragment.java @@ -67,7 +67,7 @@ public abstract class ParcelableActivitiesFragment extends AbsActivitiesFragment @Override protected ParcelableActivitiesAdapter onCreateAdapter(final Context context, final boolean compact) { - return new ParcelableActivitiesAdapter(context); + return new ParcelableActivitiesAdapter(context,compact); } @Override diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java index f0eb2a955..ff268ff86 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/ParcelableStatusesFragment.java @@ -45,6 +45,8 @@ import java.util.Set; public abstract class ParcelableStatusesFragment extends AbsStatusesFragment> { + private long mLastId; + public final void deleteStatus(final long statusId) { final List data = getAdapterData(); if (statusId <= 0 || data == null) return; @@ -88,6 +90,12 @@ public abstract class ParcelableStatusesFragment extends AbsStatusesFragment data) { + if (data == null || data.isEmpty()) return false; + return (mLastId != (mLastId = data.get(data.size() - 1).id)); + } + @Override protected long[] getAccountIds() { return new long[]{getAccountId()}; diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java index f092c8d51..d8feee437 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/RetweetQuoteDialogFragment.java @@ -24,6 +24,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.FragmentManager; @@ -50,17 +51,25 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem @Override public void onClick(final DialogInterface dialog, final int which) { + final ParcelableStatus status = getStatus(); + if (status == null) return; switch (which) { - case DialogInterface.BUTTON_POSITIVE: - final ParcelableStatus status = getStatus(); + case DialogInterface.BUTTON_POSITIVE: { final AsyncTwitterWrapper twitter = getTwitterWrapper(); - if (status == null || twitter == null) return; + if (twitter == null) return; if (isMyRetweet(status)) { twitter.cancelRetweetAsync(status.account_id, status.id, status.my_retweet_id); } else { twitter.retweetStatusAsync(status.account_id, status.id); } break; + } + case DialogInterface.BUTTON_NEUTRAL: { + final Intent intent = new Intent(INTENT_ACTION_QUOTE); + intent.putExtra(EXTRA_STATUS, status); + startActivity(intent); + break; + } default: break; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java index 5681a83df..77420040d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java @@ -66,7 +66,7 @@ import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration; import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.constant.IntentConstants; -import org.mariotaku.twidere.loader.ParcelableStatusLoader; +import org.mariotaku.twidere.loader.support.ParcelableStatusLoader; import org.mariotaku.twidere.loader.support.StatusRepliesLoader; import org.mariotaku.twidere.model.ListResponse; import org.mariotaku.twidere.model.ParcelableAccount; @@ -140,6 +140,8 @@ public class StatusFragment extends BaseSupportFragment private View mStatusContent; private View mProgressContainer; private View mErrorContainer; + private DividerItemDecoration mItemDecoration; + private LoaderCallbacks> mRepliesLoaderCallback = new LoaderCallbacks>() { @Override public Loader> onCreateLoader(int id, Bundle args) { @@ -207,8 +209,9 @@ public class StatusFragment extends BaseSupportFragment final Context context = view.getContext(); final boolean compact = Utils.isCompactCards(context); mLayoutManager = new StatusListLinearLayoutManager(context, mRecyclerView); + mItemDecoration = new DividerItemDecoration(context, mLayoutManager.getOrientation()); if (compact) { - mRecyclerView.addItemDecoration(new DividerItemDecoration(context, mLayoutManager.getOrientation())); + mRecyclerView.addItemDecoration(mItemDecoration); } mLayoutManager.setRecycleChildrenOnDetach(true); mRecyclerView.setLayoutManager(mLayoutManager); @@ -420,7 +423,7 @@ public class StatusFragment extends BaseSupportFragment mTextSize = preferences.getInt(KEY_TEXT_SIZE, res.getInteger(R.integer.default_text_size)); mIsCompact = compact; if (compact) { - mCardLayoutResource = R.layout.card_item_status_compat; + mCardLayoutResource = R.layout.card_item_status_compact; } else { mCardLayoutResource = R.layout.card_item_status; } @@ -531,8 +534,21 @@ public class StatusFragment extends BaseSupportFragment public void setDetailMediaExpanded(boolean expanded) { mDetailMediaExpanded = expanded; notifyDataSetChanged(); + updateItemDecoration(); } + private void updateItemDecoration() { + final DividerItemDecoration decoration = mFragment.getItemDecoration(); + decoration.setDecorationStart(0); + if (mReplies != null) { + decoration.setDecorationEnd(getItemCount() - 2); + } else { + decoration.setDecorationEnd(getItemCount() - 3); + } + mFragment.mRecyclerView.invalidateItemDecorations(); + } + + @Override public boolean isGapItem(int position) { return false; @@ -576,7 +592,7 @@ public class StatusFragment extends BaseSupportFragment if (mCachedHolder != null) return mCachedHolder; final View view; if (mIsCompact) { - view = mInflater.inflate(R.layout.header_status_common, parent, false); + view = mInflater.inflate(R.layout.header_status_compact, parent, false); } else { view = mInflater.inflate(R.layout.header_status, parent, false); } @@ -677,6 +693,7 @@ public class StatusFragment extends BaseSupportFragment public void setConversation(List conversation) { mConversation = conversation; notifyDataSetChanged(); + updateItemDecoration(); } public void setEventListener(StatusAdapterListener listener) { @@ -686,6 +703,7 @@ public class StatusFragment extends BaseSupportFragment public void setReplies(List replies) { mReplies = replies; notifyDataSetChanged(); + updateItemDecoration(); } public boolean setStatus(ParcelableStatus status) { @@ -697,6 +715,7 @@ public class StatusFragment extends BaseSupportFragment mStatusAccount = null; } notifyDataSetChanged(); + updateItemDecoration(); return !CompareUtils.objectEquals(old, status); } @@ -709,6 +728,10 @@ public class StatusFragment extends BaseSupportFragment } } + private DividerItemDecoration getItemDecoration() { + return mItemDecoration; + } + @Override public void onLoaderReset(final Loader> loader) { diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SupportBrowserFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SupportBrowserFragment.java index d5839f577..b7afb97b7 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SupportBrowserFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/SupportBrowserFragment.java @@ -35,12 +35,5 @@ public class SupportBrowserFragment extends BaseSupportWebViewFragment { view.loadUrl(ParseUtils.parseString(uri, "about:blank")); } - public static SupportBrowserFragment show(String uri) { - final Bundle args = new Bundle(); - args.putString(EXTRA_URI, uri); - final SupportBrowserFragment fragment = new SupportBrowserFragment(); - fragment.setArguments(args); - return fragment; - } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java index ba9b68c7f..179fc044a 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserFragment.java @@ -137,7 +137,6 @@ import static org.mariotaku.twidere.util.Utils.addIntentToMenu; import static org.mariotaku.twidere.util.Utils.formatToLongTimeString; import static org.mariotaku.twidere.util.Utils.getAccountColor; import static org.mariotaku.twidere.util.Utils.getAccountScreenName; -import static org.mariotaku.twidere.util.UserColorNameUtils.getDisplayName; import static org.mariotaku.twidere.util.Utils.getErrorMessage; import static org.mariotaku.twidere.util.Utils.getLocalizedNumber; import static org.mariotaku.twidere.util.Utils.getOriginalTwitterProfileImage; @@ -322,6 +321,8 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener mFollowButton.setVisibility(View.VISIBLE); } else if (relationship != null) { final int drawableRes; + mFollowButton.setEnabled(!relationship.isSourceBlockedByTarget()); + getView().findViewById(R.id.pages_error).setVisibility(relationship.isSourceBlockedByTarget() ? View.VISIBLE : View.GONE); if (relationship.isSourceBlockingTarget()) { mFollowButton.setText(R.string.unblock); drawableRes = R.drawable.ic_follow_blocked; @@ -369,6 +370,7 @@ public class UserFragment extends BaseSupportFragment implements OnClickListener } else { mFollowButton.setText(null); mFollowButton.setVisibility(View.GONE); + getView().findViewById(R.id.pages_error).setVisibility(View.GONE); // mFollowingYouIndicator.setVisibility(View.GONE); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java index 1cac12c3d..b57bd16f3 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListFragment.java @@ -82,7 +82,6 @@ import twitter4j.UserList; import static android.text.TextUtils.isEmpty; import static org.mariotaku.twidere.util.Utils.addIntentToMenu; import static org.mariotaku.twidere.util.Utils.getAccountColor; -import static org.mariotaku.twidere.util.UserColorNameUtils.getDisplayName; import static org.mariotaku.twidere.util.Utils.getTwitterInstance; import static org.mariotaku.twidere.util.Utils.openUserProfile; import static org.mariotaku.twidere.util.Utils.setMenuItemAvailability; @@ -98,7 +97,6 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList private TextView mListNameView, mCreatedByView, mDescriptionView, mErrorMessageView; private View mErrorRetryContainer, mProgressContainer; private ColorLabelRelativeLayout mProfileContainer; - private View mDescriptionContainer; private Button mRetryButton; private HeaderDrawerLayout mHeaderDrawerLayout; private ViewPager mViewPager; @@ -186,7 +184,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList public void displayUserList(final ParcelableUserList userList) { if (userList == null || getActivity() == null) return; getLoaderManager().destroyLoader(0); - final boolean is_myself = userList.account_id == userList.user_id; + final boolean isMyself = userList.account_id == userList.user_id; mErrorRetryContainer.setVisibility(View.GONE); mProgressContainer.setVisibility(View.GONE); mUserList = userList; @@ -196,7 +194,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList userList.user_screen_name, false); mCreatedByView.setText(getString(R.string.created_by, display_name)); final String description = userList.description; - mDescriptionContainer.setVisibility(is_myself || !isEmpty(description) ? View.VISIBLE : View.GONE); + mDescriptionView.setVisibility(isMyself || !isEmpty(description) ? View.VISIBLE : View.GONE); mDescriptionView.setText(description); final TwidereLinkify linkify = new TwidereLinkify( new OnLinkClickHandler(getActivity(), getMultiSelectManager())); @@ -276,6 +274,7 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList mViewPager.setAdapter(mPagerAdapter); mPagerIndicator.setViewPager(mViewPager); + mPagerIndicator.setTabDisplayOption(TabPagerIndicator.LABEL); mTwitterWrapper = getApplication().getTwitterWrapper(); mProfileImageLoader = getApplication().getImageLoaderWrapper(); @@ -480,7 +479,6 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList mCreatedByView = (TextView) headerView.findViewById(R.id.created_by); mDescriptionView = (TextView) headerView.findViewById(R.id.description); mProfileImageView = (ImageView) headerView.findViewById(R.id.profile_image); - mDescriptionContainer = headerView.findViewById(R.id.description_container); mRetryButton = (Button) mErrorRetryContainer.findViewById(R.id.retry); mErrorMessageView = (TextView) mErrorRetryContainer.findViewById(R.id.error_message); mViewPager = (ViewPager) contentView.findViewById(R.id.view_pager); @@ -519,8 +517,8 @@ public class UserListFragment extends BaseSupportFragment implements OnClickList tabArgs.putString(EXTRA_LIST_NAME, args.getString(EXTRA_LIST_NAME)); } mPagerAdapter.addTab(UserListTimelineFragment.class, tabArgs, getString(R.string.statuses), null, 0); - mPagerAdapter.addTab(UserListMembersFragment.class, tabArgs, getString(R.string.list_members), null, 1); - mPagerAdapter.addTab(UserListSubscribersFragment.class, tabArgs, getString(R.string.list_subscribers), null, 2); + mPagerAdapter.addTab(UserListMembersFragment.class, tabArgs, getString(R.string.members), null, 1); + mPagerAdapter.addTab(UserListSubscribersFragment.class, tabArgs, getString(R.string.subscribers), null, 2); mPagerIndicator.notifyDataSetChanged(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembershipsListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembershipsListFragment.java index 9d3403ca8..fc50ee6ee 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembershipsListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListMembershipsListFragment.java @@ -28,10 +28,10 @@ import java.util.List; public class UserListMembershipsListFragment extends BaseUserListsListFragment { - @Override - public Loader> newLoaderInstance(final long account_id, final long user_id, - final String screen_name) { - return new UserListMembershipsLoader(getActivity(), account_id, user_id, screen_name, getCursor(), getData()); - } + @Override + public Loader> newLoaderInstance(final long accountId, final long userId, + final String screenName) { + return new UserListMembershipsLoader(getActivity(), accountId, userId, screenName, getCursor(), getData()); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsFragment.java index 7f02d8987..234f26059 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsFragment.java @@ -65,6 +65,7 @@ public class UserListsFragment extends BaseSupportFragment implements RefreshScr mViewPager.setAdapter(mAdapter); mViewPager.setOffscreenPageLimit(2); mPagerIndicator.setViewPager(mViewPager); + mPagerIndicator.setTabDisplayOption(TabPagerIndicator.LABEL); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsListFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsListFragment.java index dcdddeaed..2a6d3c850 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsListFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/UserListsListFragment.java @@ -31,7 +31,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import org.mariotaku.twidere.R; -import org.mariotaku.twidere.adapter.ParcelableUserListsAdapter; +import org.mariotaku.twidere.adapter.ParcelableUserListsListAdapter; import org.mariotaku.twidere.loader.support.UserListsLoader; import org.mariotaku.twidere.model.ParcelableUserList; @@ -59,7 +59,7 @@ public class UserListsListFragment extends BaseUserListsListFragment { @Override public Loader> newLoaderInstance(final long accountId, final long userId, final String screenName) { - return new UserListsLoader(getActivity(), accountId, userId, screenName, getData()); + return new UserListsLoader(getActivity(), accountId, userId, screenName, true, getData()); } @Override @@ -113,7 +113,7 @@ public class UserListsListFragment extends BaseUserListsListFragment { } private void removeUserList(final long id) { - final ParcelableUserListsAdapter adapter = getListAdapter(); + final ParcelableUserListsListAdapter adapter = getListAdapter(); final int listsIdx = adapter.findItemPosition(id); if (listsIdx >= 0) { adapter.removeAt(listsIdx); diff --git a/twidere/src/main/java/org/mariotaku/twidere/loader/ParcelableStatusLoader.java b/twidere/src/main/java/org/mariotaku/twidere/loader/support/ParcelableStatusLoader.java similarity index 98% rename from twidere/src/main/java/org/mariotaku/twidere/loader/ParcelableStatusLoader.java rename to twidere/src/main/java/org/mariotaku/twidere/loader/support/ParcelableStatusLoader.java index f9ab61372..976b22a70 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/loader/ParcelableStatusLoader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/loader/support/ParcelableStatusLoader.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package org.mariotaku.twidere.loader; +package org.mariotaku.twidere.loader.support; import android.content.Context; import android.os.Bundle; diff --git a/twidere/src/main/java/org/mariotaku/gallery3d/GLImageLoader.java b/twidere/src/main/java/org/mariotaku/twidere/loader/support/TileImageLoader.java similarity index 89% rename from twidere/src/main/java/org/mariotaku/gallery3d/GLImageLoader.java rename to twidere/src/main/java/org/mariotaku/twidere/loader/support/TileImageLoader.java index 99f330c33..878ed4b8b 100644 --- a/twidere/src/main/java/org/mariotaku/gallery3d/GLImageLoader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/loader/support/TileImageLoader.java @@ -1,7 +1,7 @@ /* - * Twidere - Twitter client for Android + * Twidere - Twitter client for Android * - * Copyright (C) 2012-2013 Mariotaku Lee + * Copyright (C) 2012-2015 Mariotaku Lee * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package org.mariotaku.gallery3d; +package org.mariotaku.twidere.loader.support; import android.content.ContentResolver; import android.content.Context; @@ -33,11 +33,10 @@ import android.util.DisplayMetrics; import com.nostra13.universalimageloader.cache.disc.DiskCache; import com.nostra13.universalimageloader.core.download.ImageDownloader; +import com.nostra13.universalimageloader.utils.IoUtils; -import org.mariotaku.gallery3d.util.BitmapUtils; -import org.mariotaku.gallery3d.util.GalleryUtils; -import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.app.TwidereApplication; +import org.mariotaku.twidere.util.BitmapUtils; import org.mariotaku.twidere.util.Exif; import org.mariotaku.twidere.util.ImageValidator; import org.mariotaku.twidere.util.ParseUtils; @@ -49,7 +48,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -public class GLImageLoader extends AsyncTaskLoader implements Constants { +public class TileImageLoader extends AsyncTaskLoader { private final Uri mUri; private final Handler mHandler; @@ -60,7 +59,7 @@ public class GLImageLoader extends AsyncTaskLoader impleme private final float mFallbackSize; private final long mAccountId; - public GLImageLoader(final Context context, final DownloadListener listener, final long accountId, final Uri uri) { + public TileImageLoader(final Context context, final DownloadListener listener, final long accountId, final Uri uri) { super(context); mHandler = new Handler(); mAccountId = accountId; @@ -75,7 +74,7 @@ public class GLImageLoader extends AsyncTaskLoader impleme } @Override - public GLImageLoader.Result loadInBackground() { + public TileImageLoader.Result loadInBackground() { if (mUri == null) { return Result.nullInstance(); } @@ -105,8 +104,8 @@ public class GLImageLoader extends AsyncTaskLoader impleme dump(is, os); mHandler.post(new DownloadFinishRunnable(this, mListener)); } finally { - GalleryUtils.closeSilently(is); - GalleryUtils.closeSilently(os); + IoUtils.closeSilently(is); + IoUtils.closeSilently(os); } if (!ImageValidator.checkImageValidity(cacheFile)) { // The file is corrupted, so we remove it from @@ -137,7 +136,6 @@ public class GLImageLoader extends AsyncTaskLoader impleme final String path = file.getAbsolutePath(); final BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; - o.inPreferredConfig = Bitmap.Config.RGB_565; BitmapFactory.decodeFile(path, o); final int width = o.outWidth, height = o.outHeight; if (width <= 0 || height <= 0) return Result.getInstance(file, null); @@ -155,7 +153,6 @@ public class GLImageLoader extends AsyncTaskLoader impleme final int height = decoder.getHeight(); final BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = BitmapUtils.computeSampleSize(mFallbackSize / Math.max(width, height)); - options.inPreferredConfig = Bitmap.Config.RGB_565; final Bitmap bitmap = decoder.decodeRegion(new Rect(0, 0, width, height), options); return Result.getInstance(decoder, bitmap, Exif.getOrientation(file), file); } catch (final IOException e) { @@ -217,6 +214,10 @@ public class GLImageLoader extends AsyncTaskLoader impleme this.exception = exception; } + public boolean hasData() { + return bitmap != null || decoder != null; + } + public static Result getInstance(final Bitmap bitmap, final int orientation, final File file) { return new Result(null, bitmap, orientation, file, null); } @@ -237,11 +238,11 @@ public class GLImageLoader extends AsyncTaskLoader impleme private final static class DownloadErrorRunnable implements Runnable { - private final GLImageLoader loader; + private final TileImageLoader loader; private final DownloadListener listener; private final Throwable t; - DownloadErrorRunnable(final GLImageLoader loader, final DownloadListener listener, final Throwable t) { + DownloadErrorRunnable(final TileImageLoader loader, final DownloadListener listener, final Throwable t) { this.loader = loader; this.listener = listener; this.t = t; @@ -256,10 +257,10 @@ public class GLImageLoader extends AsyncTaskLoader impleme private final static class DownloadFinishRunnable implements Runnable { - private final GLImageLoader loader; + private final TileImageLoader loader; private final DownloadListener listener; - DownloadFinishRunnable(final GLImageLoader loader, final DownloadListener listener) { + DownloadFinishRunnable(final TileImageLoader loader, final DownloadListener listener) { this.loader = loader; this.listener = listener; } @@ -273,11 +274,11 @@ public class GLImageLoader extends AsyncTaskLoader impleme private final static class DownloadStartRunnable implements Runnable { - private final GLImageLoader loader; + private final TileImageLoader loader; private final DownloadListener listener; private final long total; - DownloadStartRunnable(final GLImageLoader loader, final DownloadListener listener, final long total) { + DownloadStartRunnable(final TileImageLoader loader, final DownloadListener listener, final long total) { this.loader = loader; this.listener = listener; this.total = total; diff --git a/twidere/src/main/java/org/mariotaku/twidere/loader/support/UserListsLoader.java b/twidere/src/main/java/org/mariotaku/twidere/loader/support/UserListsLoader.java index 49d4a07c4..c8c36d8cf 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/loader/support/UserListsLoader.java +++ b/twidere/src/main/java/org/mariotaku/twidere/loader/support/UserListsLoader.java @@ -23,38 +23,38 @@ import android.content.Context; import org.mariotaku.twidere.model.ParcelableUserList; +import java.util.List; + import twitter4j.ResponseList; import twitter4j.Twitter; import twitter4j.TwitterException; import twitter4j.UserList; -import java.util.List; - public class UserListsLoader extends BaseUserListsLoader { - public static final String LOGTAG = UserListsLoader.class.getSimpleName(); + private final long mUserId; + private final String mScreenName; + private final boolean mReverse; - private final long mUserId; - private final String mScreenName; + public UserListsLoader(final Context context, final long accountId, final long userId, + final String screenName, final boolean reverse, final List data) { + super(context, accountId, 0, data); + mUserId = userId; + mScreenName = screenName; + mReverse = reverse; + } - public UserListsLoader(final Context context, final long accountId, final long userId, final String screenName, - final List data) { - super(context, accountId, 0, data); - mUserId = userId; - mScreenName = screenName; - } + @Override + public ResponseList getUserLists(final Twitter twitter) throws TwitterException { + if (twitter == null) return null; + if (mUserId > 0) + return twitter.getUserLists(mUserId, mReverse); + else if (mScreenName != null) return twitter.getUserLists(mScreenName, mReverse); + return null; + } - @Override - public ResponseList getUserLists(final Twitter twitter) throws TwitterException { - if (twitter == null) return null; - if (mUserId > 0) - return twitter.getUserLists(mUserId); - else if (mScreenName != null) return twitter.getUserLists(mScreenName); - return null; - } - - @Override - protected boolean isFollowing(final UserList list) { - return true; - } + @Override + protected boolean isFollowing(final UserList list) { + return true; + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/preference/CardPreviewPreference.java b/twidere/src/main/java/org/mariotaku/twidere/preference/CardPreviewPreference.java index 01cebb533..94999dffc 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/preference/CardPreviewPreference.java +++ b/twidere/src/main/java/org/mariotaku/twidere/preference/CardPreviewPreference.java @@ -90,7 +90,7 @@ public class CardPreviewPreference extends Preference implements Constants, OnSh @Override protected View onCreateView(final ViewGroup parent) { if (mPreferences != null && mPreferences.getBoolean(KEY_COMPACT_CARDS, false)) - return mInflater.inflate(R.layout.card_item_status_compat, parent, false); + return mInflater.inflate(R.layout.card_item_status_compact, parent, false); return mInflater.inflate(R.layout.card_item_status, parent, false); } diff --git a/twidere/src/main/java/org/mariotaku/gallery3d/util/BitmapUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/BitmapUtils.java similarity index 90% rename from twidere/src/main/java/org/mariotaku/gallery3d/util/BitmapUtils.java rename to twidere/src/main/java/org/mariotaku/twidere/util/BitmapUtils.java index 1307a6251..f2f52a364 100644 --- a/twidere/src/main/java/org/mariotaku/gallery3d/util/BitmapUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/BitmapUtils.java @@ -14,19 +14,21 @@ * limitations under the License. */ -package org.mariotaku.gallery3d.util; +package org.mariotaku.twidere.util; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; +import org.mariotaku.twidere.util.MathUtils; + public class BitmapUtils { // Find the max x that 1 / x <= scale. public static int computeSampleSize(final float scale) { if (scale <= 0) return 1; final int initialSize = Math.max(1, (int) Math.ceil(1 / scale)); - return initialSize <= 8 ? GalleryUtils.nextPowerOf2(initialSize) : (initialSize + 7) / 8 * 8; + return initialSize <= 8 ? MathUtils.nextPowerOf2(initialSize) : (initialSize + 7) / 8 * 8; } // This computes a sample size which makes the longer side at least @@ -35,7 +37,7 @@ public class BitmapUtils { final int initialSize = Math.max(w / minSideLength, h / minSideLength); if (initialSize <= 1) return 1; - return initialSize <= 8 ? GalleryUtils.prevPowerOf2(initialSize) : initialSize / 8 * 8; + return initialSize <= 8 ? MathUtils.prevPowerOf2(initialSize) : initialSize / 8 * 8; } // Resize the bitmap if each side is >= targetSize * 2 diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/MathUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/MathUtils.java index ed7a8b19e..84507935c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/MathUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/MathUtils.java @@ -30,4 +30,26 @@ public class MathUtils { return Math.max(Math.min(num, max), min); } + // Returns the next power of two. + // Returns the input if it is already power of 2. + // Throws IllegalArgumentException if the input is <= 0 or + // the answer overflows. + public static int nextPowerOf2(int n) { + if (n <= 0 || n > 1 << 30) throw new IllegalArgumentException("n is invalid: " + n); + n -= 1; + n |= n >> 16; + n |= n >> 8; + n |= n >> 4; + n |= n >> 2; + n |= n >> 1; + return n + 1; + } + + // Returns the previous power of two. + // Returns the input if it is already power of 2. + // Throws IllegalArgumentException if the input is <= 0 + public static int prevPowerOf2(final int n) { + if (n <= 0) throw new IllegalArgumentException(); + return Integer.highestOneBit(n); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java index 7a01763b2..5e9771b09 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/ThemeUtils.java @@ -215,6 +215,15 @@ public class ThemeUtils implements Constants { return view; } + public static int getGlobalSearchThemeResource(final Context context) { + return getGlobalSearchThemeResource(getThemeNameOption(context)); + } + + public static int getGlobalSearchThemeResource(final String name) { + if (VALUE_THEME_NAME_DARK.equals(name)) return R.style.Theme_Twidere_Dark_GlobalSearch; + return R.style.Theme_Twidere_Light_GlobalSearch; + } + private static void applyColorTintForView(View view, int tintColor) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; if (view instanceof IThemedView) { @@ -227,10 +236,10 @@ public class ThemeUtils implements Constants { ViewAccessor.setProgressBackgroundTintList(progressBar, tintList); ViewAccessor.setIndeterminateTintList(progressBar, tintList); } else if (view instanceof Switch) { - final ColorStateList tintList = ColorStateList.valueOf(tintColor); - final Switch switchView = (Switch) view; - DrawableCompat.setTintList(switchView.getThumbDrawable(), tintList); - DrawableCompat.setTintList(switchView.getTrackDrawable(), tintList); +// final ColorStateList tintList = ColorStateList.valueOf(tintColor); +// final Switch switchView = (Switch) view; +// DrawableCompat.setTintList(switchView.getThumbDrawable(), tintList); +// DrawableCompat.setTintList(switchView.getTrackDrawable(), tintList); } else if (view instanceof CompoundButton) { final ColorStateList tintList = ColorStateList.valueOf(tintColor); final CompoundButton compoundButton = (CompoundButton) view; diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterCardFragmentFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterCardFragmentFactory.java index 23934a244..3329f3152 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterCardFragmentFactory.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterCardFragmentFactory.java @@ -21,7 +21,7 @@ package org.mariotaku.twidere.util; import android.support.v4.app.Fragment; -import org.mariotaku.twidere.fragment.support.SupportBrowserFragment; +import org.mariotaku.twidere.fragment.support.CardBrowserFragment; import org.mariotaku.twidere.model.ParcelableStatus.ParcelableCardEntity; import org.mariotaku.twidere.model.ParcelableStatus.ParcelableCardEntity.ParcelableValueItem; @@ -42,6 +42,6 @@ public abstract class TwitterCardFragmentFactory { public static Fragment createGenericPlayerFragment(ParcelableCardEntity card) { final ParcelableValueItem player_url = ParcelableCardEntity.getValue(card, "player_url"); - return SupportBrowserFragment.show((String) player_url.value); + return CardBrowserFragment.show((String) player_url.value); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java index 7a7da6ede..1285f6142 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/Utils.java @@ -103,7 +103,6 @@ import android.widget.Toast; import org.apache.http.NameValuePair; import org.json.JSONException; -import org.mariotaku.gallery3d.ImageViewerGLActivity; import org.mariotaku.jsonserializer.JSONSerializer; import org.mariotaku.menucomponent.internal.menu.MenuUtils; import org.mariotaku.querybuilder.AllColumns; @@ -122,6 +121,7 @@ import org.mariotaku.twidere.BuildConfig; import org.mariotaku.twidere.Constants; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.CameraCropActivity; +import org.mariotaku.twidere.activity.support.MediaViewerActivity; import org.mariotaku.twidere.adapter.iface.IBaseAdapter; import org.mariotaku.twidere.adapter.iface.IBaseCardAdapter; import org.mariotaku.twidere.app.TwidereApplication; @@ -190,6 +190,7 @@ import org.mariotaku.twidere.util.menu.TwidereMenuInfo; import org.mariotaku.twidere.util.net.TwidereHostResolverFactory; import org.mariotaku.twidere.util.net.TwidereHttpClientFactory; import org.mariotaku.twidere.view.ShapedImageView; +import org.mariotaku.twidere.view.ShapedImageView.ShapeStyle; import java.io.Closeable; import java.io.File; @@ -2036,6 +2037,14 @@ public final class Utils implements Constants, TwitterConstants { return url; } + @ShapeStyle + public static int getProfileImageStyle(Context context) { + final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); + final String style = prefs.getString(KEY_PROFILE_IMAGE_STYLE, null); + return getProfileImageStyle(style); + } + + @ShapeStyle public static int getProfileImageStyle(String style) { if (VALUE_PROFILE_IMAGE_STYLE_SQUARE.equalsIgnoreCase(style)) { return ShapedImageView.SHAPE_RECTANGLE; @@ -2457,7 +2466,8 @@ public final class Utils implements Constants, TwitterConstants { cb.setGZIPEnabled(enableGzip); cb.setIgnoreSSLError(ignoreSslError); cb.setIncludeCards(true); - cb.setCardsPlatform("Android-5"); + cb.setCardsPlatform("Android-12"); +// cb.setModelVersion(7); if (enableProxy) { final String proxy_host = prefs.getString(KEY_PROXY_HOST, null); final int proxy_port = ParseUtils.parseInt(prefs.getString(KEY_PROXY_PORT, "-1")); @@ -2912,7 +2922,7 @@ public final class Utils implements Constants, TwitterConstants { final Intent intent = new Intent(INTENT_ACTION_VIEW_IMAGE); intent.setData(Uri.parse(uri)); intent.putExtra(EXTRA_ACCOUNT_ID, accountId); - intent.setClass(context, ImageViewerGLActivity.class); + intent.setClass(context, MediaViewerActivity.class); context.startActivity(intent); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java b/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java index 5d26d3f9d..d4753200c 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/ShapedImageView.java @@ -40,6 +40,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.os.Build; +import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; @@ -49,13 +50,24 @@ import android.widget.ImageView; import org.mariotaku.twidere.R; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * An ImageView class with a circle mask so that all images are drawn in a * circle instead of a square. */ public class ShapedImageView extends ImageView { + + @IntDef({SHAPE_CIRCLE, SHAPE_RECTANGLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface ShapeStyle { + } + + @ShapeStyle public static final int SHAPE_CIRCLE = 0x1; + @ShapeStyle public static final int SHAPE_RECTANGLE = 0x2; private static final int SHADOW_START_COLOR = 0x37000000; @@ -120,7 +132,9 @@ public class ShapedImageView extends ImageView { } setBorderColor(a.getColor(R.styleable.ShapedImageView_sivBorderColor, Color.TRANSPARENT)); setBorderWidth(a.getDimensionPixelSize(R.styleable.ShapedImageView_sivBorderWidth, 0)); - setStyle(a.getInt(R.styleable.ShapedImageView_sivShape, SHAPE_RECTANGLE)); + @ShapeStyle + final int shapeStyle = a.getInt(R.styleable.ShapedImageView_sivShape, SHAPE_RECTANGLE); + setStyle(shapeStyle); setCornerRadius(a.getDimension(R.styleable.ShapedImageView_sivCornerRadius, 0)); setCornerRadiusRatio(a.getFraction(R.styleable.ShapedImageView_sivCornerRadiusRatio, 1, 1, -1)); @@ -203,11 +217,12 @@ public class ShapedImageView extends ImageView { } } + @ShapeStyle public int getStyle() { return mStyle; } - public void setStyle(int style) { + public void setStyle(@ShapeStyle final int style) { mStyle = style; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java b/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java index 542dad7db..1293a0ea1 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/TabPagerIndicator.java @@ -75,6 +75,8 @@ public class TabPagerIndicator extends RecyclerView implements PagerIndicator { final int dividerHorizontalPadding = a.getDimensionPixelSize(R.styleable.TabPagerIndicator_tabDividerHorizontalPadding, 0); mItemDecoration.setPadding(dividerHorizontalPadding, dividerVerticalPadding, dividerHorizontalPadding, dividerVerticalPadding); + mItemDecoration.setDecorationStart(0); + mItemDecoration.setDecorationEndOffset(1); a.recycle(); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityListViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityListViewHolder.java deleted file mode 100644 index 92a7a09cb..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ActivityListViewHolder.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Twidere - Twitter client for Android - * - * Copyright (C) 2012-2014 Mariotaku Lee - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.mariotaku.twidere.view.holder; - -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import org.mariotaku.twidere.R; - -public class ActivityListViewHolder extends StatusListViewHolder { - - public final ImageView activity_profile_image_1, activity_profile_image_2, activity_profile_image_3, - activity_profile_image_4, activity_profile_image_5; - public final ImageView[] activity_profile_images; - public final ViewGroup activity_profile_images_container; - public final TextView activity_profile_image_more_number; - public final View divider; - - public ActivityListViewHolder(final View view) { - super(view); - divider = findViewById(R.id.divider); - activity_profile_images_container = (ViewGroup) findViewById(R.id.activity_profile_image_container); - activity_profile_image_1 = (ImageView) findViewById(R.id.activity_profile_image_1); - activity_profile_image_2 = (ImageView) findViewById(R.id.activity_profile_image_2); - activity_profile_image_3 = (ImageView) findViewById(R.id.activity_profile_image_3); - activity_profile_image_4 = (ImageView) findViewById(R.id.activity_profile_image_4); - activity_profile_image_5 = (ImageView) findViewById(R.id.activity_profile_image_5); - activity_profile_image_more_number = (TextView) findViewById(R.id.activity_profile_image_more_number); - activity_profile_images = new ImageView[] { activity_profile_image_1, activity_profile_image_2, - activity_profile_image_3, activity_profile_image_4, activity_profile_image_5 }; - } - - @Override - public boolean setTextSize(final float text_size) { - if (super.setTextSize(text_size)) return false; - activity_profile_image_more_number.setTextSize(text_size); - return true; - } - -} diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/CardViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/CardViewHolder.java index d33138c13..0074148db 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/CardViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/CardViewHolder.java @@ -24,7 +24,7 @@ import android.view.View; import org.mariotaku.twidere.R; import org.mariotaku.twidere.view.iface.IColorLabelView; -public class CardViewHolder extends ListViewHolder { +public class CardViewHolder extends ViewListHolder { public final IColorLabelView content; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageConversationViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageConversationViewHolder.java index 3d09263c0..82bc38c46 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageConversationViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageConversationViewHolder.java @@ -31,7 +31,7 @@ import android.widget.TextView; import org.mariotaku.messagebubbleview.library.MessageBubbleView; import org.mariotaku.twidere.R; -public class DirectMessageConversationViewHolder extends ListViewHolder { +public class DirectMessageConversationViewHolder extends ViewListHolder { public final TextView text, time; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java index 761422548..71a37d3ae 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DirectMessageEntryViewHolder.java @@ -28,7 +28,7 @@ import org.mariotaku.twidere.R; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.view.ShortTimeView; -public class DirectMessageEntryViewHolder extends ListViewHolder { +public class DirectMessageEntryViewHolder extends ViewListHolder { public final ImageView profile_image; public final TextView name, screen_name, text; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DraftViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DraftViewHolder.java index fce938a8a..f007ce229 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/DraftViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/DraftViewHolder.java @@ -26,7 +26,7 @@ import android.widget.TextView; import org.mariotaku.twidere.R; import org.mariotaku.twidere.view.iface.IColorLabelView; -public class DraftViewHolder extends ListViewHolder { +public class DraftViewHolder extends ViewListHolder { public final IColorLabelView content; public final TextView text; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java index 20f6a55e7..bdde9a94d 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewHolder.java @@ -12,6 +12,7 @@ import android.widget.ImageView; import android.widget.TextView; import org.mariotaku.twidere.R; +import org.mariotaku.twidere.adapter.iface.ContentCardClickListener; import org.mariotaku.twidere.adapter.iface.IStatusesAdapter; import org.mariotaku.twidere.model.ParcelableMedia; import org.mariotaku.twidere.model.ParcelableStatus; @@ -48,6 +49,8 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick private final CardMediaContainer mediaPreviewContainer; private final TextView replyCountView, retweetCountView, favoriteCountView; + private StatusClickListener statusClickListener; + public StatusViewHolder(View itemView) { this(null, itemView); @@ -75,19 +78,27 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick } public void setOnClickListeners() { - setOnClickListeners(this); + setStatusClickListener(adapter); } - public void setOnClickListeners(OnClickListener listener) { - itemView.findViewById(R.id.item_content).setOnClickListener(listener); - itemView.findViewById(R.id.item_menu).setOnClickListener(listener); + public static interface StatusClickListener extends ContentCardClickListener { - itemView.setOnClickListener(listener); - profileImageView.setOnClickListener(listener); - mediaPreviewContainer.setOnClickListener(listener); - replyCountView.setOnClickListener(listener); - retweetCountView.setOnClickListener(listener); - favoriteCountView.setOnClickListener(listener); + void onUserProfileClick(StatusViewHolder holder, int position); + + void onStatusClick(StatusViewHolder holder, int position); + } + + public void setStatusClickListener(StatusClickListener listener) { + statusClickListener = listener; + itemView.findViewById(R.id.item_content).setOnClickListener(this); + itemView.findViewById(R.id.item_menu).setOnClickListener(this); + + itemView.setOnClickListener(this); + profileImageView.setOnClickListener(this); + mediaPreviewContainer.setOnClickListener(this); + replyCountView.setOnClickListener(this); + retweetCountView.setOnClickListener(this); + favoriteCountView.setOnClickListener(this); } public void setupViewOptions() { @@ -330,24 +341,25 @@ public class StatusViewHolder extends RecyclerView.ViewHolder implements OnClick @Override public void onClick(View v) { + if (statusClickListener == null) return; final int position = getPosition(); switch (v.getId()) { case R.id.item_content: { - adapter.onStatusClick(this, position); + statusClickListener.onStatusClick(this, position); break; } case R.id.item_menu: { - adapter.onItemMenuClick(this, position); + statusClickListener.onItemMenuClick(this, position); break; } case R.id.profile_image: { - adapter.onUserProfileClick(this, position); + statusClickListener.onUserProfileClick(this, position); break; } case R.id.reply_count: case R.id.retweet_count: case R.id.favorite_count: { - adapter.onItemActionClick(this, v.getId(), position); + statusClickListener.onItemActionClick(this, v.getId(), position); break; } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusListViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewListHolder.java similarity index 98% rename from twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusListViewHolder.java rename to twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewListHolder.java index d00ded6e1..e278b4c17 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusListViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/StatusViewListHolder.java @@ -37,7 +37,7 @@ import static org.mariotaku.twidere.util.UserColorNameUtils.getDisplayName; import static org.mariotaku.twidere.util.Utils.getStatusTypeIconRes; import static org.mariotaku.twidere.util.Utils.getUserTypeIconRes; -public class StatusListViewHolder extends CardViewHolder { +public class StatusViewListHolder extends CardViewHolder { public final ImageView my_profile_image, profile_image; public final ImageView image_preview; @@ -57,7 +57,7 @@ public class StatusListViewHolder extends CardViewHolder { private boolean display_profile_image; private int card_highlight_option; - public StatusListViewHolder(final View view) { + public StatusViewListHolder(final View view) { super(view); final Context context = getContext(); profile_image = (ImageView) findViewById(R.id.profile_image); diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/TwoLineWithIconViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/TwoLineWithIconViewHolder.java index c35f63511..df05264ee 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/TwoLineWithIconViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/TwoLineWithIconViewHolder.java @@ -23,7 +23,7 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; -public class TwoLineWithIconViewHolder extends ListViewHolder { +public class TwoLineWithIconViewHolder extends ViewListHolder { public final ImageView icon; public final TextView text1, text2; diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListListViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListViewListHolder.java similarity index 51% rename from twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListListViewHolder.java rename to twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListViewListHolder.java index 9a64b7163..d048128b6 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListListViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListViewListHolder.java @@ -26,31 +26,33 @@ import android.widget.TextView; import org.mariotaku.twidere.R; import org.mariotaku.twidere.view.iface.IColorLabelView; -public class UserListListViewHolder extends ListViewHolder { +public class UserListViewListHolder extends ViewListHolder { public final IColorLabelView content; - public final ImageView profile_image; - public final TextView name, description, created_by, members_count, subscribers_count; - private float text_size; - public int position; + public final ImageView profile_image; + public final TextView name, description, created_by, members_count, subscribers_count; + private float text_size; + public int position; - public UserListListViewHolder(final View view) { - super(view); + public UserListViewListHolder(final View view) { + super(view); content = (IColorLabelView) view.findViewById(R.id.content); - profile_image = (ImageView) findViewById(R.id.profile_image); - name = (TextView) findViewById(R.id.name); - description = (TextView) findViewById(R.id.description); - created_by = (TextView) findViewById(R.id.created_by); - members_count = (TextView) findViewById(R.id.members_count); - subscribers_count = (TextView) findViewById(R.id.subscribers_count); - } + profile_image = (ImageView) findViewById(R.id.profile_image); + name = (TextView) findViewById(R.id.name); + description = (TextView) findViewById(R.id.description); + created_by = (TextView) findViewById(R.id.created_by); + members_count = (TextView) findViewById(R.id.members_count); + subscribers_count = (TextView) findViewById(R.id.subscribers_count); + } - public void setTextSize(final float text_size) { - if (this.text_size == text_size) return; - this.text_size = text_size; - description.setTextSize(text_size); - name.setTextSize(text_size * 1.05f); - created_by.setTextSize(text_size * 0.65f); - } + public void setTextSize(final float text_size) { + if (this.text_size == text_size) return; + this.text_size = text_size; + if (description != null) { + description.setTextSize(text_size); + } + name.setTextSize(text_size * 1.05f); + created_by.setTextSize(text_size * 0.65f); + } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserViewListHolder.java similarity index 96% rename from twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListViewHolder.java rename to twidere/src/main/java/org/mariotaku/twidere/view/holder/UserViewListHolder.java index d0c9eed94..26eafe34b 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserListViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/UserViewListHolder.java @@ -27,7 +27,7 @@ import android.widget.TextView; import org.mariotaku.twidere.R; import org.mariotaku.twidere.view.iface.IColorLabelView; -public class UserListViewHolder extends ListViewHolder { +public class UserViewListHolder extends ViewListHolder { public final IColorLabelView content; public final ImageView profile_image, profile_type; @@ -37,7 +37,7 @@ public class UserListViewHolder extends ListViewHolder { private float text_size; public int position; - public UserListViewHolder(final View view) { + public UserViewListHolder(final View view) { super(view); content = (IColorLabelView) view.findViewById(R.id.content); profile_image = (ImageView) findViewById(R.id.profile_image); diff --git a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ListViewHolder.java b/twidere/src/main/java/org/mariotaku/twidere/view/holder/ViewListHolder.java similarity index 93% rename from twidere/src/main/java/org/mariotaku/twidere/view/holder/ListViewHolder.java rename to twidere/src/main/java/org/mariotaku/twidere/view/holder/ViewListHolder.java index abe6fec60..bfbc2cff4 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/view/holder/ListViewHolder.java +++ b/twidere/src/main/java/org/mariotaku/twidere/view/holder/ViewListHolder.java @@ -24,11 +24,11 @@ import android.view.View; import org.mariotaku.twidere.Constants; -public class ListViewHolder implements Constants { +public class ViewListHolder implements Constants { public View view; - public ListViewHolder(final View view) { + public ViewListHolder(final View view) { if (view == null) throw new NullPointerException(); this.view = view; } diff --git a/twidere/src/main/java/twitter4j/Relationship.java b/twidere/src/main/java/twitter4j/Relationship.java index 0d67187d9..22e50da44 100644 --- a/twidere/src/main/java/twitter4j/Relationship.java +++ b/twidere/src/main/java/twitter4j/Relationship.java @@ -19,92 +19,94 @@ package twitter4j; /** * A data interface that has detailed information about a relationship between * two users - * + * * @author Perry Sakkaris - psakkaris at gmail.com * @see GET - * friendships/show | Twitter Developers + * friendships/show | Twitter Developers * @since Twitter4J 2.1.0 */ public interface Relationship extends TwitterResponse { - boolean canSourceDMTarget(); + boolean canSourceDMTarget(); - boolean canSourceMediaTagTarget(); + boolean canSourceMediaTagTarget(); - /** - * Returns the source user id - * - * @return the source user id - */ - long getSourceUserId(); + /** + * Returns the source user id + * + * @return the source user id + */ + long getSourceUserId(); - /** - * Returns the source user screen name - * - * @return returns the source user screen name - */ - String getSourceUserScreenName(); + /** + * Returns the source user screen name + * + * @return returns the source user screen name + */ + String getSourceUserScreenName(); - /** - * Returns the target user id - * - * @return target user id - */ - long getTargetUserId(); + /** + * Returns the target user id + * + * @return target user id + */ + long getTargetUserId(); - /** - * Returns the target user screen name - * - * @return the target user screen name - */ - String getTargetUserScreenName(); + /** + * Returns the target user screen name + * + * @return the target user screen name + */ + String getTargetUserScreenName(); - /** - * Returns if the source user is blocking the target user - * - * @return if the source is blocking the target - */ - boolean isSourceBlockingTarget(); + /** + * Returns if the source user is blocking the target user + * + * @return if the source is blocking the target + */ + boolean isSourceBlockingTarget(); - /** - * Checks if source user is being followed by target user - * - * @return true if source user is being followed by target user - */ - boolean isSourceFollowedByTarget(); + boolean isSourceBlockedByTarget(); - /** - * Checks if source user is following target user - * - * @return true if source user is following target user - */ - boolean isSourceFollowingTarget(); + /** + * Checks if source user is being followed by target user + * + * @return true if source user is being followed by target user + */ + boolean isSourceFollowedByTarget(); - boolean isSourceMarkedTargetAsSpam(); + /** + * Checks if source user is following target user + * + * @return true if source user is following target user + */ + boolean isSourceFollowingTarget(); - boolean isSourceMutingTarget(); + boolean isSourceMarkedTargetAsSpam(); - /** - * Checks if the source user has enabled notifications for updates of the - * target user - * - * @return true if source user enabled notifications for target user - */ - boolean isSourceNotificationsEnabled(); + boolean isSourceMutingTarget(); - /** - * Checks if target user is being followed by source user.
- * This method is equivalent to isSourceFollowingTarget(). - * - * @return true if target user is being followed by source user - */ - boolean isTargetFollowedBySource(); + /** + * Checks if the source user has enabled notifications for updates of the + * target user + * + * @return true if source user enabled notifications for target user + */ + boolean isSourceNotificationsEnabled(); - /** - * Checks if target user is following source user.
- * This method is equivalent to isSourceFollowedByTarget(). - * - * @return true if target user is following source user - */ - boolean isTargetFollowingSource(); + /** + * Checks if target user is being followed by source user.
+ * This method is equivalent to isSourceFollowingTarget(). + * + * @return true if target user is being followed by source user + */ + boolean isTargetFollowedBySource(); + + /** + * Checks if target user is following source user.
+ * This method is equivalent to isSourceFollowedByTarget(). + * + * @return true if target user is following source user + */ + boolean isTargetFollowingSource(); } diff --git a/twidere/src/main/java/twitter4j/SettingsUpdate.java b/twidere/src/main/java/twitter4j/SettingsUpdate.java new file mode 100644 index 000000000..d45a307d7 --- /dev/null +++ b/twidere/src/main/java/twitter4j/SettingsUpdate.java @@ -0,0 +1,84 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package twitter4j; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; + +import twitter4j.http.HttpParameter; + +/** + * Created by mariotaku on 15/1/6. + */ +public class SettingsUpdate implements Serializable { + + private final HashMap settingsMap = new HashMap<>(); + + public void set(String key, boolean value) { + settingsMap.put(key, new HttpParameter(key, value)); + } + + public void set(String key, int value) { + settingsMap.put(key, new HttpParameter(key, value)); + } + + public void set(String key, String value) { + settingsMap.put(key, new HttpParameter(key, value)); + } + + public void setTrendLocationWoeid(int woeid) { + set("trend_location_woeid", woeid); + } + + public void setSleepTimeEnabled(boolean enabled) { + set("sleep_time_enabled", enabled); + } + + public void setStartSleepTime(int startSleepTime) { + set("start_sleep_time", startSleepTime); + } + + public void setEndSleepTime(int endSleepTime) { + set("end_sleep_time", endSleepTime); + } + + public void setTimezone(String timezone) { + set("time_zone", timezone); + } + + public void setProtected(boolean userProtected) { + set("protected", userProtected); + } + + public void setLang(String lang) { + set("lang", lang); + } + + public void setScreenName(String screenName) { + set("screen_name", screenName); + } + + + void addToHttpParameterList(List parameterList) { + parameterList.addAll(settingsMap.values()); + } + +} diff --git a/twidere/src/main/java/twitter4j/Twitter.java b/twidere/src/main/java/twitter4j/Twitter.java index e475f7b7d..247fd57ab 100644 --- a/twidere/src/main/java/twitter4j/Twitter.java +++ b/twidere/src/main/java/twitter4j/Twitter.java @@ -23,16 +23,17 @@ import twitter4j.api.HelpResources; import twitter4j.api.ListsResources; import twitter4j.api.MediaResources; import twitter4j.api.PlacesGeoResources; +import twitter4j.api.PrivateActivityResources; +import twitter4j.api.PrivateDirectMessagesResources; +import twitter4j.api.PrivateFriendsFollowersResources; +import twitter4j.api.PrivateTimelinesResources; +import twitter4j.api.PrivateTweetResources; import twitter4j.api.SavedSearchesResources; import twitter4j.api.SearchResource; import twitter4j.api.SpamReportingResources; import twitter4j.api.TimelinesResources; import twitter4j.api.TrendsResources; import twitter4j.api.TweetResources; -import twitter4j.api.UndocumentedActivityResources; -import twitter4j.api.UndocumentedFriendsFollowersResources; -import twitter4j.api.UndocumentedTimelinesResources; -import twitter4j.api.UndocumentedTweetResources; import twitter4j.api.UsersResources; import twitter4j.auth.OAuthSupport; @@ -41,8 +42,8 @@ import twitter4j.auth.OAuthSupport; * @since Twitter4J 2.2.0 */ public interface Twitter extends OAuthSupport, TwitterConstants, TwitterBase, SearchResource, TimelinesResources, - TweetResources, UsersResources, ListsResources, DirectMessagesResources, FriendsFollowersResources, - FavoritesResources, SpamReportingResources, SavedSearchesResources, TrendsResources, PlacesGeoResources, - HelpResources, UndocumentedActivityResources, UndocumentedTweetResources, UndocumentedTimelinesResources, - UndocumentedFriendsFollowersResources, MediaResources { + TweetResources, UsersResources, ListsResources, DirectMessagesResources, FriendsFollowersResources, + FavoritesResources, SpamReportingResources, SavedSearchesResources, TrendsResources, PlacesGeoResources, + HelpResources, PrivateActivityResources, PrivateTweetResources, PrivateTimelinesResources, + PrivateFriendsFollowersResources, PrivateDirectMessagesResources, MediaResources { } diff --git a/twidere/src/main/java/twitter4j/TwitterConstants.java b/twidere/src/main/java/twitter4j/TwitterConstants.java index 7d1bd00bb..204b7a3a1 100644 --- a/twidere/src/main/java/twitter4j/TwitterConstants.java +++ b/twidere/src/main/java/twitter4j/TwitterConstants.java @@ -59,6 +59,8 @@ public interface TwitterConstants { public static final String ENDPOINT_DIRECT_MESSAGES_SENT = "direct_messages/sent.json"; public static final String ENDPOINT_DIRECT_MESSAGES_SHOW = "direct_messages/show.json"; + public static final String TEMPLATE_DM_CONVERSATION_DELETE = "dm/conversation/%d-%d/delete.json"; + public static final String ENDPOINT_FAVORITES_LIST = "favorites/list.json"; public static final String ENDPOINT_FAVORITES_CREATE = "favorites/create.json"; public static final String ENDPOINT_FAVORITES_DESTROY = "favorites/destroy.json"; diff --git a/twidere/src/main/java/twitter4j/TwitterImpl.java b/twidere/src/main/java/twitter4j/TwitterImpl.java index 06d27932a..0e1d0f419 100644 --- a/twidere/src/main/java/twitter4j/TwitterImpl.java +++ b/twidere/src/main/java/twitter4j/TwitterImpl.java @@ -299,6 +299,15 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter { INCLUDE_ENTITIES)); } + @Override + public void destroyDirectMessagesConversation(long userId) throws TwitterException { + final String url = conf.getRestBaseURL() + + String.format(Locale.ROOT, TEMPLATE_DM_CONVERSATION_DELETE, id, userId); + final String signUrl = conf.getSigningRestBaseURL() + + String.format(Locale.ROOT, TEMPLATE_DM_CONVERSATION_DELETE, id, userId); + post(url, signUrl); + } + @Override public Status destroyFavorite(final long id) throws TwitterException { ensureAuthorizationEnabled(); @@ -1069,15 +1078,19 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter { } @Override - public ResponseList getUserLists(final long listOwnerUserId) throws TwitterException { - return factory.createUserListList(get(conf.getRestBaseURL() + ENDPOINT_LISTS_LIST, conf.getSigningRestBaseURL() - + ENDPOINT_LISTS_LIST, new HttpParameter("user_id", listOwnerUserId))); + public ResponseList getUserLists(final long userId, final boolean reverse) throws TwitterException { + final String url = conf.getRestBaseURL() + ENDPOINT_LISTS_LIST; + final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_LISTS_LIST; + return factory.createUserListList(get(url, signUrl, + new HttpParameter("user_id", userId), new HttpParameter("reverse", reverse))); } @Override - public ResponseList getUserLists(final String listOwnerScreenName) throws TwitterException { - return factory.createUserListList(get(conf.getRestBaseURL() + ENDPOINT_LISTS_LIST, conf.getSigningRestBaseURL() - + ENDPOINT_LISTS_LIST, new HttpParameter("screen_name", listOwnerScreenName))); + public ResponseList getUserLists(final String screenName, final boolean reverse) throws TwitterException { + final String url = conf.getRestBaseURL() + ENDPOINT_LISTS_LIST; + final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_LISTS_LIST; + return factory.createUserListList(get(url, signUrl, + new HttpParameter("screen_name", screenName), new HttpParameter("reverse", reverse))); } @Override @@ -1497,23 +1510,14 @@ final class TwitterImpl extends TwitterBaseImpl implements Twitter { } @Override - public AccountSettings updateAccountSettings(final Integer trend_locationWoeid, final Boolean sleep_timeEnabled, - final String start_sleepTime, final String end_sleepTime, final String time_zone, final String lang) - throws TwitterException { - + public AccountSettings updateAccountSettings(final SettingsUpdate settingsUpdate) throws TwitterException { ensureAuthorizationEnabled(); - - final List params = new ArrayList(6); - addParameterToList(params, "trend_location_woeid", trend_locationWoeid); - addParameterToList(params, "sleep_time_enabled", sleep_timeEnabled); - addParameterToList(params, "start_sleep_time", start_sleepTime); - addParameterToList(params, "end_sleep_time", end_sleepTime); - addParameterToList(params, "time_zone", time_zone); - addParameterToList(params, "lang", lang); + final List params = new ArrayList<>(); + settingsUpdate.addToHttpParameterList(params); params.add(INCLUDE_ENTITIES); - return factory.createAccountSettings(post(conf.getRestBaseURL() + ENDPOINT_ACCOUNT_SETTINGS, - conf.getSigningRestBaseURL() + ENDPOINT_ACCOUNT_SETTINGS, - params.toArray(new HttpParameter[params.size()]))); + final String url = conf.getRestBaseURL() + ENDPOINT_ACCOUNT_SETTINGS; + final String signUrl = conf.getSigningRestBaseURL() + ENDPOINT_ACCOUNT_SETTINGS; + return factory.createAccountSettings(post(url, signUrl, params.toArray(new HttpParameter[params.size()]))); } @Override diff --git a/twidere/src/main/java/twitter4j/api/ListsResources.java b/twidere/src/main/java/twitter4j/api/ListsResources.java index ec96d5123..d4ab33ba8 100644 --- a/twidere/src/main/java/twitter4j/api/ListsResources.java +++ b/twidere/src/main/java/twitter4j/api/ListsResources.java @@ -309,14 +309,14 @@ public interface ListsResources { * returned.
* This method calls http://api.twitter.com/1.1/lists.json * - * @param listOwnerUserId The id of the list owner + * @param userId The id of the list owner * @return the list of lists * @throws TwitterException when Twitter service or network is unavailable * @see GET lists | * Twitter Developers * @since Twitter4J 2.2.3 */ - ResponseList getUserLists(long listOwnerUserId) throws TwitterException; + ResponseList getUserLists(long userId, boolean reverse) throws TwitterException; /** * List the lists of the specified user. Private lists will be included if @@ -324,14 +324,14 @@ public interface ListsResources { * returned.
* This method calls http://api.twitter.com/1.1/lists.json * - * @param listOwnerScreenName The screen name of the list owner + * @param screenName The screen name of the user * @return the list of lists * @throws TwitterException when Twitter service or network is unavailable * @see GET lists | * Twitter Developers * @since Twitter4J 2.1.0 */ - ResponseList getUserLists(String listOwnerScreenName) throws TwitterException; + ResponseList getUserLists(String screenName, boolean reverse) throws TwitterException; /** * Show tweet timeline for members of the specified list.
diff --git a/twidere/src/main/java/twitter4j/api/UndocumentedActivityResources.java b/twidere/src/main/java/twitter4j/api/PrivateActivityResources.java similarity index 86% rename from twidere/src/main/java/twitter4j/api/UndocumentedActivityResources.java rename to twidere/src/main/java/twitter4j/api/PrivateActivityResources.java index 65b665569..09e96c6f5 100644 --- a/twidere/src/main/java/twitter4j/api/UndocumentedActivityResources.java +++ b/twidere/src/main/java/twitter4j/api/PrivateActivityResources.java @@ -5,7 +5,7 @@ import twitter4j.Paging; import twitter4j.ResponseList; import twitter4j.TwitterException; -public interface UndocumentedActivityResources extends UndocumentedResources { +public interface PrivateActivityResources extends PrivateResources { public ResponseList getActivitiesAboutMe() throws TwitterException; public ResponseList getActivitiesAboutMe(Paging paging) throws TwitterException; diff --git a/twidere/src/main/java/twitter4j/api/PrivateDirectMessagesResources.java b/twidere/src/main/java/twitter4j/api/PrivateDirectMessagesResources.java new file mode 100644 index 000000000..f092e4598 --- /dev/null +++ b/twidere/src/main/java/twitter4j/api/PrivateDirectMessagesResources.java @@ -0,0 +1,31 @@ +/* + * Twidere - Twitter client for Android + * + * Copyright (C) 2012-2015 Mariotaku Lee + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package twitter4j.api; + +import twitter4j.TwitterException; + +/** + * Created by mariotaku on 15/1/6. + */ +public interface PrivateDirectMessagesResources extends PrivateResources { + + void destroyDirectMessagesConversation(long userId) throws TwitterException; + +} diff --git a/twidere/src/main/java/twitter4j/api/UndocumentedFriendsFollowersResources.java b/twidere/src/main/java/twitter4j/api/PrivateFriendsFollowersResources.java similarity index 80% rename from twidere/src/main/java/twitter4j/api/UndocumentedFriendsFollowersResources.java rename to twidere/src/main/java/twitter4j/api/PrivateFriendsFollowersResources.java index a94719875..7d2bed674 100644 --- a/twidere/src/main/java/twitter4j/api/UndocumentedFriendsFollowersResources.java +++ b/twidere/src/main/java/twitter4j/api/PrivateFriendsFollowersResources.java @@ -3,7 +3,7 @@ package twitter4j.api; import twitter4j.TwitterException; import twitter4j.User; -public interface UndocumentedFriendsFollowersResources extends UndocumentedResources { +public interface PrivateFriendsFollowersResources extends PrivateResources { public User acceptFriendship(long userId) throws TwitterException; diff --git a/twidere/src/main/java/twitter4j/api/PrivateResources.java b/twidere/src/main/java/twitter4j/api/PrivateResources.java new file mode 100644 index 000000000..6dcedd727 --- /dev/null +++ b/twidere/src/main/java/twitter4j/api/PrivateResources.java @@ -0,0 +1,5 @@ +package twitter4j.api; + +public interface PrivateResources { + +} diff --git a/twidere/src/main/java/twitter4j/api/UndocumentedTimelinesResources.java b/twidere/src/main/java/twitter4j/api/PrivateTimelinesResources.java similarity index 88% rename from twidere/src/main/java/twitter4j/api/UndocumentedTimelinesResources.java rename to twidere/src/main/java/twitter4j/api/PrivateTimelinesResources.java index 4f3c3d183..89ceca743 100644 --- a/twidere/src/main/java/twitter4j/api/UndocumentedTimelinesResources.java +++ b/twidere/src/main/java/twitter4j/api/PrivateTimelinesResources.java @@ -5,7 +5,7 @@ import twitter4j.ResponseList; import twitter4j.Status; import twitter4j.TwitterException; -public interface UndocumentedTimelinesResources extends UndocumentedResources { +public interface PrivateTimelinesResources extends PrivateResources { ResponseList getMediaTimeline() throws TwitterException; diff --git a/twidere/src/main/java/twitter4j/api/UndocumentedTweetResources.java b/twidere/src/main/java/twitter4j/api/PrivateTweetResources.java similarity index 90% rename from twidere/src/main/java/twitter4j/api/UndocumentedTweetResources.java rename to twidere/src/main/java/twitter4j/api/PrivateTweetResources.java index b3ac5cc59..1092cc4ce 100644 --- a/twidere/src/main/java/twitter4j/api/UndocumentedTweetResources.java +++ b/twidere/src/main/java/twitter4j/api/PrivateTweetResources.java @@ -7,7 +7,7 @@ import twitter4j.StatusActivitySummary; import twitter4j.TranslationResult; import twitter4j.TwitterException; -public interface UndocumentedTweetResources extends UndocumentedResources { +public interface PrivateTweetResources extends PrivateResources { StatusActivitySummary getStatusActivitySummary(long statusId) throws TwitterException; diff --git a/twidere/src/main/java/twitter4j/api/UndocumentedResources.java b/twidere/src/main/java/twitter4j/api/UndocumentedResources.java deleted file mode 100644 index e62194f22..000000000 --- a/twidere/src/main/java/twitter4j/api/UndocumentedResources.java +++ /dev/null @@ -1,5 +0,0 @@ -package twitter4j.api; - -public interface UndocumentedResources { - -} diff --git a/twidere/src/main/java/twitter4j/api/UsersResources.java b/twidere/src/main/java/twitter4j/api/UsersResources.java index 207c35e0e..f98051553 100644 --- a/twidere/src/main/java/twitter4j/api/UsersResources.java +++ b/twidere/src/main/java/twitter4j/api/UsersResources.java @@ -16,573 +16,562 @@ package twitter4j.api; +import java.io.File; +import java.io.InputStream; + import twitter4j.AccountSettings; import twitter4j.Category; import twitter4j.CursorPaging; import twitter4j.IDs; import twitter4j.PagableResponseList; import twitter4j.ResponseList; +import twitter4j.SettingsUpdate; import twitter4j.TwitterException; import twitter4j.User; -import java.io.File; -import java.io.InputStream; - /** * @author Joern Huxhorn - jhuxhorn at googlemail.com */ public interface UsersResources { - /** - * Blocks the user specified in the ID parameter as the authenticating user. - * Returns the blocked user in the requested format when successful.
- * This method calls http://api.twitter.com/1.1/blocks/create/[id].json - * - * @param userId the ID of the user to block - * @return the blocked user - * @throws TwitterException when Twitter service or network is unavailable - * @see POST - * blocks/create | Twitter Developers - * @since Twitter4J 2.1.0 - */ - User createBlock(long userId) throws TwitterException; + /** + * Blocks the user specified in the ID parameter as the authenticating user. + * Returns the blocked user in the requested format when successful.
+ * This method calls http://api.twitter.com/1.1/blocks/create/[id].json + * + * @param userId the ID of the user to block + * @return the blocked user + * @throws TwitterException when Twitter service or network is unavailable + * @see POST + * blocks/create | Twitter Developers + * @since Twitter4J 2.1.0 + */ + User createBlock(long userId) throws TwitterException; - /** - * Blocks the user specified in the ID parameter as the authenticating user. - * Returns the blocked user in the requested format when successful.
- * This method calls http://api.twitter.com/1.1/blocks/create/[id].json - * - * @param screenName the screen_name of the user to block - * @return the blocked user - * @throws TwitterException when Twitter service or network is unavailable - * @see POST - * blocks/create | Twitter Developers - * @since Twitter4J 2.0.1 - */ - User createBlock(String screenName) throws TwitterException; + /** + * Blocks the user specified in the ID parameter as the authenticating user. + * Returns the blocked user in the requested format when successful.
+ * This method calls http://api.twitter.com/1.1/blocks/create/[id].json + * + * @param screenName the screen_name of the user to block + * @return the blocked user + * @throws TwitterException when Twitter service or network is unavailable + * @see POST + * blocks/create | Twitter Developers + * @since Twitter4J 2.0.1 + */ + User createBlock(String screenName) throws TwitterException; - User createMute(long userId) throws TwitterException; + User createMute(long userId) throws TwitterException; - User createMute(String screenName) throws TwitterException; + User createMute(String screenName) throws TwitterException; - /** - * Un-blocks the user specified in the ID parameter as the authenticating - * user. Returns the un-blocked user in the requested format when - * successful.
- * This method calls http://api.twitter.com/1.1/blocks/destroy/[id].json - * - * @param userId the ID of the user to block - * @return the unblocked user - * @throws TwitterException when Twitter service or network is unavailable - * @see POST - * blocks/destroy | Twitter Developers - * @since Twitter4J 2.0.1 - */ - User destroyBlock(long userId) throws TwitterException; + /** + * Un-blocks the user specified in the ID parameter as the authenticating + * user. Returns the un-blocked user in the requested format when + * successful.
+ * This method calls http://api.twitter.com/1.1/blocks/destroy/[id].json + * + * @param userId the ID of the user to block + * @return the unblocked user + * @throws TwitterException when Twitter service or network is unavailable + * @see POST + * blocks/destroy | Twitter Developers + * @since Twitter4J 2.0.1 + */ + User destroyBlock(long userId) throws TwitterException; - /** - * Un-blocks the user specified in the ID parameter as the authenticating - * user. Returns the un-blocked user in the requested format when - * successful.
- * This method calls http://api.twitter.com/1.1/blocks/destroy/[id].json - * - * @param screenName the screen name of the user to block - * @return the unblocked user - * @throws TwitterException when Twitter service or network is unavailable - * @see POST - * blocks/destroy | Twitter Developers - * @since Twitter4J 2.0.1 - */ - User destroyBlock(String screenName) throws TwitterException; + /** + * Un-blocks the user specified in the ID parameter as the authenticating + * user. Returns the un-blocked user in the requested format when + * successful.
+ * This method calls http://api.twitter.com/1.1/blocks/destroy/[id].json + * + * @param screenName the screen name of the user to block + * @return the unblocked user + * @throws TwitterException when Twitter service or network is unavailable + * @see POST + * blocks/destroy | Twitter Developers + * @since Twitter4J 2.0.1 + */ + User destroyBlock(String screenName) throws TwitterException; - User destroyMute(long userId) throws TwitterException; + User destroyMute(long userId) throws TwitterException; - User destroyMute(String screenName) throws TwitterException; + User destroyMute(String screenName) throws TwitterException; - /** - * Returns the current trend, geo, language, timezone and sleep time - * information for the authenticating user.
- * This method has not been finalized and the interface is subject to change - * in incompatible ways.
- * This method calls http://api.twitter.com/1.1/account/settings.json - * - * @return the current trend, geo and sleep time information for the - * authenticating user. - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * account/settings | Twitter Developers - * @since Twitter4J 2.1.9 - */ - AccountSettings getAccountSettings() throws TwitterException; + /** + * Returns the current trend, geo, language, timezone and sleep time + * information for the authenticating user.
+ * This method has not been finalized and the interface is subject to change + * in incompatible ways.
+ * This method calls http://api.twitter.com/1.1/account/settings.json + * + * @return the current trend, geo and sleep time information for the + * authenticating user. + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * account/settings | Twitter Developers + * @since Twitter4J 2.1.9 + */ + AccountSettings getAccountSettings() throws TwitterException; - /** - * Returns an array of numeric user ids the authenticating user is blocking.
- * This method calls http://api.twitter.com/1.1/blocks/blocks/ids - * - * @return Returns an array of numeric user ids the authenticating user is - * blocking. - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * blocks/ids | Twitter Developers - * @since Twitter4J 2.0.4 - */ - IDs getBlocksIDs() throws TwitterException; + /** + * Returns an array of numeric user ids the authenticating user is blocking.
+ * This method calls http://api.twitter.com/1.1/blocks/blocks/ids + * + * @return Returns an array of numeric user ids the authenticating user is + * blocking. + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * blocks/ids | Twitter Developers + * @since Twitter4J 2.0.4 + */ + IDs getBlocksIDs() throws TwitterException; - IDs getBlocksIDs(CursorPaging paging) throws TwitterException; + IDs getBlocksIDs(CursorPaging paging) throws TwitterException; - /** - * Returns a list of user objects that the authenticating user is blocking.
- * This method calls http://api.twitter.com/1.1/blocks/blocking.json - * - * @return a list of user objects that the authenticating user - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * blocks/blocking | Twitter Developers - * @since Twitter4J 2.0.4 - */ - PagableResponseList getBlocksList() throws TwitterException; + /** + * Returns a list of user objects that the authenticating user is blocking.
+ * This method calls http://api.twitter.com/1.1/blocks/blocking.json + * + * @return a list of user objects that the authenticating user + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * blocks/blocking | Twitter Developers + * @since Twitter4J 2.0.4 + */ + PagableResponseList getBlocksList() throws TwitterException; - PagableResponseList getBlocksList(CursorPaging paging) throws TwitterException; + PagableResponseList getBlocksList(CursorPaging paging) throws TwitterException; - /** - * Access the users in a given category of the Twitter suggested user list - * and return their most recent status if they are not a protected user.
- * This method has not been finalized and the interface is subject to change - * in incompatible ways.
- * This method calls - * http://api.twitter.com/1.1/users/suggestions/:slug/members.json - * - * @param categorySlug slug - * @return list of suggested users - * @throws TwitterException when Twitter service or network is unavailable - * @see #newtwitter - * and the API - Twitter API Announcements | Google Group - * @since Twitter4J 2.1.9 - */ - ResponseList getMemberSuggestions(String categorySlug) throws TwitterException; + /** + * Access the users in a given category of the Twitter suggested user list + * and return their most recent status if they are not a protected user.
+ * This method has not been finalized and the interface is subject to change + * in incompatible ways.
+ * This method calls + * http://api.twitter.com/1.1/users/suggestions/:slug/members.json + * + * @param categorySlug slug + * @return list of suggested users + * @throws TwitterException when Twitter service or network is unavailable + * @see #newtwitter + * and the API - Twitter API Announcements | Google Group + * @since Twitter4J 2.1.9 + */ + ResponseList getMemberSuggestions(String categorySlug) throws TwitterException; - IDs getMutesUsersIDs() throws TwitterException; + IDs getMutesUsersIDs() throws TwitterException; - IDs getMutesUsersIDs(CursorPaging paging) throws TwitterException; + IDs getMutesUsersIDs(CursorPaging paging) throws TwitterException; - PagableResponseList getMutesUsersList() throws TwitterException; + PagableResponseList getMutesUsersList() throws TwitterException; - PagableResponseList getMutesUsersList(CursorPaging paging) throws TwitterException; + PagableResponseList getMutesUsersList(CursorPaging paging) throws TwitterException; - /** - * Access to Twitter's suggested user list. This returns the list of - * suggested user categories. The category can be used in the - * users/suggestions/category endpoint to get the users in that category.
- * This method calls http://api.twitter.com/1.1/users/suggestions/:slug.json - * - * @return list of suggested user categories. - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * users/suggestions/:slug | Twitter Developers - * @since Twitter4J 2.1.1 - */ - ResponseList getSuggestedUserCategories() throws TwitterException; + /** + * Access to Twitter's suggested user list. This returns the list of + * suggested user categories. The category can be used in the + * users/suggestions/category endpoint to get the users in that category.
+ * This method calls http://api.twitter.com/1.1/users/suggestions/:slug.json + * + * @return list of suggested user categories. + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * users/suggestions/:slug | Twitter Developers + * @since Twitter4J 2.1.1 + */ + ResponseList getSuggestedUserCategories() throws TwitterException; - /** - * Access the users in a given category of the Twitter suggested user list.
- * It is recommended that end clients cache this data for no more than one - * hour.
- * This method calls http://api.twitter.com/1.1/users/suggestions/:slug.json - * - * @param categorySlug slug - * @return list of suggested users - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * users/suggestions/slug | Twitter Developers - * @since Twitter4J 2.1.1 - */ - ResponseList getUserSuggestions(String categorySlug) throws TwitterException; + /** + * Access the users in a given category of the Twitter suggested user list.
+ * It is recommended that end clients cache this data for no more than one + * hour.
+ * This method calls http://api.twitter.com/1.1/users/suggestions/:slug.json + * + * @param categorySlug slug + * @return list of suggested users + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * users/suggestions/slug | Twitter Developers + * @since Twitter4J 2.1.1 + */ + ResponseList getUserSuggestions(String categorySlug) throws TwitterException; - /** - * Return up to 100 users worth of extended information, specified by either - * ID, screen name, or combination of the two. The author's most recent - * status (if the authenticating user has permission) will be returned - * inline.
- * This method calls http://api.twitter.com/1.1/users/lookup.json - * - * @param ids Specifies the screen names of the users to return. - * @return users - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * users/lookup | Twitter Developers - * @since Twitter4J 2.1.1 - */ - ResponseList lookupUsers(long[] ids) throws TwitterException; + /** + * Return up to 100 users worth of extended information, specified by either + * ID, screen name, or combination of the two. The author's most recent + * status (if the authenticating user has permission) will be returned + * inline.
+ * This method calls http://api.twitter.com/1.1/users/lookup.json + * + * @param ids Specifies the screen names of the users to return. + * @return users + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * users/lookup | Twitter Developers + * @since Twitter4J 2.1.1 + */ + ResponseList lookupUsers(long[] ids) throws TwitterException; - /** - * Return up to 100 users worth of extended information, specified by either - * ID, screen name, or combination of the two. The author's most recent - * status (if the authenticating user has permission) will be returned - * inline.
- * This method calls http://api.twitter.com/1.1/users/lookup.json - * - * @param screenNames Specifies the screen names of the users to return. - * @return users - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * users/lookup | Twitter Developers - * @since Twitter4J 2.1.1 - */ - ResponseList lookupUsers(String[] screenNames) throws TwitterException; + /** + * Return up to 100 users worth of extended information, specified by either + * ID, screen name, or combination of the two. The author's most recent + * status (if the authenticating user has permission) will be returned + * inline.
+ * This method calls http://api.twitter.com/1.1/users/lookup.json + * + * @param screenNames Specifies the screen names of the users to return. + * @return users + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * users/lookup | Twitter Developers + * @since Twitter4J 2.1.1 + */ + ResponseList lookupUsers(String[] screenNames) throws TwitterException; - void removeProfileBannerImage() throws TwitterException; + void removeProfileBannerImage() throws TwitterException; - /** - * Run a search for users similar to the Find People button on Twitter.com; - * the same results returned by people search on Twitter.com will be - * returned by using this API.
- * Usage note: It is only possible to retrieve the first 1000 matches from - * this API.
- * This method calls http://api.twitter.com/1.1/users/search.json - * - * @param query The query to run against people search. - * @param page Specifies the page of results to retrieve. Number of statuses - * per page is fixed to 20. - * @return the list of Users matches the provided - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * users/search | Twitter Developers - */ - ResponseList searchUsers(String query, int page) throws TwitterException; + /** + * Run a search for users similar to the Find People button on Twitter.com; + * the same results returned by people search on Twitter.com will be + * returned by using this API.
+ * Usage note: It is only possible to retrieve the first 1000 matches from + * this API.
+ * This method calls http://api.twitter.com/1.1/users/search.json + * + * @param query The query to run against people search. + * @param page Specifies the page of results to retrieve. Number of statuses + * per page is fixed to 20. + * @return the list of Users matches the provided + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * users/search | Twitter Developers + */ + ResponseList searchUsers(String query, int page) throws TwitterException; - /** - * Returns extended information of a given user, specified by ID or screen - * name as per the required id parameter. The author's most recent status - * will be returned inline.
- * This method calls http://api.twitter.com/1.1/users/show.json - * - * @param userId the ID of the user for whom to request the detail - * @return users - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * users/show | Twitter Developers - * @since Twitter4J 2.1.0 - */ - User showUser(long userId) throws TwitterException; + /** + * Returns extended information of a given user, specified by ID or screen + * name as per the required id parameter. The author's most recent status + * will be returned inline.
+ * This method calls http://api.twitter.com/1.1/users/show.json + * + * @param userId the ID of the user for whom to request the detail + * @return users + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * users/show | Twitter Developers + * @since Twitter4J 2.1.0 + */ + User showUser(long userId) throws TwitterException; - /** - * Returns extended information of a given user, specified by ID or screen - * name as per the required id parameter. The author's most recent status - * will be returned inline.
- * This method calls http://api.twitter.com/1.1/users/show.json - * - * @param screenName the screen name of the user for whom to request the - * detail - * @return User - * @throws TwitterException when Twitter service or network is unavailable - * @see GET - * users/show | Twitter Developers - */ - User showUser(String screenName) throws TwitterException; + /** + * Returns extended information of a given user, specified by ID or screen + * name as per the required id parameter. The author's most recent status + * will be returned inline.
+ * This method calls http://api.twitter.com/1.1/users/show.json + * + * @param screenName the screen name of the user for whom to request the + * detail + * @return User + * @throws TwitterException when Twitter service or network is unavailable + * @see GET + * users/show | Twitter Developers + */ + User showUser(String screenName) throws TwitterException; - /** - * Updates the current trend, geo, language, timezone and sleep time - * information for the authenticating user.
- * This method has not been finalized and the interface is subject to change - * in incompatible ways.
- * This method calls http://api.twitter.com/1.1/account/settings.json - * - * @param trendLocationWoeid Optional. The Yahoo! Where On Earth ID to use - * as the user's default trend location. - * @param sleepTimeEnabled Optional. Whether sleep time is enabled for the - * user - * @param startSleepTime Optional. The hour that sleep time should begin if - * it is enabled. - * @param endSleepTime Optional. The hour that sleep time should end if it - * is enabled. - * @param timeZone Optional. The timezone dates and times should be - * displayed in for the user. - * @param lang Optional. The language which Twitter should render in for - * this user. (two letter ISO 639-1) - * @return the current trend, geo and sleep time information for the - * authenticating user. - * @throws TwitterException when Twitter service or network is unavailable - * @see POST - * account/settings | Twitter Developers - * @since Twitter4J 2.2.4 - */ - AccountSettings updateAccountSettings(Integer trendLocationWoeid, Boolean sleepTimeEnabled, String startSleepTime, - String endSleepTime, String timeZone, String lang) throws TwitterException; + /** + * Updates the current trend, geo, language, timezone and sleep time + * information for the authenticating user.
+ * This method has not been finalized and the interface is subject to change + * in incompatible ways.
+ * This method calls http://api.twitter.com/1.1/account/settings.json + * + * @param settingsUpdate Settings to be updated + * @return the current trend, geo and sleep time information for the + * authenticating user. + * @throws TwitterException when Twitter service or network is unavailable + * @see POST + * account/settings | Twitter Developers + * @since Twitter4J 2.2.4 + */ + AccountSettings updateAccountSettings(SettingsUpdate settingsUpdate) throws TwitterException; - /** - * Sets values that users are able to set under the "Account" tab of their - * settings page. Only the parameters specified(non-null) will be updated.
- * This method calls http://api.twitter.com/1.1/account/update_profile.json - * - * @param name Optional. Maximum of 20 characters. - * @param url Optional. Maximum of 100 characters. Will be prepended with - * "http://" if not present. - * @param location Optional. Maximum of 30 characters. The contents are not - * normalized or geocoded in any way. - * @param description Optional. Maximum of 160 characters. - * @return the updated user - * @throws TwitterException when Twitter service or network is unavailable - * @see POST - * account/update_profile | Twitter Developers - * @since Twitter4J 2.1.8 - */ - User updateProfile(String name, String url, String location, String description) throws TwitterException; + /** + * Sets values that users are able to set under the "Account" tab of their + * settings page. Only the parameters specified(non-null) will be updated.
+ * This method calls http://api.twitter.com/1.1/account/update_profile.json + * + * @param name Optional. Maximum of 20 characters. + * @param url Optional. Maximum of 100 characters. Will be prepended with + * "http://" if not present. + * @param location Optional. Maximum of 30 characters. The contents are not + * normalized or geocoded in any way. + * @param description Optional. Maximum of 160 characters. + * @return the updated user + * @throws TwitterException when Twitter service or network is unavailable + * @see POST + * account/update_profile | Twitter Developers + * @since Twitter4J 2.1.8 + */ + User updateProfile(String name, String url, String location, String description) throws TwitterException; - /** - * Updates the authenticating user's profile background image.
- * This method calls - * http://api.twitter.com/1.1/account/update_profile_background_image.json - * - * @param image Must be a valid GIF, JPG, or PNG image of less than 800 - * kilobytes in size. Images with width larger than 2048 pixels - * will be forceably scaled down. - * @param tile If set to true the background image will be displayed tiled. - * The image will not be tiled otherwise. - * @return the updated user - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - * @see POST - * account/update_profile_background_image | Twitter Developers - * @since Twitter4J 2.1.0 - */ - User updateProfileBackgroundImage(File image, boolean tile) throws TwitterException; + /** + * Updates the authenticating user's profile background image.
+ * This method calls + * http://api.twitter.com/1.1/account/update_profile_background_image.json + * + * @param image Must be a valid GIF, JPG, or PNG image of less than 800 + * kilobytes in size. Images with width larger than 2048 pixels + * will be forceably scaled down. + * @param tile If set to true the background image will be displayed tiled. + * The image will not be tiled otherwise. + * @return the updated user + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + * @see POST + * account/update_profile_background_image | Twitter Developers + * @since Twitter4J 2.1.0 + */ + User updateProfileBackgroundImage(File image, boolean tile) throws TwitterException; - /** - * Updates the authenticating user's profile background image.
- * This method calls - * http://api.twitter.com/1.1/account/update_profile_background_image.json - * - * @param image Must be a valid GIF, JPG, or PNG image of less than 800 - * kilobytes in size. Images with width larger than 2048 pixels - * will be forceably scaled down. - * @param tile If set to true the background image will be displayed tiled. - * The image will not be tiled otherwise. - * @return the updated user - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - * @see POST - * account/update_profile_background_image | Twitter Developers - * @since Twitter4J 2.1.11 - */ - User updateProfileBackgroundImage(InputStream image, boolean tile) throws TwitterException; + /** + * Updates the authenticating user's profile background image.
+ * This method calls + * http://api.twitter.com/1.1/account/update_profile_background_image.json + * + * @param image Must be a valid GIF, JPG, or PNG image of less than 800 + * kilobytes in size. Images with width larger than 2048 pixels + * will be forceably scaled down. + * @param tile If set to true the background image will be displayed tiled. + * The image will not be tiled otherwise. + * @return the updated user + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + * @see POST + * account/update_profile_background_image | Twitter Developers + * @since Twitter4J 2.1.11 + */ + User updateProfileBackgroundImage(InputStream image, boolean tile) throws TwitterException; - /** - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed - *
422The image could not be resized or it too large
- * - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - */ - void updateProfileBannerImage(File banner) throws TwitterException; + /** + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed + *
422The image could not be resized or it too large
+ * + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + */ + void updateProfileBannerImage(File banner) throws TwitterException; - /** - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed - *
422The image could not be resized or it too large
- * - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - */ - void updateProfileBannerImage(File banner, int width, int height, int offsetLeft, int offsetTop) - throws TwitterException; + /** + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed + *
422The image could not be resized or it too large
+ * + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + */ + void updateProfileBannerImage(File banner, int width, int height, int offsetLeft, int offsetTop) + throws TwitterException; - /** - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed - *
422The image could not be resized or it too large
- * - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - */ - void updateProfileBannerImage(InputStream banner) throws TwitterException; + /** + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed + *
422The image could not be resized or it too large
+ * + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + */ + void updateProfileBannerImage(InputStream banner) throws TwitterException; - /** - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed - *
422The image could not be resized or it too large
- * - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - */ - void updateProfileBannerImage(InputStream banner, int width, int height, int offsetLeft, int offsetTop) - throws TwitterException; + /** + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Code(s)Meaning
200,201,202Profile banner image successfully uploaded
400Either an image was not provided or the image data could be processed + *
422The image could not be resized or it too large
+ * + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + */ + void updateProfileBannerImage(InputStream banner, int width, int height, int offsetLeft, int offsetTop) + throws TwitterException; - /** - * Sets one or more hex values that control the color scheme of the - * authenticating user's profile page on twitter.com. Each parameter's value - * must be a valid hexidecimal value, and may be either three or six - * characters (ex: #fff or #ffffff).
- * This method calls - * http://api.twitter.com/1.1/account/update_profile_colors.json - * - * @param profileBackgroundColor optional, can be null - * @param profileTextColor optional, can be null - * @param profileLinkColor optional, can be null - * @param profileSidebarFillColor optional, can be null - * @param profileSidebarBorderColor optional, can be null - * @return the updated user - * @throws TwitterException when Twitter service or network is unavailable - * @see POST - * account/update_profile_colors | Twitter Developers - * @since Twitter4J 2.0.0 - */ - User updateProfileColors(String profileBackgroundColor, String profileTextColor, String profileLinkColor, - String profileSidebarFillColor, String profileSidebarBorderColor) throws TwitterException; + /** + * Sets one or more hex values that control the color scheme of the + * authenticating user's profile page on twitter.com. Each parameter's value + * must be a valid hexidecimal value, and may be either three or six + * characters (ex: #fff or #ffffff).
+ * This method calls + * http://api.twitter.com/1.1/account/update_profile_colors.json + * + * @param profileBackgroundColor optional, can be null + * @param profileTextColor optional, can be null + * @param profileLinkColor optional, can be null + * @param profileSidebarFillColor optional, can be null + * @param profileSidebarBorderColor optional, can be null + * @return the updated user + * @throws TwitterException when Twitter service or network is unavailable + * @see POST + * account/update_profile_colors | Twitter Developers + * @since Twitter4J 2.0.0 + */ + User updateProfileColors(String profileBackgroundColor, String profileTextColor, String profileLinkColor, + String profileSidebarFillColor, String profileSidebarBorderColor) throws TwitterException; - /** - * Updates the authenticating user's profile image.
- * This method calls - * http://api.twitter.com/1.1/account/update_profile_image.json - * - * @param image Must be a valid GIF, JPG, or PNG image of less than 700 - * kilobytes in size. Images with width larger than 500 pixels - * will be scaled down. - * @return the updated user - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - * @see POST - * account/update_profile_image | Twitter Developers - * @since Twitter4J 2.1.0 - */ - User updateProfileImage(File image) throws TwitterException; + /** + * Updates the authenticating user's profile image.
+ * This method calls + * http://api.twitter.com/1.1/account/update_profile_image.json + * + * @param image Must be a valid GIF, JPG, or PNG image of less than 700 + * kilobytes in size. Images with width larger than 500 pixels + * will be scaled down. + * @return the updated user + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + * @see POST + * account/update_profile_image | Twitter Developers + * @since Twitter4J 2.1.0 + */ + User updateProfileImage(File image) throws TwitterException; - /** - * Updates the authenticating user's profile image.
- * This method calls - * http://api.twitter.com/1.1/account/update_profile_image.json - * - * @param image Must be a valid GIF, JPG, or PNG image of less than 700 - * kilobytes in size. Images with width larger than 500 pixels - * will be scaled down. - * @return the updated user - * @throws TwitterException when Twitter service or network is unavailable, - * or when the specified file is not found - * (FileNotFoundException will be nested), or when the specified - * file object in not representing a file (IOException will be - * nested) - * @see POST - * account/update_profile_image | Twitter Developers - * @since Twitter4J 2.1.11 - */ - User updateProfileImage(InputStream image) throws TwitterException; + /** + * Updates the authenticating user's profile image.
+ * This method calls + * http://api.twitter.com/1.1/account/update_profile_image.json + * + * @param image Must be a valid GIF, JPG, or PNG image of less than 700 + * kilobytes in size. Images with width larger than 500 pixels + * will be scaled down. + * @return the updated user + * @throws TwitterException when Twitter service or network is unavailable, + * or when the specified file is not found + * (FileNotFoundException will be nested), or when the specified + * file object in not representing a file (IOException will be + * nested) + * @see POST + * account/update_profile_image | Twitter Developers + * @since Twitter4J 2.1.11 + */ + User updateProfileImage(InputStream image) throws TwitterException; - /** - * Returns an HTTP 200 OK response code and a representation of the - * requesting user if authentication was successful; returns a 401 status - * code and an error message if not. Use this method to test if supplied - * user credentials are valid.
- * This method calls - * http://api.twitter.com/1.1/account/verify_credentials.json - * - * @return user - * @throws twitter4j.TwitterException when Twitter service or network is - * unavailable, or if supplied credential is wrong - * (TwitterException.getStatusCode() == 401) - * @see GET - * account/verify_credentials | Twitter Developers - * @since Twitter4J 2.0.0 - */ - User verifyCredentials() throws TwitterException; + /** + * Returns an HTTP 200 OK response code and a representation of the + * requesting user if authentication was successful; returns a 401 status + * code and an error message if not. Use this method to test if supplied + * user credentials are valid.
+ * This method calls + * http://api.twitter.com/1.1/account/verify_credentials.json + * + * @return user + * @throws twitter4j.TwitterException when Twitter service or network is + * unavailable, or if supplied credential is wrong + * (TwitterException.getStatusCode() == 401) + * @see GET + * account/verify_credentials | Twitter Developers + * @since Twitter4J 2.0.0 + */ + User verifyCredentials() throws TwitterException; } diff --git a/twidere/src/main/java/twitter4j/internal/json/RelationshipJSONImpl.java b/twidere/src/main/java/twitter4j/internal/json/RelationshipJSONImpl.java index 4330cb808..72f581618 100644 --- a/twidere/src/main/java/twitter4j/internal/json/RelationshipJSONImpl.java +++ b/twidere/src/main/java/twitter4j/internal/json/RelationshipJSONImpl.java @@ -16,10 +16,6 @@ package twitter4j.internal.json; -import static twitter4j.internal.util.InternalParseUtil.getBoolean; -import static twitter4j.internal.util.InternalParseUtil.getHTMLUnescapedString; -import static twitter4j.internal.util.InternalParseUtil.getLong; - import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -30,234 +26,248 @@ import twitter4j.TwitterException; import twitter4j.conf.Configuration; import twitter4j.http.HttpResponse; +import static twitter4j.internal.util.InternalParseUtil.getBoolean; +import static twitter4j.internal.util.InternalParseUtil.getHTMLUnescapedString; +import static twitter4j.internal.util.InternalParseUtil.getLong; + /** * A data class that has detailed information about a relationship between two * users - * + * * @author Perry Sakkaris - psakkaris at gmail.com * @see GET - * friendships/show | Twitter Developers + * friendships/show | Twitter Developers * @since Twitter4J 2.1.0 */ /* package */class RelationshipJSONImpl extends TwitterResponseImpl implements Relationship { - private static final long serialVersionUID = 2816753598969317818L; - private final long targetUserId; - private final String targetUserScreenName; - private final boolean sourceBlockingTarget; - private final boolean sourceNotificationsEnabled; - private final boolean sourceFollowingTarget; - private final boolean sourceFollowedByTarget; - private final long sourceUserId; - private final String sourceUserScreenName; - private final boolean sourceCanDM; - private final boolean sourceCanMediaTag; - private final boolean sourceMutingTarget; - private final boolean sourceMarkedTargetAsSpam; + private static final long serialVersionUID = 2816753598969317818L; + private final long targetUserId; + private final String targetUserScreenName; + private final boolean sourceBlockingTarget; + private final boolean sourceBlockedByTarget; + private final boolean sourceNotificationsEnabled; + private final boolean sourceFollowingTarget; + private final boolean sourceFollowedByTarget; + private final long sourceUserId; + private final String sourceUserScreenName; + private final boolean sourceCanDM; + private final boolean sourceCanMediaTag; + private final boolean sourceMutingTarget; + private final boolean sourceMarkedTargetAsSpam; - /* package */RelationshipJSONImpl(final HttpResponse res, final Configuration conf) throws TwitterException { - this(res, res.asJSONObject()); - } + /* package */RelationshipJSONImpl(final HttpResponse res, final Configuration conf) throws TwitterException { + this(res, res.asJSONObject()); + } - /* package */RelationshipJSONImpl(final HttpResponse res, final JSONObject json) throws TwitterException { - super(res); - try { - final JSONObject relationship = json.getJSONObject("relationship"); - final JSONObject sourceJson = relationship.getJSONObject("source"); - final JSONObject targetJson = relationship.getJSONObject("target"); - sourceUserId = getLong("id", sourceJson); - targetUserId = getLong("id", targetJson); - sourceUserScreenName = getHTMLUnescapedString("screen_name", sourceJson); - targetUserScreenName = getHTMLUnescapedString("screen_name", targetJson); - sourceBlockingTarget = getBoolean("blocking", sourceJson); - sourceFollowingTarget = getBoolean("following", sourceJson); - sourceFollowedByTarget = getBoolean("followed_by", sourceJson); - sourceNotificationsEnabled = getBoolean("notifications_enabled", sourceJson); - sourceCanDM = getBoolean("can_dm", sourceJson); - sourceCanMediaTag = getBoolean("can_media_tag", sourceJson); - sourceMutingTarget = getBoolean("muting", sourceJson); - sourceMarkedTargetAsSpam = getBoolean("marked_spam", sourceJson); - } catch (final JSONException jsone) { - throw new TwitterException(jsone.getMessage() + ":" + json.toString(), jsone); - } - } + /* package */RelationshipJSONImpl(final HttpResponse res, final JSONObject json) throws TwitterException { + super(res); + try { + final JSONObject relationship = json.getJSONObject("relationship"); + final JSONObject sourceJson = relationship.getJSONObject("source"); + final JSONObject targetJson = relationship.getJSONObject("target"); + sourceUserId = getLong("id", sourceJson); + targetUserId = getLong("id", targetJson); + sourceUserScreenName = getHTMLUnescapedString("screen_name", sourceJson); + targetUserScreenName = getHTMLUnescapedString("screen_name", targetJson); + sourceBlockingTarget = getBoolean("blocking", sourceJson); + sourceBlockedByTarget = getBoolean("blocked_by", sourceJson); + sourceFollowingTarget = getBoolean("following", sourceJson); + sourceFollowedByTarget = getBoolean("followed_by", sourceJson); + sourceNotificationsEnabled = getBoolean("notifications_enabled", sourceJson); + sourceCanDM = getBoolean("can_dm", sourceJson); + sourceCanMediaTag = getBoolean("can_media_tag", sourceJson); + sourceMutingTarget = getBoolean("muting", sourceJson); + sourceMarkedTargetAsSpam = getBoolean("marked_spam", sourceJson); + } catch (final JSONException jsone) { + throw new TwitterException(jsone.getMessage() + ":" + json.toString(), jsone); + } + } - /* package */RelationshipJSONImpl(final JSONObject json) throws TwitterException { - this(null, json); - } + /* package */RelationshipJSONImpl(final JSONObject json) throws TwitterException { + this(null, json); + } - @Override - public boolean canSourceDMTarget() { - return sourceCanDM; - } + @Override + public boolean canSourceDMTarget() { + return sourceCanDM; + } - @Override - public boolean canSourceMediaTagTarget() { - return sourceCanMediaTag; - } + @Override + public boolean canSourceMediaTagTarget() { + return sourceCanMediaTag; + } - @Override - public boolean equals(final Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (!(obj instanceof RelationshipJSONImpl)) return false; - final RelationshipJSONImpl other = (RelationshipJSONImpl) obj; - if (sourceBlockingTarget != other.sourceBlockingTarget) return false; - if (sourceCanDM != other.sourceCanDM) return false; - if (sourceCanMediaTag != other.sourceCanMediaTag) return false; - if (sourceFollowedByTarget != other.sourceFollowedByTarget) return false; - if (sourceFollowingTarget != other.sourceFollowingTarget) return false; - if (sourceMarkedTargetAsSpam != other.sourceMarkedTargetAsSpam) return false; - if (sourceMutingTarget != other.sourceMutingTarget) return false; - if (sourceNotificationsEnabled != other.sourceNotificationsEnabled) return false; - if (sourceUserId != other.sourceUserId) return false; - if (sourceUserScreenName == null) { - if (other.sourceUserScreenName != null) return false; - } else if (!sourceUserScreenName.equals(other.sourceUserScreenName)) return false; - if (targetUserId != other.targetUserId) return false; - if (targetUserScreenName == null) { - if (other.targetUserScreenName != null) return false; - } else if (!targetUserScreenName.equals(other.targetUserScreenName)) return false; - return true; - } + @Override + public boolean equals(final Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof RelationshipJSONImpl)) return false; + final RelationshipJSONImpl other = (RelationshipJSONImpl) obj; + if (sourceBlockingTarget != other.sourceBlockingTarget) return false; + if (sourceCanDM != other.sourceCanDM) return false; + if (sourceCanMediaTag != other.sourceCanMediaTag) return false; + if (sourceFollowedByTarget != other.sourceFollowedByTarget) return false; + if (sourceFollowingTarget != other.sourceFollowingTarget) return false; + if (sourceMarkedTargetAsSpam != other.sourceMarkedTargetAsSpam) return false; + if (sourceMutingTarget != other.sourceMutingTarget) return false; + if (sourceNotificationsEnabled != other.sourceNotificationsEnabled) return false; + if (sourceUserId != other.sourceUserId) return false; + if (sourceUserScreenName == null) { + if (other.sourceUserScreenName != null) return false; + } else if (!sourceUserScreenName.equals(other.sourceUserScreenName)) return false; + if (targetUserId != other.targetUserId) return false; + if (targetUserScreenName == null) { + if (other.targetUserScreenName != null) return false; + } else if (!targetUserScreenName.equals(other.targetUserScreenName)) return false; + return true; + } - /** - * {@inheritDoc} - */ - @Override - public long getSourceUserId() { - return sourceUserId; - } + /** + * {@inheritDoc} + */ + @Override + public long getSourceUserId() { + return sourceUserId; + } - /** - * {@inheritDoc} - */ - @Override - public String getSourceUserScreenName() { - return sourceUserScreenName; - } + /** + * {@inheritDoc} + */ + @Override + public String getSourceUserScreenName() { + return sourceUserScreenName; + } - /** - * {@inheritDoc} - */ - @Override - public long getTargetUserId() { - return targetUserId; - } + /** + * {@inheritDoc} + */ + @Override + public long getTargetUserId() { + return targetUserId; + } - /** - * {@inheritDoc} - */ - @Override - public String getTargetUserScreenName() { - return targetUserScreenName; - } + /** + * {@inheritDoc} + */ + @Override + public String getTargetUserScreenName() { + return targetUserScreenName; + } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (sourceBlockingTarget ? 1231 : 1237); - result = prime * result + (sourceCanDM ? 1231 : 1237); - result = prime * result + (sourceCanMediaTag ? 1231 : 1237); - result = prime * result + (sourceFollowedByTarget ? 1231 : 1237); - result = prime * result + (sourceFollowingTarget ? 1231 : 1237); - result = prime * result + (sourceMarkedTargetAsSpam ? 1231 : 1237); - result = prime * result + (sourceMutingTarget ? 1231 : 1237); - result = prime * result + (sourceNotificationsEnabled ? 1231 : 1237); - result = prime * result + (int) (sourceUserId ^ sourceUserId >>> 32); - result = prime * result + (sourceUserScreenName == null ? 0 : sourceUserScreenName.hashCode()); - result = prime * result + (int) (targetUserId ^ targetUserId >>> 32); - result = prime * result + (targetUserScreenName == null ? 0 : targetUserScreenName.hashCode()); - return result; - } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (sourceBlockingTarget ? 1231 : 1237); + result = prime * result + (sourceCanDM ? 1231 : 1237); + result = prime * result + (sourceCanMediaTag ? 1231 : 1237); + result = prime * result + (sourceFollowedByTarget ? 1231 : 1237); + result = prime * result + (sourceFollowingTarget ? 1231 : 1237); + result = prime * result + (sourceMarkedTargetAsSpam ? 1231 : 1237); + result = prime * result + (sourceMutingTarget ? 1231 : 1237); + result = prime * result + (sourceNotificationsEnabled ? 1231 : 1237); + result = prime * result + (int) (sourceUserId ^ sourceUserId >>> 32); + result = prime * result + (sourceUserScreenName == null ? 0 : sourceUserScreenName.hashCode()); + result = prime * result + (int) (targetUserId ^ targetUserId >>> 32); + result = prime * result + (targetUserScreenName == null ? 0 : targetUserScreenName.hashCode()); + return result; + } - /** - * {@inheritDoc} - */ - @Override - public boolean isSourceBlockingTarget() { - return sourceBlockingTarget; - } + /** + * {@inheritDoc} + */ + @Override + public boolean isSourceBlockingTarget() { + return sourceBlockingTarget; + } - /** - * {@inheritDoc} - */ - @Override - public boolean isSourceFollowedByTarget() { - return sourceFollowedByTarget; - } + /** + * {@inheritDoc} + */ + @Override + public boolean isSourceBlockedByTarget() { + return sourceBlockedByTarget; + } - /** - * {@inheritDoc} - */ - @Override - public boolean isSourceFollowingTarget() { - return sourceFollowingTarget; - } + /** + * {@inheritDoc} + */ + @Override + public boolean isSourceFollowedByTarget() { + return sourceFollowedByTarget; + } - @Override - public boolean isSourceMarkedTargetAsSpam() { - return sourceMarkedTargetAsSpam; - } + /** + * {@inheritDoc} + */ + @Override + public boolean isSourceFollowingTarget() { + return sourceFollowingTarget; + } - @Override - public boolean isSourceMutingTarget() { - return sourceMutingTarget; - } + @Override + public boolean isSourceMarkedTargetAsSpam() { + return sourceMarkedTargetAsSpam; + } - /** - * {@inheritDoc} - */ - @Override - public boolean isSourceNotificationsEnabled() { - return sourceNotificationsEnabled; - } + @Override + public boolean isSourceMutingTarget() { + return sourceMutingTarget; + } - /** - * {@inheritDoc} - */ - @Override - public boolean isTargetFollowedBySource() { - return sourceFollowingTarget; - } + /** + * {@inheritDoc} + */ + @Override + public boolean isSourceNotificationsEnabled() { + return sourceNotificationsEnabled; + } - /** - * {@inheritDoc} - */ - @Override - public boolean isTargetFollowingSource() { - return sourceFollowedByTarget; - } + /** + * {@inheritDoc} + */ + @Override + public boolean isTargetFollowedBySource() { + return sourceFollowingTarget; + } - @Override - public String toString() { - return "RelationshipJSONImpl{targetUserId=" + targetUserId + ", targetUserScreenName=" + targetUserScreenName - + ", sourceBlockingTarget=" + sourceBlockingTarget + ", sourceNotificationsEnabled=" - + sourceNotificationsEnabled + ", sourceFollowingTarget=" + sourceFollowingTarget - + ", sourceFollowedByTarget=" + sourceFollowedByTarget + ", sourceUserId=" + sourceUserId - + ", sourceUserScreenName=" + sourceUserScreenName + ", sourceCanDM=" + sourceCanDM - + ", sourceCanMediaTag=" + sourceCanMediaTag + ", sourceMutingTarget=" + sourceMutingTarget - + ", sourceMarkedTargetAsSpam=" + sourceMarkedTargetAsSpam + "}"; - } + /** + * {@inheritDoc} + */ + @Override + public boolean isTargetFollowingSource() { + return sourceFollowedByTarget; + } - /* package */ - static ResponseList createRelationshipList(final HttpResponse res, final Configuration conf) - throws TwitterException { - try { - final JSONArray list = res.asJSONArray(); - final int size = list.length(); - final ResponseList relationships = new ResponseListImpl(size, res); - for (int i = 0; i < size; i++) { - final JSONObject json = list.getJSONObject(i); - final Relationship relationship = new RelationshipJSONImpl(json); - relationships.add(relationship); - } - return relationships; - } catch (final JSONException jsone) { - throw new TwitterException(jsone); - } catch (final TwitterException te) { - throw te; - } - } + @Override + public String toString() { + return "RelationshipJSONImpl{targetUserId=" + targetUserId + ", targetUserScreenName=" + targetUserScreenName + + ", sourceBlockingTarget=" + sourceBlockingTarget + ", sourceNotificationsEnabled=" + + sourceNotificationsEnabled + ", sourceFollowingTarget=" + sourceFollowingTarget + + ", sourceFollowedByTarget=" + sourceFollowedByTarget + ", sourceUserId=" + sourceUserId + + ", sourceUserScreenName=" + sourceUserScreenName + ", sourceCanDM=" + sourceCanDM + + ", sourceCanMediaTag=" + sourceCanMediaTag + ", sourceMutingTarget=" + sourceMutingTarget + + ", sourceMarkedTargetAsSpam=" + sourceMarkedTargetAsSpam + "}"; + } + + /* package */ + static ResponseList createRelationshipList(final HttpResponse res, final Configuration conf) + throws TwitterException { + try { + final JSONArray list = res.asJSONArray(); + final int size = list.length(); + final ResponseList relationships = new ResponseListImpl(size, res); + for (int i = 0; i < size; i++) { + final JSONObject json = list.getJSONObject(i); + final Relationship relationship = new RelationshipJSONImpl(json); + relationships.add(relationship); + } + return relationships; + } catch (final JSONException jsone) { + throw new TwitterException(jsone); + } catch (final TwitterException te) { + throw te; + } + } } diff --git a/twidere/src/main/res/layout/activity_global_search_box.xml b/twidere/src/main/res/layout/activity_global_search_box.xml new file mode 100644 index 000000000..e73f771cf --- /dev/null +++ b/twidere/src/main/res/layout/activity_global_search_box.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/card_item_activity_summary.xml b/twidere/src/main/res/layout/card_item_activity_summary.xml new file mode 100644 index 000000000..eca140499 --- /dev/null +++ b/twidere/src/main/res/layout/card_item_activity_summary.xml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/list_item_activity_title_summary.xml b/twidere/src/main/res/layout/card_item_activity_summary_compact.xml similarity index 100% rename from twidere/src/main/res/layout/list_item_activity_title_summary.xml rename to twidere/src/main/res/layout/card_item_activity_summary_compact.xml diff --git a/twidere/src/main/res/layout/card_item_message_conversation.xml b/twidere/src/main/res/layout/card_item_message_conversation.xml index ccff01c5b..439aea103 100644 --- a/twidere/src/main/res/layout/card_item_message_conversation.xml +++ b/twidere/src/main/res/layout/card_item_message_conversation.xml @@ -39,23 +39,12 @@ - - + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/card_item_status_compat.xml b/twidere/src/main/res/layout/card_item_status_compat.xml deleted file mode 100644 index 3945f5428..000000000 --- a/twidere/src/main/res/layout/card_item_status_compat.xml +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/twidere/src/main/res/layout/fragment_content_pages.xml b/twidere/src/main/res/layout/fragment_content_pages.xml index 983f25771..be29a4b8a 100644 --- a/twidere/src/main/res/layout/fragment_content_pages.xml +++ b/twidere/src/main/res/layout/fragment_content_pages.xml @@ -17,34 +17,47 @@ ~ You should have received a copy of the GNU General Public License ~ along with this program. If not, see . --> - - + android:layout_height="match_parent"> - - - - - - \ No newline at end of file + android:orientation="vertical"> + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/fragment_messages_conversation.xml b/twidere/src/main/res/layout/fragment_messages_conversation.xml index 889e7f057..6b222c45a 100644 --- a/twidere/src/main/res/layout/fragment_messages_conversation.xml +++ b/twidere/src/main/res/layout/fragment_messages_conversation.xml @@ -71,6 +71,25 @@ android:paddingRight="@dimen/element_spacing_normal" android:scrollbarStyle="outsideInset"/> + + + + + + + + + android:textColor="?android:textColorPrimary" + tools:text="Name"/> + android:textColor="?android:attr/textColorSecondary" + tools:text="\@username"/> @@ -152,15 +153,17 @@ + android:textAppearance="?android:textAppearanceMedium" + android:textColor="?android:textColorPrimary" + android:textIsSelectable="true" + tools:text="@string/sample_status_text"/> diff --git a/twidere/src/main/res/layout/header_status_compact.xml b/twidere/src/main/res/layout/header_status_compact.xml new file mode 100644 index 000000000..1c70ec1ea --- /dev/null +++ b/twidere/src/main/res/layout/header_status_compact.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/header_user_list_details.xml b/twidere/src/main/res/layout/header_user_list_details.xml index 02adf0e91..649ceb6d0 100644 --- a/twidere/src/main/res/layout/header_user_list_details.xml +++ b/twidere/src/main/res/layout/header_user_list_details.xml @@ -61,7 +61,7 @@ android:contentDescription="@string/profile_image" android:foreground="?android:selectableItemBackground" android:scaleType="fitCenter" - android:src="@drawable/ic_profile_image_default"/> + tools:src="@drawable/ic_profile_image_default"/> - - - - - - + android:padding="@dimen/element_spacing_small" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="?android:attr/textColorSecondary" + tools:text="@string/sample_status_text"/> diff --git a/twidere/src/main/res/layout/image_viewer.xml b/twidere/src/main/res/layout/image_viewer.xml new file mode 100644 index 000000000..1be45d7ae --- /dev/null +++ b/twidere/src/main/res/layout/image_viewer.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/list_item_user_list.xml b/twidere/src/main/res/layout/list_item_user_list.xml new file mode 100644 index 000000000..85ffa288c --- /dev/null +++ b/twidere/src/main/res/layout/list_item_user_list.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twidere/src/main/res/layout/spinner_item_account_icon.xml b/twidere/src/main/res/layout/spinner_item_account_icon.xml index 79bc12d7d..f90895654 100644 --- a/twidere/src/main/res/layout/spinner_item_account_icon.xml +++ b/twidere/src/main/res/layout/spinner_item_account_icon.xml @@ -28,8 +28,9 @@ Open Twitter links another %d others + Type name to search + No user found + Members + Subscriber \ No newline at end of file diff --git a/twidere/src/main/res/values/themes.xml b/twidere/src/main/res/values/themes.xml index 0c251616e..0c7eff9c1 100644 --- a/twidere/src/main/res/values/themes.xml +++ b/twidere/src/main/res/values/themes.xml @@ -14,6 +14,13 @@ @android:color/black + + + + + +