Must be a boolean value, either "true" or "false".
13 |
This may also be a reference to a resource (in the form
14 | "@[package:]type:name") or
15 | theme attribute (in the form
16 | "?[package:][type:]name")
17 | containing a value of this type.
18 | */
19 | public static final int adjustViewBounds=0x7f010000;
20 | /**
May be a reference to another resource, in the form "@[+][package:]type:name"
21 | or to a theme attribute in the form "?[package:][type:]name".
22 |
May be a color value, in the form of "#rgb", "#argb",
23 | "#rrggbb", or "#aarrggbb".
24 | */
25 | public static final int emptyDrawable=0x7f010001;
26 | /**
Must be one of the following constant values.
27 |
28 |
29 |
30 |
31 |
Constant
Value
Description
32 |
none
0
33 |
horizontal
1
34 |
vertical
2
35 |
36 | */
37 | public static final int fillDirection=0x7f010002;
38 | }
39 | public static final class drawable {
40 | public static final int empty_newsthumb=0x7f020000;
41 | public static final int ic_launcher=0x7f020001;
42 | public static final int loader=0x7f020002;
43 | public static final int loader_0=0x7f020003;
44 | public static final int loader_1=0x7f020004;
45 | public static final int loader_2=0x7f020005;
46 | public static final int loader_3=0x7f020006;
47 | public static final int loader_4=0x7f020007;
48 | public static final int loader_5=0x7f020008;
49 | public static final int loader_6=0x7f020009;
50 | public static final int loader_7=0x7f02000a;
51 | public static final int test=0x7f02000b;
52 | public static final int test_gif=0x7f02000c;
53 | }
54 | public static final class id {
55 | public static final int horizontal=0x7f040001;
56 | public static final int imageViewEx1=0x7f040003;
57 | public static final int imageViewEx2=0x7f040004;
58 | public static final int imageViewNext1=0x7f040005;
59 | public static final int imageViewNext2=0x7f040006;
60 | public static final int menu_settings=0x7f040007;
61 | public static final int none=0x7f040000;
62 | public static final int vertical=0x7f040002;
63 | }
64 | public static final class layout {
65 | public static final int activity_main=0x7f030000;
66 | }
67 | public static final class menu {
68 | public static final int activity_main=0x7f070000;
69 | }
70 | public static final class string {
71 | public static final int app_name=0x7f050000;
72 | public static final int hello_world=0x7f050001;
73 | public static final int menu_settings=0x7f050002;
74 | }
75 | public static final class style {
76 | /**
77 | Base application theme, dependent on API level. This theme is replaced
78 | by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
79 |
80 |
81 | Theme customizations available in newer API levels can go in
82 | res/values-vXX/styles.xml, while customizations related to
83 | backward-compatibility can go here.
84 |
85 |
86 | Base application theme for API 11+. This theme completely replaces
87 | AppBaseTheme from res/values/styles.xml on API 11+ devices.
88 |
89 | API 11 theme customizations can go here.
90 |
91 | Base application theme for API 14+. This theme completely replaces
92 | AppBaseTheme from BOTH res/values/styles.xml and
93 | res/values-v11/styles.xml on API 14+ devices.
94 |
95 | API 14 theme customizations can go here.
96 | */
97 | public static final int AppBaseTheme=0x7f060000;
98 | /** Application theme.
99 | All customizations that are NOT specific to a particular API-level can go here.
100 | */
101 | public static final int AppTheme=0x7f060001;
102 | }
103 | public static final class styleable {
104 | /** Attributes that can be used with a ImageViewEx.
105 |
This symbol is the offset where the {@link com.ftxgame.ftxcn.R.attr#adjustViewBounds}
123 | attribute's value can be found in the {@link #ImageViewEx} array.
124 |
125 |
126 |
Must be a boolean value, either "true" or "false".
127 |
This may also be a reference to a resource (in the form
128 | "@[package:]type:name") or
129 | theme attribute (in the form
130 | "?[package:][type:]name")
131 | containing a value of this type.
132 | @attr name android:adjustViewBounds
133 | */
134 | public static final int ImageViewEx_adjustViewBounds = 0;
135 | /**
136 |
This symbol is the offset where the {@link com.ftxgame.ftxcn.R.attr#emptyDrawable}
137 | attribute's value can be found in the {@link #ImageViewEx} array.
138 |
139 |
140 |
May be a reference to another resource, in the form "@[+][package:]type:name"
141 | or to a theme attribute in the form "?[package:][type:]name".
142 |
May be a color value, in the form of "#rgb", "#argb",
143 | "#rrggbb", or "#aarrggbb".
144 | @attr name android:emptyDrawable
145 | */
146 | public static final int ImageViewEx_emptyDrawable = 1;
147 | /**
148 |
This symbol is the offset where the {@link com.ftxgame.ftxcn.R.attr#fillDirection}
149 | attribute's value can be found in the {@link #ImageViewEx} array.
150 |
151 |
152 |
Must be one of the following constant values.
153 |
154 |
155 |
156 |
157 |
Constant
Value
Description
158 |
none
0
159 |
horizontal
1
160 |
vertical
2
161 |
162 | @attr name android:fillDirection
163 | */
164 | public static final int ImageViewEx_fillDirection = 2;
165 | };
166 | }
167 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/libs/disklrucache-2.0.2.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/libs/disklrucache-2.0.2.jar
--------------------------------------------------------------------------------
/libs/okhttp-1.1.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/libs/okhttp-1.1.1.jar
--------------------------------------------------------------------------------
/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-17
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/empty_newsthumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/empty_newsthumb.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_0.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_1.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_2.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_3.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_4.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_5.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_6.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/loader_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-hdpi/loader_7.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-mdpi/test.jpg
--------------------------------------------------------------------------------
/res/drawable-mdpi/test_gif.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-mdpi/test_gif.gif
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangwenmai/GifImageViewEx/de37b1947a90917e01e5cfa465838a7f5b48630a/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable/loader.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
18 |
19 |
25 |
26 |
33 |
34 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/res/menu/activity_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values/attribs.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | com.ftxgame.ftxcn
5 | Hello world!
6 | Settings
7 |
8 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/src/android/support/util/Base64.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2010 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.support.util;
18 |
19 | import android.util.Base64OutputStream;
20 |
21 | import java.io.UnsupportedEncodingException;
22 |
23 | /**
24 | * Utilities for encoding and decoding the Base64 representation of binary data. See RFCs 2045 and 3548.
27 | *
28 | * Implementation copied from the Froyo implementation of {@link Base64}
29 | */
30 | public final class Base64 {
31 | /**
32 | * Default values for encoder/decoder flags.
33 | */
34 | public static final int DEFAULT = 0;
35 |
36 | /**
37 | * Encoder flag bit to omit the padding '=' characters at the end of the output (if any).
38 | */
39 | public static final int NO_PADDING = 1;
40 |
41 | /**
42 | * Encoder flag bit to omit all line terminators (i.e., the output will be on one long line).
43 | */
44 | public static final int NO_WRAP = 2;
45 |
46 | /**
47 | * Encoder flag bit to indicate lines should be terminated with a CRLF pair instead of just an
48 | * LF. Has no effect if {@code NO_WRAP} is specified as well.
49 | */
50 | public static final int CRLF = 4;
51 |
52 | /**
53 | * Encoder/decoder flag bit to indicate using the "URL and filename safe" variant of Base64 (see
54 | * RFC 3548 section 4) where {@code -} and {@code _} are used in place of {@code +} and
55 | * {@code /}.
56 | */
57 | public static final int URL_SAFE = 8;
58 |
59 | /**
60 | * Flag to pass to {@link Base64OutputStream} to indicate that it should not close the output
61 | * stream it is wrapping when it itself is closed.
62 | */
63 | public static final int NO_CLOSE = 16;
64 |
65 | // --------------------------------------------------------
66 | // shared code
67 | // --------------------------------------------------------
68 |
69 | /* package */static abstract class Coder {
70 | public byte[] output;
71 | public int op;
72 |
73 | /**
74 | * Encode/decode another block of input data. this.output is provided by the caller, and
75 | * must be big enough to hold all the coded data. On exit, this.opwill be set to the length
76 | * of the coded data.
77 | *
78 | * @param finish true if this is the final call to process for this object. Will finalize
79 | * the coder state and include any final bytes in the output.
80 | * @return true if the input so far is good; false if some error has been detected in the
81 | * input stream..
82 | */
83 | public abstract boolean process(byte[] input, int offset, int len, boolean finish);
84 |
85 | /**
86 | * @return the maximum number of bytes a call to process() could produce for the given
87 | * number of input bytes. This may be an overestimate.
88 | */
89 | public abstract int maxOutputSize(int len);
90 | }
91 |
92 | // --------------------------------------------------------
93 | // decoding
94 | // --------------------------------------------------------
95 |
96 | /**
97 | * Decode the Base64-encoded data in input and return the data in a new byte array.
98 | *
99 | * The padding '=' characters at the end are considered optional, but if any are present, there
100 | * must be the correct number of them.
101 | *
102 | * @param str the input String to decode, which is converted to bytes using the default charset
103 | * @param flags controls certain features of the decoded output. Pass {@code DEFAULT} to decode
104 | * standard Base64.
105 | * @throws IllegalArgumentException if the input contains incorrect padding
106 | */
107 | public static byte[] decode(String str, int flags) {
108 | return decode(str.getBytes(), flags);
109 | }
110 |
111 | /**
112 | * Decode the Base64-encoded data in input and return the data in a new byte array.
113 | *
114 | * The padding '=' characters at the end are considered optional, but if any are present, there
115 | * must be the correct number of them.
116 | *
117 | * @param input the input array to decode
118 | * @param flags controls certain features of the decoded output. Pass {@code DEFAULT} to decode
119 | * standard Base64.
120 | * @throws IllegalArgumentException if the input contains incorrect padding
121 | */
122 | public static byte[] decode(byte[] input, int flags) {
123 | return decode(input, 0, input.length, flags);
124 | }
125 |
126 | /**
127 | * Decode the Base64-encoded data in input and return the data in a new byte array.
128 | *
129 | * The padding '=' characters at the end are considered optional, but if any are present, there
130 | * must be the correct number of them.
131 | *
132 | * @param input the data to decode
133 | * @param offset the position within the input array at which to start
134 | * @param len the number of bytes of input to decode
135 | * @param flags controls certain features of the decoded output. Pass {@code DEFAULT} to decode
136 | * standard Base64.
137 | * @throws IllegalArgumentException if the input contains incorrect padding
138 | */
139 | public static byte[] decode(byte[] input, int offset, int len, int flags) {
140 | // Allocate space for the most data the input could represent.
141 | // (It could contain less if it contains whitespace, etc.)
142 | Decoder decoder = new Decoder(flags, new byte[len * 3 / 4]);
143 |
144 | if (!decoder.process(input, offset, len, true)) {
145 | throw new IllegalArgumentException("bad base-64");
146 | }
147 |
148 | // Maybe we got lucky and allocated exactly enough output space.
149 | if (decoder.op == decoder.output.length) {
150 | return decoder.output;
151 | }
152 |
153 | // Need to shorten the array, so allocate a new one of the
154 | // right size and copy.
155 | byte[] temp = new byte[decoder.op];
156 | System.arraycopy(decoder.output, 0, temp, 0, decoder.op);
157 | return temp;
158 | }
159 |
160 | /* package */static class Decoder extends Coder {
161 | /**
162 | * Lookup table for turning bytes into their position in the Base64 alphabet.
163 | */
164 | private static final int DECODE[] = {
165 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
166 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
167 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
168 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
169 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
170 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
171 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
172 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
173 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
174 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
175 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
176 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
177 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
178 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
179 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
180 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
181 | };
182 |
183 | /**
184 | * Decode lookup table for the "web safe" variant (RFC 3548 sec. 4) where - and _ replace +
185 | * and /.
186 | */
187 | private static final int DECODE_WEBSAFE[] = {
188 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
189 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
190 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
191 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
192 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
193 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
194 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
195 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
196 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
197 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
198 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
199 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
200 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
201 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
202 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
203 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
204 | };
205 |
206 | /** Non-data values in the DECODE arrays. */
207 | private static final int SKIP = -1;
208 | private static final int EQUALS = -2;
209 |
210 | /**
211 | * States 0-3 are reading through the next input tuple. State 4 is having read one '=' and
212 | * expecting exactly one more. State 5 is expecting no more data or padding characters in
213 | * the input. State 6 is the error state; an error has been detected in the input and no
214 | * future input can "fix" it.
215 | */
216 | private int state; // state number (0 to 6)
217 | private int value;
218 |
219 | final private int[] alphabet;
220 |
221 | public Decoder(int flags, byte[] output) {
222 | this.output = output;
223 |
224 | alphabet = ((flags & URL_SAFE) == 0) ? DECODE : DECODE_WEBSAFE;
225 | state = 0;
226 | value = 0;
227 | }
228 |
229 | /**
230 | * @return an overestimate for the number of bytes {@code len} bytes could decode to.
231 | */
232 | @Override
233 | public int maxOutputSize(int len) {
234 | return len * 3 / 4 + 10;
235 | }
236 |
237 | /**
238 | * Decode another block of input data.
239 | *
240 | * @return true if the state machine is still healthy. false if bad base-64 data has been
241 | * detected in the input stream.
242 | */
243 | @Override
244 | public boolean process(byte[] input, int offset, int len, boolean finish) {
245 | if (this.state == 6) {
246 | return false;
247 | }
248 |
249 | int p = offset;
250 | len += offset;
251 |
252 | // Using local variables makes the decoder about 12%
253 | // faster than if we manipulate the member variables in
254 | // the loop. (Even alphabet makes a measurable
255 | // difference, which is somewhat surprising to me since
256 | // the member variable is final.)
257 | int state = this.state;
258 | int value = this.value;
259 | int op = 0;
260 | final byte[] output = this.output;
261 | final int[] alphabet = this.alphabet;
262 |
263 | while (p < len) {
264 | // Try the fast path: we're starting a new tuple and the
265 | // next four bytes of the input stream are all data
266 | // bytes. This corresponds to going through states
267 | // 0-1-2-3-0. We expect to use this method for most of
268 | // the data.
269 | //
270 | // If any of the next four bytes of input are non-data
271 | // (whitespace, etc.), value will end up negative. (All
272 | // the non-data values in decode are small negative
273 | // numbers, so shifting any of them up and or'ing them
274 | // together will result in a value with its top bit set.)
275 | //
276 | // You can remove this whole block and the output should
277 | // be the same, just slower.
278 | if (state == 0) {
279 | while (p + 4 <= len &&
280 | (value = ((alphabet[input[p] & 0xff] << 18) |
281 | (alphabet[input[p + 1] & 0xff] << 12) |
282 | (alphabet[input[p + 2] & 0xff] << 6) |
283 | (alphabet[input[p + 3] & 0xff]))) >= 0) {
284 | output[op + 2] = (byte) value;
285 | output[op + 1] = (byte) (value >> 8);
286 | output[op] = (byte) (value >> 16);
287 | op += 3;
288 | p += 4;
289 | }
290 | if (p >= len) {
291 | break;
292 | }
293 | }
294 |
295 | // The fast path isn't available -- either we've read a
296 | // partial tuple, or the next four input bytes aren't all
297 | // data, or whatever. Fall back to the slower state
298 | // machine implementation.
299 |
300 | int d = alphabet[input[p++] & 0xff];
301 |
302 | switch (state) {
303 | case 0:
304 | if (d >= 0) {
305 | value = d;
306 | ++state;
307 | } else if (d != SKIP) {
308 | this.state = 6;
309 | return false;
310 | }
311 | break;
312 |
313 | case 1:
314 | if (d >= 0) {
315 | value = (value << 6) | d;
316 | ++state;
317 | } else if (d != SKIP) {
318 | this.state = 6;
319 | return false;
320 | }
321 | break;
322 |
323 | case 2:
324 | if (d >= 0) {
325 | value = (value << 6) | d;
326 | ++state;
327 | } else if (d == EQUALS) {
328 | // Emit the last (partial) output tuple;
329 | // expect exactly one more padding character.
330 | output[op++] = (byte) (value >> 4);
331 | state = 4;
332 | } else if (d != SKIP) {
333 | this.state = 6;
334 | return false;
335 | }
336 | break;
337 |
338 | case 3:
339 | if (d >= 0) {
340 | // Emit the output triple and return to state 0.
341 | value = (value << 6) | d;
342 | output[op + 2] = (byte) value;
343 | output[op + 1] = (byte) (value >> 8);
344 | output[op] = (byte) (value >> 16);
345 | op += 3;
346 | state = 0;
347 | } else if (d == EQUALS) {
348 | // Emit the last (partial) output tuple;
349 | // expect no further data or padding characters.
350 | output[op + 1] = (byte) (value >> 2);
351 | output[op] = (byte) (value >> 10);
352 | op += 2;
353 | state = 5;
354 | } else if (d != SKIP) {
355 | this.state = 6;
356 | return false;
357 | }
358 | break;
359 |
360 | case 4:
361 | if (d == EQUALS) {
362 | ++state;
363 | } else if (d != SKIP) {
364 | this.state = 6;
365 | return false;
366 | }
367 | break;
368 |
369 | case 5:
370 | if (d != SKIP) {
371 | this.state = 6;
372 | return false;
373 | }
374 | break;
375 | }
376 | }
377 |
378 | if (!finish) {
379 | // We're out of input, but a future call could provide
380 | // more.
381 | this.state = state;
382 | this.value = value;
383 | this.op = op;
384 | return true;
385 | }
386 |
387 | // Done reading input. Now figure out where we are left in
388 | // the state machine and finish up.
389 |
390 | switch (state) {
391 | case 0:
392 | // Output length is a multiple of three. Fine.
393 | break;
394 | case 1:
395 | // Read one extra input byte, which isn't enough to
396 | // make another output byte. Illegal.
397 | this.state = 6;
398 | return false;
399 | case 2:
400 | // Read two extra input bytes, enough to emit 1 more
401 | // output byte. Fine.
402 | output[op++] = (byte) (value >> 4);
403 | break;
404 | case 3:
405 | // Read three extra input bytes, enough to emit 2 more
406 | // output bytes. Fine.
407 | output[op++] = (byte) (value >> 10);
408 | output[op++] = (byte) (value >> 2);
409 | break;
410 | case 4:
411 | // Read one padding '=' when we expected 2. Illegal.
412 | this.state = 6;
413 | return false;
414 | case 5:
415 | // Read all the padding '='s we expected and no more.
416 | // Fine.
417 | break;
418 | }
419 |
420 | this.state = state;
421 | this.op = op;
422 | return true;
423 | }
424 | }
425 |
426 | // --------------------------------------------------------
427 | // encoding
428 | // --------------------------------------------------------
429 |
430 | /**
431 | * Base64-encode the given data and return a newly allocated String with the result.
432 | *
433 | * @param input the data to encode
434 | * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results
435 | * in output that adheres to RFC 2045.
436 | */
437 | public static String encodeToString(byte[] input, int flags) {
438 | try {
439 | return new String(encode(input, flags), "US-ASCII");
440 | } catch (UnsupportedEncodingException e) {
441 | // US-ASCII is guaranteed to be available.
442 | throw new AssertionError(e);
443 | }
444 | }
445 |
446 | /**
447 | * Base64-encode the given data and return a newly allocated String with the result.
448 | *
449 | * @param input the data to encode
450 | * @param offset the position within the input array at which to start
451 | * @param len the number of bytes of input to encode
452 | * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results
453 | * in output that adheres to RFC 2045.
454 | */
455 | public static String encodeToString(byte[] input, int offset, int len, int flags) {
456 | try {
457 | return new String(encode(input, offset, len, flags), "US-ASCII");
458 | } catch (UnsupportedEncodingException e) {
459 | // US-ASCII is guaranteed to be available.
460 | throw new AssertionError(e);
461 | }
462 | }
463 |
464 | /**
465 | * Base64-encode the given data and return a newly allocated byte[] with the result.
466 | *
467 | * @param input the data to encode
468 | * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results
469 | * in output that adheres to RFC 2045.
470 | */
471 | public static byte[] encode(byte[] input, int flags) {
472 | return encode(input, 0, input.length, flags);
473 | }
474 |
475 | /**
476 | * Base64-encode the given data and return a newly allocated byte[] with the result.
477 | *
478 | * @param input the data to encode
479 | * @param offset the position within the input array at which to start
480 | * @param len the number of bytes of input to encode
481 | * @param flags controls certain features of the encoded output. Passing {@code DEFAULT} results
482 | * in output that adheres to RFC 2045.
483 | */
484 | public static byte[] encode(byte[] input, int offset, int len, int flags) {
485 | Encoder encoder = new Encoder(flags, null);
486 |
487 | // Compute the exact length of the array we will produce.
488 | int output_len = len / 3 * 4;
489 |
490 | // Account for the tail of the data and the padding bytes, if any.
491 | if (encoder.do_padding) {
492 | if (len % 3 > 0) {
493 | output_len += 4;
494 | }
495 | } else {
496 | switch (len % 3) {
497 | case 0:
498 | break;
499 | case 1:
500 | output_len += 2;
501 | break;
502 | case 2:
503 | output_len += 3;
504 | break;
505 | }
506 | }
507 |
508 | // Account for the newlines, if any.
509 | if (encoder.do_newline && len > 0) {
510 | output_len += (((len - 1) / (3 * Encoder.LINE_GROUPS)) + 1) *
511 | (encoder.do_cr ? 2 : 1);
512 | }
513 |
514 | encoder.output = new byte[output_len];
515 | encoder.process(input, offset, len, true);
516 |
517 | assert encoder.op == output_len;
518 |
519 | return encoder.output;
520 | }
521 |
522 | /* package */static class Encoder extends Coder {
523 | /**
524 | * Emit a new line every this many output tuples. Corresponds to a 76-character line length
525 | * (the maximum allowable according to RFC
526 | * 2045).
527 | */
528 | public static final int LINE_GROUPS = 19;
529 |
530 | /**
531 | * Lookup table for turning Base64 alphabet positions (6 bits) into output bytes.
532 | */
533 | private static final byte ENCODE[] = {
534 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
535 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
536 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
537 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/',
538 | };
539 |
540 | /**
541 | * Lookup table for turning Base64 alphabet positions (6 bits) into output bytes.
542 | */
543 | private static final byte ENCODE_WEBSAFE[] = {
544 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
545 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
546 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
547 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_',
548 | };
549 |
550 | final private byte[] tail;
551 | /* package */int tailLen;
552 | private int count;
553 |
554 | final public boolean do_padding;
555 | final public boolean do_newline;
556 | final public boolean do_cr;
557 | final private byte[] alphabet;
558 |
559 | public Encoder(int flags, byte[] output) {
560 | this.output = output;
561 |
562 | do_padding = (flags & NO_PADDING) == 0;
563 | do_newline = (flags & NO_WRAP) == 0;
564 | do_cr = (flags & CRLF) != 0;
565 | alphabet = ((flags & URL_SAFE) == 0) ? ENCODE : ENCODE_WEBSAFE;
566 |
567 | tail = new byte[2];
568 | tailLen = 0;
569 |
570 | count = do_newline ? LINE_GROUPS : -1;
571 | }
572 |
573 | /**
574 | * @return an overestimate for the number of bytes {@code len} bytes could encode to.
575 | */
576 | @Override
577 | public int maxOutputSize(int len) {
578 | return len * 8 / 5 + 10;
579 | }
580 |
581 | @Override
582 | public boolean process(byte[] input, int offset, int len, boolean finish) {
583 | // Using local variables makes the encoder about 9% faster.
584 | final byte[] alphabet = this.alphabet;
585 | final byte[] output = this.output;
586 | int op = 0;
587 | int count = this.count;
588 |
589 | int p = offset;
590 | len += offset;
591 | int v = -1;
592 |
593 | // First we need to concatenate the tail of the previous call
594 | // with any input bytes available now and see if we can empty
595 | // the tail.
596 |
597 | switch (tailLen) {
598 | case 0:
599 | // There was no tail.
600 | break;
601 |
602 | case 1:
603 | if (p + 2 <= len) {
604 | // A 1-byte tail with at least 2 bytes of
605 | // input available now.
606 | v = ((tail[0] & 0xff) << 16) |
607 | ((input[p++] & 0xff) << 8) |
608 | (input[p++] & 0xff);
609 | tailLen = 0;
610 | }
611 | break;
612 |
613 | case 2:
614 | if (p + 1 <= len) {
615 | // A 2-byte tail with at least 1 byte of input.
616 | v = ((tail[0] & 0xff) << 16) |
617 | ((tail[1] & 0xff) << 8) |
618 | (input[p++] & 0xff);
619 | tailLen = 0;
620 | }
621 | break;
622 | }
623 |
624 | if (v != -1) {
625 | output[op++] = alphabet[(v >> 18) & 0x3f];
626 | output[op++] = alphabet[(v >> 12) & 0x3f];
627 | output[op++] = alphabet[(v >> 6) & 0x3f];
628 | output[op++] = alphabet[v & 0x3f];
629 | if (--count == 0) {
630 | if (do_cr) {
631 | output[op++] = '\r';
632 | }
633 | output[op++] = '\n';
634 | count = LINE_GROUPS;
635 | }
636 | }
637 |
638 | // At this point either there is no tail, or there are fewer
639 | // than 3 bytes of input available.
640 |
641 | // The main loop, turning 3 input bytes into 4 output bytes on
642 | // each iteration.
643 | while (p + 3 <= len) {
644 | v = ((input[p] & 0xff) << 16) |
645 | ((input[p + 1] & 0xff) << 8) |
646 | (input[p + 2] & 0xff);
647 | output[op] = alphabet[(v >> 18) & 0x3f];
648 | output[op + 1] = alphabet[(v >> 12) & 0x3f];
649 | output[op + 2] = alphabet[(v >> 6) & 0x3f];
650 | output[op + 3] = alphabet[v & 0x3f];
651 | p += 3;
652 | op += 4;
653 | if (--count == 0) {
654 | if (do_cr) {
655 | output[op++] = '\r';
656 | }
657 | output[op++] = '\n';
658 | count = LINE_GROUPS;
659 | }
660 | }
661 |
662 | if (finish) {
663 | // Finish up the tail of the input. Note that we need to
664 | // consume any bytes in tail before any bytes
665 | // remaining in input; there should be at most two bytes
666 | // total.
667 |
668 | if (p - tailLen == len - 1) {
669 | int t = 0;
670 | v = ((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 4;
671 | tailLen -= t;
672 | output[op++] = alphabet[(v >> 6) & 0x3f];
673 | output[op++] = alphabet[v & 0x3f];
674 | if (do_padding) {
675 | output[op++] = '=';
676 | output[op++] = '=';
677 | }
678 | if (do_newline) {
679 | if (do_cr) {
680 | output[op++] = '\r';
681 | }
682 | output[op++] = '\n';
683 | }
684 | } else if (p - tailLen == len - 2) {
685 | int t = 0;
686 | v = (((tailLen > 1 ? tail[t++] : input[p++]) & 0xff) << 10) |
687 | (((tailLen > 0 ? tail[t++] : input[p++]) & 0xff) << 2);
688 | tailLen -= t;
689 | output[op++] = alphabet[(v >> 12) & 0x3f];
690 | output[op++] = alphabet[(v >> 6) & 0x3f];
691 | output[op++] = alphabet[v & 0x3f];
692 | if (do_padding) {
693 | output[op++] = '=';
694 | }
695 | if (do_newline) {
696 | if (do_cr) {
697 | output[op++] = '\r';
698 | }
699 | output[op++] = '\n';
700 | }
701 | } else if (do_newline && op > 0 && count != LINE_GROUPS) {
702 | if (do_cr) {
703 | output[op++] = '\r';
704 | }
705 | output[op++] = '\n';
706 | }
707 |
708 | assert tailLen == 0;
709 | assert p == len;
710 | } else {
711 | // Save the leftovers in tail to be consumed on the next
712 | // call to encodeInternal.
713 |
714 | if (p == len - 1) {
715 | tail[tailLen++] = input[p];
716 | } else if (p == len - 2) {
717 | tail[tailLen++] = input[p];
718 | tail[tailLen++] = input[p + 1];
719 | }
720 | }
721 |
722 | this.op = op;
723 | this.count = count;
724 |
725 | return true;
726 | }
727 | }
728 |
729 | private Base64() {
730 | } // don't instantiate
731 | }
732 |
--------------------------------------------------------------------------------
/src/android/support/util/LruCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package android.support.util;
18 |
19 | import java.util.LinkedHashMap;
20 | import java.util.Locale;
21 | import java.util.Map;
22 |
23 | /**
24 | * Static library version of {@link android.util.LruCache}. Used to write apps
25 | * that run on API levels prior to 12. When running on API level 12 or above,
26 | * this implementation is still used; it does not try to switch to the
27 | * framework's implementation. See the framework SDK documentation for a class
28 | * overview.
29 | */
30 | public class LruCache {
31 | private final LinkedHashMap map;
32 |
33 | /** Size of this cache in units. Not necessarily the number of elements. */
34 | private int size;
35 | private int maxSize;
36 |
37 | private int putCount;
38 | private int createCount;
39 | private int evictionCount;
40 | private int hitCount;
41 | private int missCount;
42 |
43 | /**
44 | * @param maxSize for caches that do not override {@link #sizeOf}, this is
45 | * the maximum number of entries in the cache. For all other caches,
46 | * this is the maximum sum of the sizes of the entries in this cache.
47 | */
48 | public LruCache(int maxSize) {
49 | if (maxSize <= 0) {
50 | throw new IllegalArgumentException("maxSize <= 0");
51 | }
52 | this.maxSize = maxSize;
53 | this.map = new LinkedHashMap(0, 0.75f, true);
54 | }
55 |
56 | /**
57 | * Returns the value for {@code key} if it exists in the cache or can be
58 | * created by {@code #create}. If a value was returned, it is moved to the
59 | * head of the queue. This returns null if a value is not cached and cannot
60 | * be created.
61 | */
62 | public final V get(K key) {
63 | if (key == null) {
64 | throw new NullPointerException("key == null");
65 | }
66 |
67 | V mapValue;
68 | synchronized (this) {
69 | mapValue = map.get(key);
70 | if (mapValue != null) {
71 | hitCount++;
72 | return mapValue;
73 | }
74 | missCount++;
75 | }
76 |
77 | /*
78 | * Attempt to create a value. This may take a long time, and the map
79 | * may be different when create() returns. If a conflicting value was
80 | * added to the map while create() was working, we leave that value in
81 | * the map and release the created value.
82 | */
83 |
84 | V createdValue = create(key);
85 | if (createdValue == null) {
86 | return null;
87 | }
88 |
89 | synchronized (this) {
90 | createCount++;
91 | mapValue = map.put(key, createdValue);
92 |
93 | if (mapValue != null) {
94 | // There was a conflict so undo that last put
95 | map.put(key, mapValue);
96 | } else {
97 | size += safeSizeOf(key, createdValue);
98 | }
99 | }
100 |
101 | if (mapValue != null) {
102 | entryRemoved(false, key, createdValue, mapValue);
103 | return mapValue;
104 | } else {
105 | trimToSize(maxSize);
106 | return createdValue;
107 | }
108 | }
109 |
110 | /**
111 | * Caches {@code value} for {@code key}. The value is moved to the head of
112 | * the queue.
113 | *
114 | * @return the previous value mapped by {@code key}.
115 | */
116 | public final V put(K key, V value) {
117 | if (key == null || value == null) {
118 | throw new NullPointerException("key == null || value == null");
119 | }
120 |
121 | V previous;
122 | synchronized (this) {
123 | putCount++;
124 | size += safeSizeOf(key, value);
125 | previous = map.put(key, value);
126 | if (previous != null) {
127 | size -= safeSizeOf(key, previous);
128 | }
129 | }
130 |
131 | if (previous != null) {
132 | entryRemoved(false, key, previous, value);
133 | }
134 |
135 | trimToSize(maxSize);
136 | return previous;
137 | }
138 |
139 | /**
140 | * Remove the eldest entries until the total of remaining entries is at or
141 | * below the requested size.
142 | *
143 | * @param maxSize the maximum size of the cache before returning. May be -1
144 | * to evict even 0-sized elements.
145 | */
146 | public void trimToSize(int maxSize) {
147 | while (true) {
148 | K key;
149 | V value;
150 | synchronized (this) {
151 | if (size < 0 || (map.isEmpty() && size != 0)) {
152 | throw new IllegalStateException(getClass().getName()
153 | + ".sizeOf() is reporting inconsistent results!");
154 | }
155 |
156 | if (size <= maxSize || map.isEmpty()) {
157 | break;
158 | }
159 |
160 | Map.Entry toEvict = map.entrySet().iterator().next();
161 | key = toEvict.getKey();
162 | value = toEvict.getValue();
163 | map.remove(key);
164 | size -= safeSizeOf(key, value);
165 | evictionCount++;
166 | }
167 |
168 | entryRemoved(true, key, value, null);
169 | }
170 | }
171 |
172 | /**
173 | * Removes the entry for {@code key} if it exists.
174 | *
175 | * @return the previous value mapped by {@code key}.
176 | */
177 | public final V remove(K key) {
178 | if (key == null) {
179 | throw new NullPointerException("key == null");
180 | }
181 |
182 | V previous;
183 | synchronized (this) {
184 | previous = map.remove(key);
185 | if (previous != null) {
186 | size -= safeSizeOf(key, previous);
187 | }
188 | }
189 |
190 | if (previous != null) {
191 | entryRemoved(false, key, previous, null);
192 | }
193 |
194 | return previous;
195 | }
196 |
197 | /**
198 | * Called for entries that have been evicted or removed. This method is
199 | * invoked when a value is evicted to make space, removed by a call to
200 | * {@link #remove}, or replaced by a call to {@link #put}. The default
201 | * implementation does nothing.
202 | *
203 | *
The method is called without synchronization: other threads may
204 | * access the cache while this method is executing.
205 | *
206 | * @param evicted true if the entry is being removed to make space, false
207 | * if the removal was caused by a {@link #put} or {@link #remove}.
208 | * @param newValue the new value for {@code key}, if it exists. If non-null,
209 | * this removal was caused by a {@link #put}. Otherwise it was caused by
210 | * an eviction or a {@link #remove}.
211 | */
212 | protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
213 |
214 | /**
215 | * Called after a cache miss to compute a value for the corresponding key.
216 | * Returns the computed value or null if no value can be computed. The
217 | * default implementation returns null.
218 | *
219 | *
The method is called without synchronization: other threads may
220 | * access the cache while this method is executing.
221 | *
222 | *
If a value for {@code key} exists in the cache when this method
223 | * returns, the created value will be released with {@link #entryRemoved}
224 | * and discarded. This can occur when multiple threads request the same key
225 | * at the same time (causing multiple values to be created), or when one
226 | * thread calls {@link #put} while another is creating a value for the same
227 | * key.
228 | */
229 | protected V create(K key) {
230 | return null;
231 | }
232 |
233 | private int safeSizeOf(K key, V value) {
234 | int result = sizeOf(key, value);
235 | if (result < 0) {
236 | throw new IllegalStateException("Negative size: " + key + "=" + value);
237 | }
238 | return result;
239 | }
240 |
241 | /**
242 | * Returns the size of the entry for {@code key} and {@code value} in
243 | * user-defined units. The default implementation returns 1 so that size
244 | * is the number of entries and max size is the maximum number of entries.
245 | *
246 | *
An entry's size must not change while it is in the cache.
247 | */
248 | protected int sizeOf(K key, V value) {
249 | return 1;
250 | }
251 |
252 | /**
253 | * Clear the cache, calling {@link #entryRemoved} on each removed entry.
254 | */
255 | public final void evictAll() {
256 | trimToSize(-1); // -1 will evict 0-sized elements
257 | }
258 |
259 | /**
260 | * For caches that do not override {@link #sizeOf}, this returns the number
261 | * of entries in the cache. For all other caches, this returns the sum of
262 | * the sizes of the entries in this cache.
263 | */
264 | public synchronized final int size() {
265 | return size;
266 | }
267 |
268 | /**
269 | * For caches that do not override {@link #sizeOf}, this returns the maximum
270 | * number of entries in the cache. For all other caches, this returns the
271 | * maximum sum of the sizes of the entries in this cache.
272 | */
273 | public synchronized final int maxSize() {
274 | return maxSize;
275 | }
276 |
277 | /**
278 | * Returns the number of times {@link #get} returned a value.
279 | */
280 | public synchronized final int hitCount() {
281 | return hitCount;
282 | }
283 |
284 | /**
285 | * Returns the number of times {@link #get} returned null or required a new
286 | * value to be created.
287 | */
288 | public synchronized final int missCount() {
289 | return missCount;
290 | }
291 |
292 | /**
293 | * Returns the number of times {@link #create(Object)} returned a value.
294 | */
295 | public synchronized final int createCount() {
296 | return createCount;
297 | }
298 |
299 | /**
300 | * Returns the number of times {@link #put} was called.
301 | */
302 | public synchronized final int putCount() {
303 | return putCount;
304 | }
305 |
306 | /**
307 | * Returns the number of values that have been evicted.
308 | */
309 | public synchronized final int evictionCount() {
310 | return evictionCount;
311 | }
312 |
313 | /**
314 | * Returns a copy of the current contents of the cache, ordered from least
315 | * recently accessed to most recently accessed.
316 | */
317 | public synchronized final Map snapshot() {
318 | return new LinkedHashMap(map);
319 | }
320 |
321 | @Override public synchronized final String toString() {
322 | int accesses = hitCount + missCount;
323 | int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
324 | return String.format(Locale.US, "LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
325 | maxSize, hitCount, missCount, hitPercent);
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/src/com/foxykeep/datadroid/exception/ConnectionException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 2011 Foxykeep (http://datadroid.foxykeep.com)
3 | *
4 | * Licensed under the Beerware License :
5 | * As long as you retain this notice you can do whatever you want with this stuff. If we meet some
6 | * day, and you think this stuff is worth it, you can buy me a beer in return
7 | */
8 |
9 | package com.foxykeep.datadroid.exception;
10 |
11 | /**
12 | * Thrown to indicate that a connection error occurred.
13 | *
14 | * @author Foxykeep
15 | */
16 | public final class ConnectionException extends Exception {
17 |
18 | private static final long serialVersionUID = 4658308128254827562L;
19 |
20 | private String mRedirectionUrl;
21 | private int mStatusCode = -1;
22 |
23 | /**
24 | * Constructs a new {@link ConnectionException} that includes the current stack trace.
25 | */
26 | public ConnectionException() {
27 | super();
28 | }
29 |
30 | /**
31 | * Constructs a new {@link ConnectionException} that includes the current stack trace, the
32 | * specified detail message and the specified cause.
33 | *
34 | * @param detailMessage The detail message for this exception.
35 | * @param throwable The cause of this exception.
36 | */
37 | public ConnectionException(final String detailMessage, final Throwable throwable) {
38 | super(detailMessage, throwable);
39 | }
40 |
41 | /**
42 | * Constructs a new {@link ConnectionException} that includes the current stack trace and the
43 | * specified detail message.
44 | *
45 | * @param detailMessage The detail message for this exception.
46 | */
47 | public ConnectionException(final String detailMessage) {
48 | super(detailMessage);
49 | }
50 |
51 | /**
52 | * Constructs a new {@link ConnectionException} that includes the current stack trace and the
53 | * specified detail message.
54 | *
55 | * @param detailMessage The detail message for this exception.
56 | * @param redirectionUrl The redirection URL.
57 | */
58 | public ConnectionException(final String detailMessage, final String redirectionUrl) {
59 | super(detailMessage);
60 | mRedirectionUrl = redirectionUrl;
61 | mStatusCode = 301;
62 | }
63 |
64 | /**
65 | * Constructs a new {@link ConnectionException} that includes the current stack trace and the
66 | * specified detail message and the error status code
67 | *
68 | * @param detailMessage The detail message for this exception.
69 | * @param statusCode The HTTP status code
70 | */
71 | public ConnectionException(final String detailMessage, final int statusCode) {
72 | super(detailMessage);
73 | mStatusCode = statusCode;
74 | }
75 |
76 | /**
77 | * Constructs a new {@link ConnectionException} that includes the current stack trace and the
78 | * specified cause.
79 | *
80 | * @param throwable The cause of this exception.
81 | */
82 | public ConnectionException(final Throwable throwable) {
83 | super(throwable);
84 | }
85 |
86 | public String getRedirectionUrl() {
87 | return mRedirectionUrl;
88 | }
89 |
90 | public int getStatusCode() {
91 | return mStatusCode;
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/src/com/foxykeep/datadroid/exception/CustomRequestException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 2011 Foxykeep (http://datadroid.foxykeep.com)
3 | *
4 | * Licensed under the Beerware License :
5 | * As long as you retain this notice you can do whatever you want with this stuff. If we meet some
6 | * day, and you think this stuff is worth it, you can buy me a beer in return
7 | */
8 |
9 | package com.foxykeep.datadroid.exception;
10 |
11 | /**
12 | * Thrown to indicate that a custom exception occurred.
13 | *
14 | * Subclass this class to create special exceptions for your needs.
15 | *
16 | * @author Foxykeep
17 | */
18 | public abstract class CustomRequestException extends Exception {
19 |
20 | private static final long serialVersionUID = 4658308128254827562L;
21 |
22 | /**
23 | * Constructs a new {@link CustomRequestException} that includes the current stack trace.
24 | */
25 | public CustomRequestException() {
26 | super();
27 | }
28 |
29 | /**
30 | * Constructs a new {@link CustomRequestException} that includes the current stack trace, the
31 | * specified detail message and the specified cause.
32 | *
33 | * @param detailMessage The detail message for this exception.
34 | * @param throwable The cause of this exception.
35 | */
36 | public CustomRequestException(final String detailMessage, final Throwable throwable) {
37 | super(detailMessage, throwable);
38 | }
39 |
40 | /**
41 | * Constructs a new {@link CustomRequestException} that includes the current stack trace and the
42 | * specified detail message.
43 | *
44 | * @param detailMessage The detail message for this exception.
45 | */
46 | public CustomRequestException(final String detailMessage) {
47 | super(detailMessage);
48 | }
49 |
50 | /**
51 | * Constructs a new {@link CustomRequestException} that includes the current stack trace and the
52 | * specified cause.
53 | *
54 | * @param throwable The cause of this exception.
55 | */
56 | public CustomRequestException(final Throwable throwable) {
57 | super(throwable);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/com/foxykeep/datadroid/exception/DataException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 2011 Foxykeep (http://datadroid.foxykeep.com)
3 | *
4 | * Licensed under the Beerware License :
5 | * As long as you retain this notice you can do whatever you want with this stuff. If we meet some
6 | * day, and you think this stuff is worth it, you can buy me a beer in return
7 | */
8 |
9 | package com.foxykeep.datadroid.exception;
10 |
11 | /**
12 | * Thrown to indicate that a compulsory parameter is missing.
13 | *
14 | * @author Foxykeep
15 | */
16 | public final class DataException extends Exception {
17 |
18 | private static final long serialVersionUID = -6031863210486494461L;
19 |
20 | /**
21 | * Constructs a new {@link DataException} that includes the current stack trace.
22 | */
23 | public DataException() {
24 | super();
25 | }
26 |
27 | /**
28 | * Constructs a new {@link DataException} that includes the current stack trace, the
29 | * specified detail message and the specified cause.
30 | *
31 | * @param detailMessage The detail message for this exception.
32 | * @param throwable The cause of this exception.
33 | */
34 | public DataException(final String detailMessage, final Throwable throwable) {
35 | super(detailMessage, throwable);
36 | }
37 |
38 | /**
39 | * Constructs a new {@link DataException} that includes the current stack trace and the
40 | * specified detail message.
41 | *
42 | * @param detailMessage The detail message for this exception.
43 | */
44 | public DataException(final String detailMessage) {
45 | super(detailMessage);
46 | }
47 |
48 | /**
49 | * Constructs a new {@link DataException} that includes the current stack trace and the
50 | * specified cause.
51 | *
52 | * @param throwable The cause of this exception.
53 | */
54 | public DataException(final Throwable throwable) {
55 | super(throwable);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/com/foxykeep/datadroid/internal/network/NetworkConnectionImpl.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 2011 Foxykeep (http://datadroid.foxykeep.com)
3 | *
4 | * Licensed under the Beerware License :
5 | * As long as you retain this notice you can do whatever you want with this stuff. If we meet some
6 | * day, and you think this stuff is worth it, you can buy me a beer in return
7 | */
8 |
9 | package com.foxykeep.datadroid.internal.network;
10 |
11 | import com.foxykeep.datadroid.exception.ConnectionException;
12 | import com.foxykeep.datadroid.network.NetworkConnection.ConnectionResult;
13 | import com.foxykeep.datadroid.network.NetworkConnection.Method;
14 | import com.foxykeep.datadroid.network.UserAgentUtils;
15 | import com.foxykeep.datadroid.util.DataDroidLog;
16 |
17 | import android.content.Context;
18 | import android.support.util.Base64;
19 | import android.text.TextUtils;
20 | import android.util.Log;
21 |
22 | import org.apache.http.HttpStatus;
23 | import org.apache.http.auth.UsernamePasswordCredentials;
24 | import org.apache.http.message.BasicNameValuePair;
25 | import org.apache.http.protocol.HTTP;
26 |
27 | import java.io.BufferedReader;
28 | import java.io.IOException;
29 | import java.io.InputStream;
30 | import java.io.InputStreamReader;
31 | import java.io.OutputStream;
32 | import java.net.HttpURLConnection;
33 | import java.net.URL;
34 | import java.net.URLEncoder;
35 | import java.security.KeyManagementException;
36 | import java.security.NoSuchAlgorithmException;
37 | import java.security.cert.X509Certificate;
38 | import java.util.ArrayList;
39 | import java.util.HashMap;
40 | import java.util.Map.Entry;
41 | import java.util.zip.GZIPInputStream;
42 | import javax.net.ssl.HostnameVerifier;
43 | import javax.net.ssl.HttpsURLConnection;
44 | import javax.net.ssl.SSLContext;
45 | import javax.net.ssl.SSLSession;
46 | import javax.net.ssl.SSLSocketFactory;
47 | import javax.net.ssl.TrustManager;
48 | import javax.net.ssl.X509TrustManager;
49 |
50 | /**
51 | * Implementation of the network connection.
52 | *
53 | * @author Foxykeep
54 | */
55 | public final class NetworkConnectionImpl {
56 |
57 | private static final String TAG = NetworkConnectionImpl.class.getSimpleName();
58 |
59 | private static final String ACCEPT_CHARSET_HEADER = "Accept-Charset";
60 | private static final String ACCEPT_ENCODING_HEADER = "Accept-Encoding";
61 | private static final String AUTHORIZATION_HEADER = "Authorization";
62 | private static final String LOCATION_HEADER = "Location";
63 |
64 | private static final String UTF8_CHARSET = "UTF-8";
65 |
66 | // Default connection and socket timeout of 60 seconds. Tweak to taste.
67 | private static final int OPERATION_TIMEOUT = 60 * 1000;
68 |
69 | private NetworkConnectionImpl() {
70 | // No public constructor
71 | }
72 |
73 | /**
74 | * Call the webservice using the given parameters to construct the request and return the
75 | * result.
76 | *
77 | * @param context The context to use for this operation. Used to generate the user agent if
78 | * needed.
79 | * @param urlValue The webservice URL.
80 | * @param method The request method to use.
81 | * @param parameterList The parameters to add to the request.
82 | * @param headerMap The headers to add to the request.
83 | * @param isGzipEnabled Whether the request will use gzip compression if available on the
84 | * server.
85 | * @param userAgent The user agent to set in the request. If null, a default Android one will be
86 | * created.
87 | * @param postText The POSTDATA text to add in the request.
88 | * @param credentials The credentials to use for authentication.
89 | * @param isSslValidationEnabled Whether the request will validate the SSL certificates.
90 | * @return The result of the webservice call.
91 | */
92 | public static ConnectionResult execute(Context context, String urlValue, Method method,
93 | ArrayList parameterList, HashMap headerMap,
94 | boolean isGzipEnabled, String userAgent, String postText,
95 | UsernamePasswordCredentials credentials, boolean isSslValidationEnabled) throws
96 | ConnectionException {
97 | HttpURLConnection connection = null;
98 | try {
99 | // Prepare the request information
100 | if (userAgent == null) {
101 | userAgent = UserAgentUtils.get(context);
102 | }
103 | if (headerMap == null) {
104 | headerMap = new HashMap();
105 | }
106 | headerMap.put(HTTP.USER_AGENT, userAgent);
107 | if (isGzipEnabled) {
108 | headerMap.put(ACCEPT_ENCODING_HEADER, "gzip");
109 | }
110 | headerMap.put(ACCEPT_CHARSET_HEADER, UTF8_CHARSET);
111 | if (credentials != null) {
112 | headerMap.put(AUTHORIZATION_HEADER, createAuthenticationHeader(credentials));
113 | }
114 |
115 | StringBuilder paramBuilder = new StringBuilder();
116 | if (parameterList != null && !parameterList.isEmpty()) {
117 | for (int i = 0, size = parameterList.size(); i < size; i++) {
118 | BasicNameValuePair parameter = parameterList.get(i);
119 | String name = parameter.getName();
120 | String value = parameter.getValue();
121 | if (TextUtils.isEmpty(name)) {
122 | // Empty parameter name. Check the next one.
123 | continue;
124 | }
125 | if (value == null) {
126 | value = "";
127 | }
128 | paramBuilder.append(URLEncoder.encode(name, UTF8_CHARSET));
129 | paramBuilder.append("=");
130 | paramBuilder.append(URLEncoder.encode(value, UTF8_CHARSET));
131 | paramBuilder.append("&");
132 | }
133 | }
134 |
135 | // Log the request
136 | if (DataDroidLog.canLog(Log.DEBUG)) {
137 | DataDroidLog.d(TAG, "Request url: " + urlValue);
138 | DataDroidLog.d(TAG, "Method: " + method.toString());
139 |
140 | if (parameterList != null && !parameterList.isEmpty()) {
141 | DataDroidLog.d(TAG, "Parameters:");
142 | for (int i = 0, size = parameterList.size(); i < size; i++) {
143 | BasicNameValuePair parameter = parameterList.get(i);
144 | String message = "- \"" + parameter.getName() + "\" = \""
145 | + parameter.getValue() + "\"";
146 | DataDroidLog.d(TAG, message);
147 | }
148 |
149 | DataDroidLog.d(TAG, "Parameters String: \"" + paramBuilder.toString() + "\"");
150 | }
151 |
152 | if (postText != null) {
153 | DataDroidLog.d(TAG, "Post data: " + postText);
154 | }
155 |
156 | if (headerMap != null && !headerMap.isEmpty()) {
157 | DataDroidLog.d(TAG, "Headers:");
158 | for (Entry header : headerMap.entrySet()) {
159 | DataDroidLog.d(TAG, "- " + header.getKey() + " = " + header.getValue());
160 | }
161 | }
162 | }
163 |
164 | // Create the connection object
165 | URL url = null;
166 | String outputText = null;
167 | switch (method) {
168 | case GET:
169 | case DELETE:
170 | String fullUrlValue = urlValue;
171 | if (paramBuilder.length() > 0) {
172 | fullUrlValue += "?" + paramBuilder.toString();
173 | }
174 | url = new URL(fullUrlValue);
175 | connection = (HttpURLConnection) url.openConnection();
176 | break;
177 | case PUT:
178 | case POST:
179 | url = new URL(urlValue);
180 | connection = (HttpURLConnection) url.openConnection();
181 | connection.setDoOutput(true);
182 |
183 | if (paramBuilder.length() > 0) {
184 | outputText = paramBuilder.toString();
185 | headerMap.put(HTTP.CONTENT_TYPE, "application/x-www-form-urlencoded");
186 | headerMap.put(HTTP.CONTENT_LEN,
187 | String.valueOf(outputText.getBytes().length));
188 | } else if (postText != null) {
189 | outputText = postText;
190 | }
191 | break;
192 | }
193 |
194 | // Set the request method
195 | connection.setRequestMethod(method.toString());
196 |
197 | // If it's an HTTPS request and the SSL Validation is disabled
198 | if (url.getProtocol().equals("https")
199 | && !isSslValidationEnabled) {
200 | HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
201 | httpsConnection.setSSLSocketFactory(getAllHostsValidSocketFactory());
202 | httpsConnection.setHostnameVerifier(getAllHostsValidVerifier());
203 | }
204 |
205 | // Add the headers
206 | if (!headerMap.isEmpty()) {
207 | for (Entry header : headerMap.entrySet()) {
208 | connection.addRequestProperty(header.getKey(), header.getValue());
209 | }
210 | }
211 |
212 | // Set the connection and read timeout
213 | connection.setConnectTimeout(OPERATION_TIMEOUT);
214 | connection.setReadTimeout(OPERATION_TIMEOUT);
215 |
216 | // Set the outputStream content for POST and PUT requests
217 | if ((method == Method.POST || method == Method.PUT) && outputText != null) {
218 | OutputStream output = null;
219 | try {
220 | output = connection.getOutputStream();
221 | output.write(outputText.getBytes());
222 | } finally {
223 | if (output != null) {
224 | try {
225 | output.close();
226 | } catch (IOException e) {
227 | // Already catching the first IOException so nothing to do here.
228 | }
229 | }
230 | }
231 | }
232 |
233 | String contentEncoding = connection.getHeaderField(HTTP.CONTENT_ENCODING);
234 |
235 | int responseCode = connection.getResponseCode();
236 | boolean isGzip = contentEncoding != null
237 | && contentEncoding.equalsIgnoreCase("gzip");
238 | DataDroidLog.d(TAG, "Response code: " + responseCode);
239 |
240 | if (responseCode == HttpStatus.SC_MOVED_PERMANENTLY) {
241 | String redirectionUrl = connection.getHeaderField(LOCATION_HEADER);
242 | throw new ConnectionException("New location : " + redirectionUrl,
243 | redirectionUrl);
244 | }
245 |
246 | InputStream errorStream = connection.getErrorStream();
247 | if (errorStream != null) {
248 | String error = convertStreamToString(errorStream, isGzip);
249 | throw new ConnectionException(error, responseCode);
250 | }
251 |
252 | String body = convertStreamToString(connection.getInputStream(),
253 | isGzip);
254 |
255 | if (DataDroidLog.canLog(Log.VERBOSE)) {
256 | DataDroidLog.v(TAG, "Response body: ");
257 |
258 | int pos = 0;
259 | int bodyLength = body.length();
260 | while (pos < bodyLength) {
261 | DataDroidLog.v(TAG, body.substring(pos, Math.min(bodyLength - 1, pos + 200)));
262 | pos = pos + 200;
263 | }
264 | }
265 |
266 | return new ConnectionResult(connection.getHeaderFields(), body);
267 | } catch (IOException e) {
268 | DataDroidLog.e(TAG, "IOException", e);
269 | throw new ConnectionException(e);
270 | } catch (KeyManagementException e) {
271 | DataDroidLog.e(TAG, "KeyManagementException", e);
272 | throw new ConnectionException(e);
273 | } catch (NoSuchAlgorithmException e) {
274 | DataDroidLog.e(TAG, "NoSuchAlgorithmException", e);
275 | throw new ConnectionException(e);
276 | } finally {
277 | if (connection != null) {
278 | connection.disconnect();
279 | }
280 | }
281 | }
282 |
283 | private static String createAuthenticationHeader(UsernamePasswordCredentials credentials) {
284 | StringBuilder sb = new StringBuilder();
285 | sb.append(credentials.getUserName()).append(":").append(credentials.getPassword());
286 | return "Basic " + Base64.encodeToString(sb.toString().getBytes(), Base64.NO_WRAP);
287 | }
288 |
289 | private static SSLSocketFactory sAllHostsValidSocketFactory;
290 |
291 | private static SSLSocketFactory getAllHostsValidSocketFactory()
292 | throws NoSuchAlgorithmException, KeyManagementException {
293 | if (sAllHostsValidSocketFactory == null) {
294 | TrustManager[] trustAllCerts = new TrustManager[] {
295 | new X509TrustManager() {
296 | public java.security.cert.X509Certificate[] getAcceptedIssuers() {
297 | return null;
298 | }
299 |
300 | public void checkClientTrusted(X509Certificate[] certs, String authType) {}
301 |
302 | public void checkServerTrusted(X509Certificate[] certs, String authType) {}
303 | }
304 | };
305 |
306 | SSLContext sc = SSLContext.getInstance("SSL");
307 | sc.init(null, trustAllCerts, new java.security.SecureRandom());
308 | sAllHostsValidSocketFactory = sc.getSocketFactory();
309 | }
310 |
311 | return sAllHostsValidSocketFactory;
312 | }
313 |
314 | private static HostnameVerifier sAllHostsValidVerifier;
315 |
316 | private static HostnameVerifier getAllHostsValidVerifier() {
317 | if (sAllHostsValidVerifier == null) {
318 | sAllHostsValidVerifier = new HostnameVerifier() {
319 | public boolean verify(String hostname, SSLSession session) {
320 | return true;
321 | }
322 | };
323 | }
324 |
325 | return sAllHostsValidVerifier;
326 | }
327 |
328 | private static String convertStreamToString(InputStream is, boolean isGzipEnabled)
329 | throws IOException {
330 | InputStream cleanedIs = is;
331 | if (isGzipEnabled) {
332 | cleanedIs = new GZIPInputStream(is);
333 | }
334 |
335 | BufferedReader reader = null;
336 | try {
337 | reader = new BufferedReader(new InputStreamReader(cleanedIs, UTF8_CHARSET));
338 | StringBuilder sb = new StringBuilder();
339 | for (String line; (line = reader.readLine()) != null;) {
340 | sb.append(line);
341 | sb.append("\n");
342 | }
343 |
344 | return sb.toString();
345 | } finally {
346 | if (reader != null) {
347 | reader.close();
348 | }
349 |
350 | cleanedIs.close();
351 |
352 | if (isGzipEnabled) {
353 | is.close();
354 | }
355 | }
356 | }
357 | }
358 |
--------------------------------------------------------------------------------
/src/com/foxykeep/datadroid/network/NetworkConnection.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 2012 Foxykeep (http://datadroid.foxykeep.com)
3 | *
4 | * Licensed under the Beerware License :
5 | * As long as you retain this notice you can do whatever you want with this stuff. If we meet some
6 | * day, and you think this stuff is worth it, you can buy me a beer in return
7 | */
8 |
9 | package com.foxykeep.datadroid.network;
10 |
11 | import com.foxykeep.datadroid.exception.ConnectionException;
12 | import com.foxykeep.datadroid.internal.network.NetworkConnectionImpl;
13 | import com.foxykeep.datadroid.util.DataDroidLog;
14 |
15 | import android.content.Context;
16 |
17 | import org.apache.http.auth.UsernamePasswordCredentials;
18 | import org.apache.http.message.BasicNameValuePair;
19 |
20 | import java.util.ArrayList;
21 | import java.util.HashMap;
22 | import java.util.List;
23 | import java.util.Map;
24 |
25 | /**
26 | * This class gives the user an API to easily call a webservice and return the received response.
27 | *
28 | * @author Foxykeep
29 | */
30 | public final class NetworkConnection {
31 |
32 | private static final String LOG_TAG = NetworkConnection.class.getSimpleName();
33 |
34 | public static enum Method {
35 | GET, POST, PUT, DELETE
36 | }
37 |
38 | /**
39 | * The result of a webservice call.
40 | *
41 | * Contains the headers and the body of the response as an unparsed String.
42 | *
43 | * @author Foxykeep
44 | */
45 | public static final class ConnectionResult {
46 |
47 | public final Map> headerMap;
48 | public final String body;
49 |
50 | public ConnectionResult(Map> headerMap, String body) {
51 | this.headerMap = headerMap;
52 | this.body = body;
53 | }
54 | }
55 |
56 | private final Context mContext;
57 | private final String mUrl;
58 | private Method mMethod = Method.GET;
59 | private ArrayList mParameterList = null;
60 | private HashMap mHeaderMap = null;
61 | private boolean mIsGzipEnabled = true;
62 | private String mUserAgent = null;
63 | private String mPostText = null;
64 | private UsernamePasswordCredentials mCredentials = null;
65 | private boolean mIsSslValidationEnabled = true;
66 |
67 | /**
68 | * Create a {@link NetworkConnection}.
69 | *
70 | * The Method to use is {@link Method#GET} by default.
71 | *
72 | * @param context The context used by the {@link NetworkConnection}. Used to create the
73 | * User-Agent.
74 | * @param url The URL to call.
75 | */
76 | public NetworkConnection(Context context, String url) {
77 | if (url == null) {
78 | DataDroidLog.e(LOG_TAG, "NetworkConnection.NetworkConnection - request URL cannot be null.");
79 | throw new NullPointerException("Request URL has not been set.");
80 | }
81 | mContext = context;
82 | mUrl = url;
83 | }
84 |
85 | /**
86 | * Set the method to use. Default is {@link Method#GET}.
87 | *
88 | * If set to another value than {@link Method#POST}, the POSTDATA text will be reset as it can
89 | * only be used with a POST request.
90 | *
91 | * @param method The method to use.
92 | * @return The networkConnection.
93 | */
94 | public NetworkConnection setMethod(Method method) {
95 | mMethod = method;
96 | if (method != Method.POST) {
97 | mPostText = null;
98 | }
99 | return this;
100 | }
101 |
102 | /**
103 | * Set the parameters to add to the request. This is meant to be a "key" => "value" Map.
104 | *
105 | * The POSTDATA text will be reset as they cannot be used at the same time.
106 | *
107 | * @see #setPostText(String)
108 | * @see #setParameters(ArrayList)
109 | * @param parameterMap The parameters to add to the request.
110 | * @return The networkConnection.
111 | */
112 | public NetworkConnection setParameters(HashMap parameterMap) {
113 | ArrayList parameterList = new ArrayList();
114 | for (Map.Entry entry : parameterMap.entrySet()) {
115 | parameterList.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
116 | }
117 |
118 | return setParameters(parameterList);
119 | }
120 |
121 |
122 | /**
123 | * Set the parameters to add to the request. This is meant to be a "key" => "value" Map.
124 | *
125 | * The POSTDATA text will be reset as they cannot be used at the same time.
126 | *
127 | * This method allows you to have multiple values for a single key in contrary to the HashMap
128 | * version of the method ({@link #setParameters(HashMap)})
129 | *
130 | * @see #setPostText(String)
131 | * @see #setParameters(HashMap)
132 | * @param parameterList The parameters to add to the request.
133 | * @return The networkConnection.
134 | */
135 | public NetworkConnection setParameters(ArrayList parameterList) {
136 | mParameterList = parameterList;
137 | mPostText = null;
138 | return this;
139 | }
140 |
141 | /**
142 | * Set the headers to add to the request.
143 | *
144 | * @param headerMap The headers to add to the request.
145 | * @return The networkConnection.
146 | */
147 | public NetworkConnection setHeaderList(HashMap headerMap) {
148 | mHeaderMap = headerMap;
149 | return this;
150 | }
151 |
152 | /**
153 | * Set whether the request will use gzip compression if available on the server. Default is
154 | * true.
155 | *
156 | * @param isGzipEnabled Whether the request will user gzip compression if available on the
157 | * server.
158 | * @return The networkConnection.
159 | */
160 | public NetworkConnection setGzipEnabled(boolean isGzipEnabled) {
161 | mIsGzipEnabled = isGzipEnabled;
162 | return this;
163 | }
164 |
165 | /**
166 | * Set the user agent to set in the request. Otherwise a default Android one will be used.
167 | *
168 | * @param userAgent The user agent.
169 | * @return The networkConnection.
170 | */
171 | public NetworkConnection setUserAgent(String userAgent) {
172 | mUserAgent = userAgent;
173 | return this;
174 | }
175 |
176 | /**
177 | * Set the POSTDATA text that will be added in the request. Also automatically set the
178 | * {@link Method} to {@link Method#POST} to be able to use it.
179 | *
180 | * The parameters will be reset as they cannot be used at the same time.
181 | *
182 | * @param postText The POSTDATA text that will be added in the request.
183 | * @return The networkConnection.
184 | * @see #setParameters(HashMap)
185 | * @see #setPostText(String)
186 | */
187 | public NetworkConnection setPostText(String postText) {
188 | return setPostText(postText, Method.POST);
189 | }
190 |
191 | /**
192 | * Set the POSTDATA text that will be added in the request and also set the {@link Method}
193 | * to use. The Method can only be {@link Method#POST} or {@link Method#PUT}.
194 | *
195 | * The parameters will be reset as they cannot be used at the same time.
196 | *
197 | * @param postText The POSTDATA text that will be added in the request.
198 | * @param method The method to use.
199 | * @return The networkConnection.
200 | * @see #setParameters(HashMap)
201 | * @see #setPostText(String)
202 | */
203 | public NetworkConnection setPostText(String postText, Method method) {
204 | if (method != Method.POST && method != Method.PUT) {
205 | throw new IllegalArgumentException("Method must be POST or PUT");
206 | }
207 | mPostText = postText;
208 | mMethod = method;
209 | mParameterList = null;
210 | return this;
211 | }
212 |
213 | /**
214 | * Set the credentials to use for authentication.
215 | *
216 | * @param credentials The credentials to use for authentication.
217 | * @return The networkConnection.
218 | */
219 | public NetworkConnection setCredentials(UsernamePasswordCredentials credentials) {
220 | mCredentials = credentials;
221 | return this;
222 | }
223 |
224 | /**
225 | * Set whether the SSL certificates validation are enabled. Default is true.
226 | *
227 | * @param enabled Whether the SSL certificates validation are enabled.
228 | * @return The networkConnection.
229 | */
230 | public NetworkConnection setSslValidationEnabled(boolean enabled) {
231 | mIsSslValidationEnabled = enabled;
232 | return this;
233 | }
234 |
235 | /**
236 | * Execute the webservice call and return the {@link ConnectionResult}.
237 | *
238 | * @return The result of the webservice call.
239 | */
240 | public ConnectionResult execute() throws ConnectionException {
241 | return NetworkConnectionImpl.execute(mContext, mUrl, mMethod, mParameterList,
242 | mHeaderMap, mIsGzipEnabled, mUserAgent, mPostText, mCredentials,
243 | mIsSslValidationEnabled);
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/src/com/foxykeep/datadroid/network/UserAgentUtils.java:
--------------------------------------------------------------------------------
1 | /**
2 | * 2012 Foxykeep (http://datadroid.foxykeep.com)
3 | *
4 | * Licensed under the Beerware License :
5 | * As long as you retain this notice you can do whatever you want with this stuff. If we meet some
6 | * day, and you think this stuff is worth it, you can buy me a beer in return
7 | */
8 |
9 | package com.foxykeep.datadroid.network;
10 |
11 | import android.content.Context;
12 | import android.content.pm.PackageInfo;
13 | import android.content.pm.PackageManager;
14 | import android.content.pm.PackageManager.NameNotFoundException;
15 | import android.os.Build;
16 | import android.text.TextUtils;
17 |
18 | import java.util.Locale;
19 |
20 | /**
21 | * @author Foxykeep
22 | *
23 | * Utility class to generate and cache a User-Agent header for HTTP requests.
24 | */
25 | public final class UserAgentUtils {
26 |
27 | private static String sUserAgent;
28 |
29 | private UserAgentUtils() {
30 | // No public constructor
31 | }
32 |
33 | /**
34 | * Get the User-Agent with the following syntax:
35 | *
4 | * Licensed under the Beerware License :
5 | * As long as you retain this notice you can do whatever you want with this stuff. If we meet some
6 | * day, and you think this stuff is worth it, you can buy me a beer in return
7 | */
8 |
9 | package com.foxykeep.datadroid.requestmanager;
10 |
11 | import com.foxykeep.datadroid.service.RequestService;
12 | import com.foxykeep.datadroid.util.ObjectUtils;
13 |
14 | import android.os.Bundle;
15 | import android.os.Parcel;
16 | import android.os.Parcelable;
17 |
18 | import java.util.ArrayList;
19 |
20 | /**
21 | * Class used to store your request information : request type as well as parameters.
22 | *
23 | * @author Foxykeep
24 | */
25 | public final class Request implements Parcelable {
26 |
27 | private static final int TYPE_BOOLEAN = 1;
28 | private static final int TYPE_BYTE = 2;
29 | private static final int TYPE_CHAR = 3;
30 | private static final int TYPE_SHORT = 4;
31 | private static final int TYPE_INT = 5;
32 | private static final int TYPE_LONG = 6;
33 | private static final int TYPE_FLOAT = 7;
34 | private static final int TYPE_DOUBLE = 8;
35 | private static final int TYPE_STRING = 9;
36 | private static final int TYPE_CHARSEQUENCE = 10;
37 | private static final int TYPE_PARCELABLE = 11;
38 |
39 | private int mRequestType = -1;
40 | private boolean mMemoryCacheDataEnabled = false;
41 | private final ArrayList mParamList = new ArrayList();
42 | private final ArrayList mTypeList = new ArrayList();
43 | private Bundle mBundle = new Bundle();
44 |
45 | /**
46 | * Constructor
47 | *
48 | * @param requestType The request type.
49 | */
50 | public Request(int requestType) {
51 | mRequestType = requestType;
52 | }
53 |
54 | /**
55 | * Return the request type.
56 | *
57 | * @return The request type.
58 | */
59 | public int getRequestType() {
60 | return mRequestType;
61 | }
62 |
63 | /**
64 | * Set whether the data returned from the {@link RequestService} must be cached in memory or
65 | * not.
66 | *
67 | * @param enabled Whether the data returned from the {@link RequestService} must be cached in
68 | * memory or not.
69 | */
70 | public void setMemoryCacheEnabled(boolean enabled) {
71 | mMemoryCacheDataEnabled = enabled;
72 | }
73 |
74 | /**
75 | * Return whether the data returned from the {@link RequestService} must be cached in memory or
76 | * not.
77 | *
78 | * @return Whether the data returned from the {@link RequestService} must be cached in memory or
79 | * not.
80 | */
81 | public boolean isMemoryCacheEnabled() {
82 | return mMemoryCacheDataEnabled;
83 | }
84 |
85 | /**
86 | * Add a boolean parameter to the request, replacing any existing value for the given name.
87 | *
88 | * @param name The parameter name.
89 | * @param value The parameter value.
90 | * @return This RequestData.
91 | */
92 | public Request put(String name, boolean value) {
93 | removeFromRequestData(name);
94 | mParamList.add(name);
95 | mTypeList.add(TYPE_BOOLEAN);
96 | mBundle.putBoolean(name, value);
97 | return this;
98 | }
99 |
100 | /**
101 | * Add a byte parameter to the request, replacing any existing value for the given name.
102 | *
103 | * @param name The parameter name.
104 | * @param value The parameter value.
105 | * @return This RequestData.
106 | */
107 | public Request put(String name, byte value) {
108 | removeFromRequestData(name);
109 | mParamList.add(name);
110 | mTypeList.add(TYPE_BYTE);
111 | mBundle.putByte(name, value);
112 | return this;
113 | }
114 |
115 | /**
116 | * Add a char parameter to the request, replacing any existing value for the given name.
117 | *
118 | * @param name The parameter name.
119 | * @param value The parameter value.
120 | * @return This RequestData.
121 | */
122 | public Request put(String name, char value) {
123 | removeFromRequestData(name);
124 | mParamList.add(name);
125 | mTypeList.add(TYPE_CHAR);
126 | mBundle.putChar(name, value);
127 | return this;
128 | }
129 |
130 | /**
131 | * Add a short parameter to the request, replacing any existing value for the given name.
132 | *
133 | * @param name The parameter name.
134 | * @param value The parameter value.
135 | * @return This RequestData.
136 | */
137 | public Request put(String name, short value) {
138 | removeFromRequestData(name);
139 | mParamList.add(name);
140 | mTypeList.add(TYPE_SHORT);
141 | mBundle.putShort(name, value);
142 | return this;
143 | }
144 |
145 | /**
146 | * Add a int parameter to the request, replacing any existing value for the given name.
147 | *
148 | * @param name The parameter name.
149 | * @param value The parameter value.
150 | * @return This RequestData.
151 | */
152 | public Request put(String name, int value) {
153 | removeFromRequestData(name);
154 | mParamList.add(name);
155 | mTypeList.add(TYPE_INT);
156 | mBundle.putInt(name, value);
157 | return this;
158 | }
159 |
160 | /**
161 | * Add a long parameter to the request, replacing any existing value for the given name.
162 | *
163 | * @param name The parameter name.
164 | * @param value The parameter value.
165 | * @return This RequestData.
166 | */
167 | public Request put(String name, long value) {
168 | removeFromRequestData(name);
169 | mParamList.add(name);
170 | mTypeList.add(TYPE_LONG);
171 | mBundle.putLong(name, value);
172 | return this;
173 | }
174 |
175 | /**
176 | * Add a float parameter to the request, replacing any existing value for the given name.
177 | *
178 | * @param name The parameter name.
179 | * @param value The parameter value.
180 | * @return This RequestData.
181 | */
182 | public Request put(String name, float value) {
183 | removeFromRequestData(name);
184 | mParamList.add(name);
185 | mTypeList.add(TYPE_FLOAT);
186 | mBundle.putFloat(name, value);
187 | return this;
188 | }
189 |
190 | /**
191 | * Add a double parameter to the request, replacing any existing value for the given name.
192 | *
193 | * @param name The parameter name.
194 | * @param value The parameter value.
195 | * @return This RequestData.
196 | */
197 | public Request put(String name, double value) {
198 | removeFromRequestData(name);
199 | mParamList.add(name);
200 | mTypeList.add(TYPE_DOUBLE);
201 | mBundle.putDouble(name, value);
202 | return this;
203 | }
204 |
205 | /**
206 | * Add a String parameter to the request, replacing any existing value for the given name.
207 | *
208 | * @param name The parameter name.
209 | * @param value The parameter value.
210 | * @return This RequestData.
211 | */
212 | public Request put(String name, String value) {
213 | removeFromRequestData(name);
214 | mParamList.add(name);
215 | mTypeList.add(TYPE_STRING);
216 | mBundle.putString(name, value);
217 | return this;
218 | }
219 |
220 | /**
221 | * Add a CharSequence parameter to the request, replacing any existing value for the given name.
222 | *
223 | * @param name The parameter name.
224 | * @param value The parameter value.
225 | * @return This RequestData.
226 | */
227 | public Request put(String name, CharSequence value) {
228 | removeFromRequestData(name);
229 | mParamList.add(name);
230 | mTypeList.add(TYPE_CHARSEQUENCE);
231 | mBundle.putCharSequence(name, value);
232 | return this;
233 | }
234 |
235 | /**
236 | * Add a Parcelable parameter to the request, replacing any existing value for the given name.
237 | *
238 | * @param name The parameter name.
239 | * @param value The parameter value.
240 | * @return This RequestData.
241 | */
242 | public Request put(String name, Parcelable value) {
243 | removeFromRequestData(name);
244 | mParamList.add(name);
245 | mTypeList.add(TYPE_PARCELABLE);
246 | mBundle.putParcelable(name, value);
247 | return this;
248 | }
249 |
250 | private void removeFromRequestData(String name) {
251 | if (mParamList.contains(name)) {
252 | final int index = mParamList.indexOf(name);
253 | mParamList.remove(index);
254 | mTypeList.remove(index);
255 | mBundle.remove(name);
256 | }
257 | }
258 |
259 | /**
260 | * Check whether the request has an existing value for the given name.
261 | *
262 | * @param name The parameter name.
263 | * @return Whether the request has an existing value for the given name.
264 | */
265 | public boolean contains(String name) {
266 | return mParamList.contains(name);
267 | }
268 |
269 | /**
270 | * Returns the value associated with the given name, or false if no mapping of the desired type
271 | * exists for the given name.
272 | *
273 | * @param name The parameter name.
274 | * @return A boolean value.
275 | */
276 | public boolean getBoolean(String name) {
277 | return mBundle.getBoolean(name);
278 | }
279 |
280 | /**
281 | * Returns the value associated with the given name transformed as "1" if true or "0" if false.
282 | * If no mapping of the desired type exists for the given name, "0" is returned.
283 | *
284 | * @param name The parameter name.
285 | * @return The int String representation of the boolean value.
286 | */
287 | public String getBooleanAsIntString(String name) {
288 | boolean value = getBoolean(name);
289 | return value ? "1" : "0";
290 | }
291 |
292 | /**
293 | * Returns the value associated with the given name transformed as a String (using
294 | * {@link String#valueOf(boolean)}), or "false" if no mapping of the desired type exists for the
295 | * given name.
296 | *
297 | * @param name The parameter name.
298 | * @return The String representation of the boolean value.
299 | */
300 | public String getBooleanAsString(String name) {
301 | boolean value = getBoolean(name);
302 | return String.valueOf(value);
303 | }
304 |
305 | /**
306 | * Returns the value associated with the given name, or (byte) 0 if no mapping of the desired
307 | * type exists for the given name.
308 | *
309 | * @param name The parameter name.
310 | * @return A byte value.
311 | */
312 | public byte getByte(String name) {
313 | return mBundle.getByte(name);
314 | }
315 |
316 | /**
317 | * Returns the value associated with the given name, or (char) 0 if no mapping of the desired
318 | * type exists for the given name.
319 | *
320 | * @param name The parameter name.
321 | * @return A char value.
322 | */
323 | public char getChar(String name) {
324 | return mBundle.getChar(name);
325 | }
326 |
327 | /**
328 | * Returns the value associated with the given name transformed as a String (using
329 | * {@link String#valueOf(char)}), or "0" if no mapping of the desired type exists for the given
330 | * name.
331 | *
332 | * @param name The parameter name.
333 | * @return The String representation of the boolean value.
334 | */
335 | public String getCharAsString(String name) {
336 | char value = getChar(name);
337 | return String.valueOf(value);
338 | }
339 |
340 | /**
341 | * Returns the value associated with the given name, or (short) 0 if no mapping of the desired
342 | * type exists for the given name.
343 | *
344 | * @param name The parameter name.
345 | * @return A short value.
346 | */
347 | public short getShort(String name) {
348 | return mBundle.getShort(name);
349 | }
350 |
351 | /**
352 | * Returns the value associated with the given name transformed as a String (using
353 | * {@link String#valueOf(int)}), or "0" if no mapping of the desired type exists for the given
354 | * name.
355 | *
356 | * @param name The parameter name.
357 | * @return The String representation of the boolean value.
358 | */
359 | public String getShortAsString(String name) {
360 | short value = getShort(name);
361 | return String.valueOf(value);
362 | }
363 |
364 | /**
365 | * Returns the value associated with the given name, or 0 if no mapping of the desired type
366 | * exists for the given name.
367 | *
368 | * @param name The parameter name.
369 | * @return An int value.
370 | */
371 | public int getInt(String name) {
372 | return mBundle.getInt(name);
373 | }
374 |
375 | /**
376 | * Returns the value associated with the given name transformed as a String (using
377 | * {@link String#valueOf(int)}), or "0" if no mapping of the desired type exists for the given
378 | * name.
379 | *
380 | * @param name The parameter name.
381 | * @return The String representation of the boolean value.
382 | */
383 | public String getIntAsString(String name) {
384 | int value = getInt(name);
385 | return String.valueOf(value);
386 | }
387 |
388 | /**
389 | * Returns the value associated with the given name, or 0L if no mapping of the desired type
390 | * exists for the given name.
391 | *
392 | * @param name The parameter name.
393 | * @return A long value.
394 | */
395 | public long getLong(String name) {
396 | return mBundle.getLong(name);
397 | }
398 |
399 | /**
400 | * Returns the value associated with the given name transformed as a String (using
401 | * {@link String#valueOf(long)}), or "0" if no mapping of the desired type exists for the given
402 | * name.
403 | *
404 | * @param name The parameter name.
405 | * @return The String representation of the boolean value.
406 | */
407 | public String getLongAsString(String name) {
408 | long value = getLong(name);
409 | return String.valueOf(value);
410 | }
411 |
412 | /**
413 | * Returns the value associated with the given name, or 0.0f if no mapping of the desired type
414 | * exists for the given name.
415 | *
416 | * @param name The parameter name.
417 | * @return A float value.
418 | */
419 | public float getFloat(String name) {
420 | return mBundle.getFloat(name);
421 | }
422 |
423 | /**
424 | * Returns the value associated with the given name transformed as a String (using
425 | * {@link String#valueOf(float)}), or "0" if no mapping of the desired type exists for the given
426 | * name.
427 | *
428 | * @param name The parameter name.
429 | * @return The String representation of the boolean value.
430 | */
431 | public String getFloatAsString(String name) {
432 | float value = getFloat(name);
433 | return String.valueOf(value);
434 | }
435 |
436 | /**
437 | * Returns the value associated with the given name, or 0.0 if no mapping of the desired type
438 | * exists for the given name.
439 | *
440 | * @param name The parameter name.
441 | * @return A double value.
442 | */
443 | public double getDouble(String name) {
444 | return mBundle.getDouble(name);
445 | }
446 |
447 | /**
448 | * Returns the value associated with the given name transformed as a String (using
449 | * {@link String#valueOf(double)}), or "0" if no mapping of the desired type exists for the
450 | * given name.
451 | *
452 | * @param name The parameter name.
453 | * @return The String representation of the boolean value.
454 | */
455 | public String getDoubleAsString(String name) {
456 | double value = getDouble(name);
457 | return String.valueOf(value);
458 | }
459 |
460 | /**
461 | * Returns the value associated with the given name, or null if no mapping of the desired type
462 | * exists for the given name.
463 | *
464 | * @param name The parameter name.
465 | * @return A String value.
466 | */
467 | public String getString(String name) {
468 | return mBundle.getString(name);
469 | }
470 |
471 | /**
472 | * Returns the value associated with the given name, or null if no mapping of the desired type
473 | * exists for the given name.
474 | *
475 | * @param name The parameter name.
476 | * @return A CharSequence value.
477 | */
478 | public CharSequence getCharSequence(String name) {
479 | return mBundle.getCharSequence(name);
480 | }
481 |
482 | /**
483 | * Returns the value associated with the given name, or null if no mapping of the desired type
484 | * exists for the given name.
485 | *
486 | * @param name The parameter name.
487 | * @return A Parcelable value.
488 | */
489 | public Parcelable getParcelable(String name) {
490 | return mBundle.getParcelable(name);
491 | }
492 |
493 | /**
494 | * Sets the ClassLoader to use by the underlying Bundle when getting Parcelable objects.
495 | *
496 | * @param classLoader The ClassLoader to use by the underlying Bundle when getting Parcelable
497 | * objects.
498 | */
499 | public void setClassLoader(ClassLoader classLoader) {
500 | mBundle.setClassLoader(classLoader);
501 | }
502 |
503 | @Override
504 | public boolean equals(Object o) {
505 | if (o instanceof Request) {
506 | Request oParams = (Request) o;
507 | if (mRequestType != oParams.mRequestType) {
508 | return false;
509 | }
510 |
511 | if (mParamList.size() != oParams.mParamList.size()) {
512 | return false;
513 | }
514 |
515 | for (int i = 0, length = mParamList.size(); i < length; i++) {
516 | String param = mParamList.get(i);
517 | if (!oParams.mParamList.contains(param)) {
518 | return false;
519 | }
520 |
521 | int type = mTypeList.get(i);
522 | if (oParams.mTypeList.get(i) != type) {
523 | return false;
524 | }
525 |
526 | switch (mTypeList.get(i)) {
527 | case TYPE_BOOLEAN:
528 | if (mBundle.getBoolean(param) != oParams.mBundle.getBoolean(param)) {
529 | return false;
530 | }
531 | break;
532 | case TYPE_BYTE:
533 | if (mBundle.getByte(param) != oParams.mBundle.getByte(param)) {
534 | return false;
535 | }
536 | break;
537 | case TYPE_CHAR:
538 | if (mBundle.getChar(param) != oParams.mBundle.getChar(param)) {
539 | return false;
540 | }
541 | break;
542 | case TYPE_SHORT:
543 | if (mBundle.getShort(param) != oParams.mBundle.getShort(param)) {
544 | return false;
545 | }
546 | break;
547 | case TYPE_INT:
548 | if (mBundle.getInt(param) != oParams.mBundle.getInt(param)) {
549 | return false;
550 | }
551 | break;
552 | case TYPE_LONG:
553 | if (mBundle.getLong(param) != oParams.mBundle.getLong(param)) {
554 | return false;
555 | }
556 | break;
557 | case TYPE_FLOAT:
558 | if (mBundle.getFloat(param) != oParams.mBundle.getFloat(param)) {
559 | return false;
560 | }
561 | break;
562 | case TYPE_DOUBLE:
563 | if (mBundle.getDouble(param) != oParams.mBundle.getDouble(param)) {
564 | return false;
565 | }
566 | break;
567 | case TYPE_STRING:
568 | if (!ObjectUtils.safeEquals(mBundle.getString(param),
569 | oParams.mBundle.getString(param))) {
570 | return false;
571 | }
572 | break;
573 | case TYPE_CHARSEQUENCE:
574 | if (!ObjectUtils.safeEquals(mBundle.getCharSequence(param),
575 | oParams.mBundle.getCharSequence(param))) {
576 | return false;
577 | }
578 | break;
579 | case TYPE_PARCELABLE:
580 | if (!ObjectUtils.safeEquals(mBundle.getParcelable(param),
581 | oParams.mBundle.getParcelable(param))) {
582 | return false;
583 | }
584 | break;
585 | default:
586 | // We should never arrive here normally.
587 | throw new IllegalArgumentException(
588 | "The type of the field is not a valid one");
589 | }
590 | }
591 | return true;
592 | }
593 | return false;
594 | }
595 |
596 | @Override
597 | public int hashCode() {
598 | ArrayList