On Android-Wheel project at http://code.google.com/p/android-wheel/
Blog at http://android-devblog.blogspot.com/2011/01/android-wheel-update-custom-views-for.html
What/where should be changed so minutes (as example) on the time wheel, would show only numbers 00, 15, 30, 45 rather than all numbers 00 to 59 ?
The author, Kankan said: "just use a custom adapter based on the numeric one, with predefined items." But this is not enough lead to help me to change the project/code.
Can you provide specific lead on What/Where in the project should be changed ?
I just thought if this helps someone they can try this approach. I created a sample class
public class NumericWheelAdapterV2 extends AbstractWheelTextAdapter {
protected NumericWheelAdapterV2(Context context) {
super(context);
}
#Override
public int getItemsCount() {
return 4;
}
#Override
protected CharSequence getItemText(int index) {
if(0 == index){
return "00";
}else if(1 == index){
return "15";
} else if (2 == index){
return "30";
}else if (3 == index){
return "45";
}
return null;
}
}
If you check out the project and poke around a bit you'll find "NumericWheelAdapter" to be a good starting point to look into.
The example does
#Override
public CharSequence getItemText(int index) {
if (index >= 0 && index < getItemsCount()) {
int value = minValue + index;
return format != null ? String.format(format, value) : Integer.toString(value);
}
return null;
}
You could create your own NumericWheelAdapter that overrides CharSequence and instead of generating a value for each index, you can do something like index x 15 to get 0, 15, 30 ,45 and set min and max to 0 and 3.
I think you should be able to modify the code to your liking with these indications, otherwise if after trial and error you can't, you can always come back for more help ^^
Cheers
Jason
Related
Im having troubles with Android SortedList in RecyclerView, mainly with the remove method:
public void replaceAll(List userFertList, List defaultFertList){
restartIndexes(userFertList, defaultFertList);
mComparator.swapLists(Utils.fertiliserListToNameList(userFertList));
List<Fertiliser> combinedList = Utils.combineFertLists(userFertList, defaultFertList);
mSortedList.beginBatchedUpdates();
for (int i = mSortedList.size() -1; i > -1 ; i--) {
final Fertiliser fertiliser = mSortedList.get(i);
if(!combinedList.contains(fertiliser)){
if(!mSortedList.remove(fertiliser)){
throw new RuntimeException();
};
}
}
mSortedList.addAll(combinedList);
mSortedList.endBatchedUpdates();
}
The above code is executed when filtering the list. All of the objects that are not present in the new list are removed. However the call to remove objects sometimes fail. I know the object is present, because it's taken from the SortedList itself.
My research hinted me there's something wrong with my Comparator compare method:
#Override
public int compare(Fertiliser fertiliser, Fertiliser t1) {
if(fertiliser == t1){
return 0;
}
if(mUserFertNames.contains(fertiliser.getName()) != mUserFertNames.contains(t1.getName())){
return mUserFertNames.contains(fertiliser.getName()) ? -1 : 1;
} else {
return fertiliser.getName().compareToIgnoreCase(t1.getName());
}
}
Im sorting by two criteria (one that checks if the object is present in a list and by name).
So my thinking is, because SortedList uses the Comparator to locate the element, my Comparator gives false results, and the list cannot find the item:
The called method from the SortedList:
private int findIndexOf(T item, T[] mData, int left, int right, int reason) {
while (left < right) {
final int middle = (left + right) / 2;
T myItem = mData[middle];
final int cmp = mCallback.compare(myItem, item);
if (cmp < 0) {
left = middle + 1;
} else if (cmp == 0) {
if (mCallback.areItemsTheSame(myItem, item)) {
return middle;
} else {
int exact = linearEqualitySearch(item, middle, left, right);
if (reason == INSERTION) {
return exact == INVALID_POSITION ? middle : exact;
} else {
return exact;
}
}
} else {
right = middle;
}
}
return reason == INSERTION ? left : INVALID_POSITION;
}
However i coudn't find a solution. Can you help me?
P.S. When i examined the error, both objects were not in the list (so they were compared by names only).
Try .removeItemAt(i) instead of .remove(fertiliser). This worked for me while list was filtered.
In my MainActivity.java I have:
public static ArrayList<HashMap<Integer,Boolean>> booleanArrayList = new ArrayList<>();
public static void setBooleanArrayList(int p, Boolean b){
HashMap<Integer,Boolean> map = new HashMap<>();
map.put(p, b);
booleanArrayList.add(map);
}
public static HashMap<Integer, Boolean> getBooleanArrayList(int position){
return booleanArrayList.get(position);
}
I have a fragment which has a button. When the button is clicked I want it to sort MainActivity.booleanArrayList in ascending order.
For example:
{1,true}
{4,true}
{2,false}
{3,true}
sorted:
{1=true}
{2=false}
{3=true}
{4=true}
How would I go about sorting the ArrayList> once it is populated?
There are 2 steps in doing that. First, write a Comparator that will compare 2 Maps:
public class MapKeyComparator implements Comparator<Map<Integer, Boolean>> {
#Override
public int compare(Map<Integer, Boolean> me, Map<Integer, Boolean> him) {
Set<Integer> meSet = me.keySet();
Set<Integer> himSet = him.keySet();
// Sanity check
if(me.size() != 1 || himSet.size() != 1){
throw new RuntimeException("Comparison can only be done between 2 valid integers.");
}
// Values
int meI = 0, himI = 0;
for(Integer i : meSet){
meI = i;
}
for(Integer i : himSet){
himI = i;
}
// Compare
if(meI > himI){
return -1;
}
else if(meI < himI){
return 1;
}
else{
return 0;
}
}
}
And then, sort the list like below:
Collections.sort(booleanArrayList, new MapKeyComparator());
Explanation:
Well, a Comparator is an interface that you can implement to compare 2 objects, say Integers. It can be any object in general but think about it, since it is comparison, it should make sense too comparing them. For ex, you wouldn't compare Bird object with a Tree. You can compare one Bird type with that of another because they have attributes that you can compare.
Now, in your case, we had to compare the Keys of the Maps present in the ArrayList. That means, the object to compare is a Map but based on what?
Well, you already know that - the Key of the Map. And, Comparator is an interface that facilitates just that.
So, we shall build a Comparator for comparing 2 Maps based on each Map's key. This Comparator in turn shall be used by the Collections.sort() method which takes a list of objects to compare and a Comparator to compare them.
Before we build the Comparator, lets see what the Docs have to say about a Comparator.
public interface Comparator<T>
A comparison function, which imposes a total ordering on some
collection of objects. Comparators can be passed to a sort method
(such as Collections.sort or Arrays.sort) to allow precise control
over the sort order. Comparators can also be used to control the order
of certain data structures (such as sorted sets or sorted maps), or to
provide an ordering for collections of objects that don't have a
natural ordering.
And about the method you have to implement,
int compare(T o1, T o2)
Parameters:
o1 - the first object to be compared.
o2 - the second object to be compared.
Returns:
a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
Notice that it says to return 1 if first object is greater than second. But I did the reverse in the implementation so that you can get a ascending arrangement instead of a descending one in Collections.sort().
The implementation:
The implementation itself it pretty straightforward. Since I have to compare the keys from the Maps, I retrieve the keys in a Set:
Set<Integer> meSet = me.keySet();
Set<Integer> himSet = him.keySet();
Next, I check if they have a single element so that the comparison actually makes sense:
// Sanity check
if (me.size() != 1 && himSet.size() != 1) {
throw new RuntimeException("Comparison can only be done between 2 valid integers.");
}
Next, I iterate the sets to get the one element present in both of them:
// Values
int meI = 0, himI = 0;
for (Integer i: meSet) {
meI = i;
}
for (Integer i: himSet) {
himI = i;
}
Next, we actually compare them,
// Compare
if (meI > himI) {
return -1;
} else if (meI < himI) {
return 1;
} else {
return 0;
}
Also, see the docs for Collections.sort() method.
Having said all that, there are other(better) things you can do for your basic problem like using a TreeMap which arranges the elements when you insert with the natural ordering. So when you retrieve them, you get them in the order you had always wanted. #ravi-koradia 's advice is spot on in terms of that and this article does a great job of explaining it:
https://dzone.com/articles/hashmap-vs-treemap-vs
Instead of Map, you can use your own custom class with position and selected value field, as I can see you need hold only these two value and every map of your list is having single value only.
public static ArrayList<Values> booleanArrayList = new ArrayList<>();
public class Values implements Comparable<Values>{
int position;
boolean isSelected;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Values values = (Values) o;
return position == values.position;
}
#Override
public int hashCode() {
return position;
}
#Override
public int compareTo(#NonNull Values o) {
return (position < o.position) ? -1 : ((position == o.position) ? 0 : 1);
}
}
After that, you can call from anywhere to sort the arraylist:-
Collections.sort(booleanArrayList);
The minimum Youtube App. version required to be installed on a device to use the Youtube API is 4.2.16
To get the youtube app. version installed on a device:
String version = YouTubeIntents.getInstalledYouTubeVersionName(getContext());
Thats could return as known a three numbers separeted by dots. How can I check if the returned version number is less than 4.2.16. Which is obviously not an integer and not a floating point number?
Accepted for the ##Original answer:
Original answer:
Well, this may not be the easiest way, but you can do something like this:
First split the string at the .'s:
String[] partsOfVersion = version.split("\\.");
Then convert each part into an int:
int first = partsOfVersion.length>0 ? Integer.parseInt(partsOfVersion[0]) : 0;
int second = partsOfVersion.length>1 ? Integer.parseInt(partsOfVersion[1]) : 0;
int third = partsOfVersion.length>2 ? Integer.parseInt(partsOfVersion[2]) : 0;
Then you can just use an if statement to check if they're above 4.2.16:
if((first == 4 && second == 2 && third >= 16) || (first == 4 && second >== 3) || first >= 5) {
...
}
And if you want to check if it's less than that, just use an else statement after that if statement.
That should work, I haven't tested it though.
Added:
Someone (#JeroenVannevel) pointed out to me that there's an easier way to do this.
First remove all occurrences of .:
version = version.replace(".", "");
Then convert the string to an int:
int versionInt = Integer.parseInt(version);
And finally, check if the number is greater than or equal to (or less than if you want incompatible devices) 4216:
if(versionInt >= 4216) {
...
}
Also, you might want to check if version is empty or not before starting.
the answer to your problem is very simple, as you see from AndroidManifest.xml file to
YouTube 4.2.16 android apk I found this line .
<manifest android:versionCode="4216" android:versionName="4.2.16" package="com.google.android.youtube" ...>
so to check if app you need to do to check if the VersionCode biger than int (4216) by Public methods
getInstalledYouTubeVersionCode(Context context)
for more info visit YouTubeIntents Reference Guide.
In final you can use this ready check
if(YouTubeIntents.isYouTubeInstalled(getActivity()) && YouTubeIntents.getInstalledYouTubeVersionCode(getActivity())>4216){
//do what you want whit YouTubeIntents
}else {
//Ask user to install the last youtube app .
}
I hope this answer help you.
I wrote a class for precisely this purpose, maybe it could be useful.
public class Version implements Comparable<Version>
{
public final int Major;
public final int Minor;
public final int Build;
public final int Revision;
public Version(int major, int minor)
{
this(major, minor, 0);
}
public Version(int major, int minor, int build)
{
this(major, minor, build, 0);
}
public Version(int major, int minor, int build, int revision)
{
Major = major;
Minor = minor;
Build = build;
Revision = revision;
}
public Version(String str)
{
int major = 0;
int minor = 0;
int build = 0;
int revision = 0;
if (Strings.hasValue(str))
{
String[] parts = str.split("\\.");
if (parts.length > 0)
major = parseInt(parts[0]);
if (parts.length > 1)
minor = parseInt(parts[1]);
if (parts.length > 2)
build = parseInt(parts[2]);
if (parts.length > 3)
revision = parseInt(parts[3]);
}
Major = major;
Minor = minor;
Build = build;
Revision = revision;
}
private static int parseInt(String str)
{
try
{
return Integer.parseInt(str);
}
catch (NumberFormatException e)
{
return 0;
}
}
#Override
public String toString()
{
return String.format("%s.%s.%s.%s", Major, Minor, Build, Revision);
}
public boolean isEmpty()
{
return (Major == 0 && Minor == 0 && Build == 0 && Revision == 0);
}
#Override
public int compareTo(Version another)
{
if (another == null)
throw new NullPointerException();
int[] thisV = new int[] { Major, Minor, Build, Revision };
int[] otherV = new int[] { another.Major, another.Minor, another.Build, another.Revision };
for (int i = 0; i < thisV.length; i++)
{
if (thisV[i] < otherV[i])
return -1;
else if (thisV[i] > otherV[i])
return +1;
// else, loop to consider less significant parts.
}
return 0; // All equal.
}
}
I have a requirement where i have to implement Buttons which can move the cursor up and down the line in a multi line Edittext.
I went through the Developer website and found that Selection class http://developer.android.com/reference/android/text/Selection.html
can be used, but i am not able to use it..
Please help me here..
Thank you.
Source: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/text/Selection.java
I copied the code and made a little modification below, then appended it to my class.
public boolean moveDown(Layout layout) {
int start = myTextbox.getSelectionStart();
int end = myTextbox.getSelectionEnd();
if (start != end) {
int min = Math.min(start, end);
int max = Math.max(start, end);
myTextbox.setSelection(max);
if (min == 0 && max == myTextbox.length()) {
return false;
}
return true;
} else {
int line = layout.getLineForOffset(end);
if (line < layout.getLineCount() - 1) {
int move;
if (layout.getParagraphDirection(line) ==
layout.getParagraphDirection(line + 1)) {
float h = layout.getPrimaryHorizontal(end);
move = layout.getOffsetForHorizontal(line + 1, h);
} else {
move = layout.getLineStart(line + 1);
}
myTextbox.setSelection(move);
return true;
}
}
return false;
}
I hope this will be useful.
If somebody knows how to use real method without recreating it, please share :D
I am not sure about this code but it may help you to some extend....
just go through the code from the following url. i might help you to find out solution.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/text/Selection.java
Try:
editText.append("\n");
It will move cursor to next line.
Using Joda 1.6.2 with Android
The following code hangs for about 15 seconds.
DateTime dt = new DateTime();
Originally posted this post
Android Java - Joda Date is slow in Eclipse/Emulator -
Just tried it again and its still not any better. Does anyone else have this problem or know how to fix it?
I also ran into this problem. Jon Skeet's suspicions were correct, the problem is that the time zones are being loaded really inefficiently, opening a jar file and then reading the manifest to try to get this information.
However, simply calling DateTimeZone.setProvider([custom provider instance ...]) is not sufficient because, for reasons that don't make sense to me, DateTimeZone has a static initializer where it calls getDefaultProvider().
To be completely safe, you can override this default by setting this system property before you ever call anything in the joda.
In your activity, for example, add this:
#Override
public void onCreate(Bundle savedInstanceState) {
System.setProperty("org.joda.time.DateTimeZone.Provider",
"com.your.package.FastDateTimeZoneProvider");
}
Then all you have to do is define FastDateTimeZoneProvider. I wrote the following:
package com.your.package;
public class FastDateTimeZoneProvider implements Provider {
public static final Set<String> AVAILABLE_IDS = new HashSet<String>();
static {
AVAILABLE_IDS.addAll(Arrays.asList(TimeZone.getAvailableIDs()));
}
public DateTimeZone getZone(String id) {
if (id == null) {
return DateTimeZone.UTC;
}
TimeZone tz = TimeZone.getTimeZone(id);
if (tz == null) {
return DateTimeZone.UTC;
}
int rawOffset = tz.getRawOffset();
//sub-optimal. could be improved to only create a new Date every few minutes
if (tz.inDaylightTime(new Date())) {
rawOffset += tz.getDSTSavings();
}
return DateTimeZone.forOffsetMillis(rawOffset);
}
public Set getAvailableIDs() {
return AVAILABLE_IDS;
}
}
I've tested this and it appears to work on Android SDK 2.1+ with joda version 1.6.2. It can of course be optimized further, but while profiling my app (mogwee), this decreased the DateTimeZone initialize time from ~500ms to ~18ms.
If you are using proguard to build your app, you'll have to add this line to proguard.cfg because Joda expects the class name to be exactly as you specify:
-keep class com.your.package.FastDateTimeZoneProvider
I strongly suspect it's because it's having to build the ISO chronology for the default time zone, which probably involves reading all the time zone information in.
You could verify this by calling ISOChronology.getInstance() first - time that, and then time a subsequent call to new DateTime(). I suspect it'll be fast.
Do you know which time zones are going to be relevant in your application? You may find you can make the whole thing much quicker by rebuilding Joda Time with a very much reduced time zone database. Alternatively, call DateTimeZone.setProvider() with your own implementation of Provider which doesn't do as much work.
It's worth checking whether that's actually the problem first, of course :) You may also want to try explicitly passing in the UTC time zone, which won't require reading in the time zone database... although you never know when you'll accidentally trigger a call which does require the default time zone, at which point you'll incur the same cost.
I only need UTC in my application. So, following unchek's advice, I used
System.setProperty("org.joda.time.DateTimeZone.Provider", "org.joda.time.tz.UTCProvider");
org.joda.time.tz.UTCProvider is actually used by JodaTime as the secondary backup, so I thought why not use it for primary use? So far so good. It loads fast.
The top answer provided by plowman is not reliable if you must have precise timezone computations for your dates. Here is an example of problem that can happen:
Suppose your DateTime object is set for 4:00am, one hour after daylight savings have started that day. When Joda checks the FastDateTimeZoneProvider provider before 3:00am (i.e., before daylight savings) it will get a DateTimeZone object with the wrong offset because the tz.inDaylightTime(new Date()) check will return false.
My solution was to adopt the recently published joda-time-android library. It uses the core of Joda but makes sure to load a time zone only as needed from the raw folder. Setting up is easy with gradle. In your project, extend the Application class and add the following on its onCreate():
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
JodaTimeAndroid.init(this);
}
}
The author wrote a blog post about it last year.
I can confirm this issue with version 1, 1.5 and 1.62 of joda. Date4J is working well for me as an alternative.
http://www.date4j.net/
I just performed the test that #"Name is carl" posted, on several devices. I must note that the test is not completely valid and the results are misleading (in that it only reflects a single instance of DateTime).
From his test, When comparing DateTime to Date, DateTime is forced to parse the String ts, where Date does not parse anything.
While the initial creation of the DateTime was accurate, it ONLY takes that much time on the very FIRST creation... every instance after that was 0ms (or very near 0ms)
To verify this, I used the following code and created 1000 new instances of DateTime on an OLD Android 2.3 device
int iterations = 1000;
long totalTime = 0;
// Test Joda Date
for (int i = 0; i < iterations; i++) {
long d1 = System.currentTimeMillis();
DateTime d = new DateTime();
long d2 = System.currentTimeMillis();
long duration = (d2 - d1);
totalTime += duration;
log.i(TAG, "datetime : " + duration);
}
log.i(TAG, "Average datetime : " + ((double) totalTime/ (double) iterations));
My results showed:
datetime : 264
datetime : 0
datetime : 0
datetime : 0
datetime : 0
datetime : 0
datetime : 0
...
datetime : 0
datetime : 0
datetime : 1
datetime : 0
...
datetime : 0
datetime : 0
datetime : 0
So, the result was that the first instance was 264ms and more than 95% of the following were 0ms (I occasionally had a 1ms, but never had a value larger than 1ms).
Hope this gives a clearer picture of the cost of using Joda.
NOTE: I was using joda-time version 2.1
Using dlew/joda-time-android gradle dependency it takes only 22.82 ms (milliseconds). So I recommend you using it instead of overriding anything.
I found solution for me. I load UTC and default time zone. So it's loads very fast. And I think in this case I need catch broadcast TIME ZONE CHANGE and reload default time zone.
public class FastDateTimeZoneProvider implements Provider {
public static final Set<String> AVAILABLE_IDS = new HashSet<String>();
static {
AVAILABLE_IDS.add("UTC");
AVAILABLE_IDS.add(TimeZone.getDefault().getID());
}
public DateTimeZone getZone(String id) {
int rawOffset = 0;
if (id == null) {
return DateTimeZone.getDefault();
}
TimeZone tz = TimeZone.getTimeZone(id);
if (tz == null) {
return DateTimeZone.getDefault();
}
rawOffset = tz.getRawOffset();
//sub-optimal. could be improved to only create a new Date every few minutes
if (tz.inDaylightTime(new Date())) {
rawOffset += tz.getDSTSavings();
}
return DateTimeZone.forOffsetMillis(rawOffset);
}
public Set getAvailableIDs() {
return AVAILABLE_IDS;
}
}
This quick note to complete the answer about date4j from #Steven
I ran a quick and dirty benchmark comparing java.util.Date, jodatime and date4j on the weakest android device I have (HTC Dream/Sapphire 2.3.5).
Details : normal build (no proguard), implementing the FastDateTimeZoneProvider for jodatime.
Here's the code:
String ts = "2010-01-19T23:59:59.123456789";
long d1 = System.currentTimeMillis();
DateTime d = new DateTime(ts);
long d2 = System.currentTimeMillis();
System.err.println("datetime : " + dateUtils.durationtoString(d2 - d1));
d1 = System.currentTimeMillis();
Date dd = new Date();
d2 = System.currentTimeMillis();
System.err.println("date : " + dateUtils.durationtoString(d2 - d1));
d1 = System.currentTimeMillis();
hirondelle.date4j.DateTime ddd = new hirondelle.date4j.DateTime(ts);
d2 = System.currentTimeMillis();
System.err.println("date4j : " + dateUtils.durationtoString(d2 - d1));
Here are the results :
debug | normal
joda : 3s (3577ms) | 0s (284ms)
date : 0s (0) | 0s (0s)
date4j : 0s (55ms) | 0s (2ms)
One last thing, the jar sizes :
jodatime 2.1 : 558 kb
date4j : 35 kb
I think I'll give date4j a try.
You could also checkout Jake Wharton's JSR-310 backport of the java.time.* packages.
This library places the timezone information as a standard Android asset and provides a custom loader for parsing it efficiently. [It] offers the standard APIs in Java 8 as a much smaller package in not only binary size and method count, but also in API size.
Thus, this solution provides a smaller binary-size library with a smaller method count footprint, combined with an efficient loader for Timezone data.
As already mentioned you could use the joda-time-android library.
Do not use FastDateTimeZoneProvider proposed by #ElijahSh and #plowman. Because it is treat DST offset as standart offset for the selected timezone. As it will give "right" results for the today and for the rest of a half of a year before the next DST transition occurs. But it will defenetly give wrong result for the day before DST transition, and for the day after next DST transition.
The right way to utilize system's timezones with JodaTime:
public class AndroidDateTimeZoneProvider implements org.joda.time.tz.Provider {
#Override
public Set<String> getAvailableIDs() {
return new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs()));
}
#Override
public DateTimeZone getZone(String id) {
return id == null
? null
: id.equals("UTC")
? DateTimeZone.UTC
: Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? new AndroidNewDateTimeZone(id)
: new AndroidOldDateTimeZone(id);
}
}
Where AndroidOldDateTimeZone:
public class AndroidOldDateTimeZone extends DateTimeZone {
private final TimeZone mTz;
private final Calendar mCalendar;
private long[] mTransition;
public AndroidOldDateTimeZone(final String id) {
super(id);
mTz = TimeZone.getTimeZone(id);
mCalendar = GregorianCalendar.getInstance(mTz);
mTransition = new long[0];
try {
final Class tzClass = mTz.getClass();
final Field field = tzClass.getDeclaredField("mTransitions");
field.setAccessible(true);
final Object transitions = field.get(mTz);
if (transitions instanceof long[]) {
mTransition = (long[]) transitions;
} else if (transitions instanceof int[]) {
final int[] intArray = (int[]) transitions;
final int size = intArray.length;
mTransition = new long[size];
for (int i = 0; i < size; i++) {
mTransition[i] = intArray[i];
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public TimeZone getTz() {
return mTz;
}
#Override
public long previousTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, false);
if (index <= 0) {
return instant;
}
return mTransition[index - 1] * 1000;
}
#Override
public long nextTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, true);
if (index > mTransition.length - 2) {
return instant;
}
return mTransition[index + 1] * 1000;
}
#Override
public boolean isFixed() {
return mTransition.length > 0 &&
mCalendar.getMinimum(Calendar.DST_OFFSET) == mCalendar.getMaximum(Calendar.DST_OFFSET) &&
mCalendar.getMinimum(Calendar.ZONE_OFFSET) == mCalendar.getMaximum(Calendar.ZONE_OFFSET);
}
#Override
public boolean isStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.DST_OFFSET) == 0;
}
#Override
public int getStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.ZONE_OFFSET);
}
#Override
public int getOffset(final long instant) {
return mTz.getOffset(instant);
}
#Override
public String getShortName(final long instant, final Locale locale) {
return getName(instant, locale, true);
}
#Override
public String getName(final long instant, final Locale locale) {
return getName(instant, locale, false);
}
private String getName(final long instant, final Locale locale, final boolean isShort) {
return mTz.getDisplayName(!isStandardOffset(instant),
isShort ? TimeZone.SHORT : TimeZone.LONG,
locale == null ? Locale.getDefault() : locale);
}
#Override
public String getNameKey(final long instant) {
return null;
}
#Override
public TimeZone toTimeZone() {
return (TimeZone) mTz.clone();
}
#Override
public String toString() {
return mTz.getClass().getSimpleName();
}
#Override
public boolean equals(final Object o) {
return (o instanceof AndroidOldDateTimeZone) && mTz == ((AndroidOldDateTimeZone) o).getTz();
}
#Override
public int hashCode() {
return 31 * super.hashCode() + mTz.hashCode();
}
private long roundDownMillisToSeconds(final long millis) {
return millis < 0 ? (millis - 999) / 1000 : millis / 1000;
}
private int findTransitionIndex(final long millis, final boolean isNext) {
final long seconds = roundDownMillisToSeconds(millis);
int index = isNext ? mTransition.length : -1;
for (int i = 0; i < mTransition.length; i++) {
if (mTransition[i] == seconds) {
index = i;
}
}
return index;
}
}
The AndroidNewDateTimeZone.java same as "Old" one but based on android.icu.util.TimeZone instead.
I have created a fork of Joda Time especially for this. It loads for only ~29 ms in debug mode and ~2ms in release mode. Also it has less weight as it doesn't include timezone database.