1 /* 2 * Copyright (C) 2015 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 com.android.tv.data; 18 19 import android.support.annotation.NonNull; 20 import android.text.TextUtils; 21 import android.view.KeyEvent; 22 23 import com.android.tv.util.StringUtils; 24 25 import java.util.Objects; 26 27 /** 28 * A convenience class to handle channel number. 29 */ 30 public final class ChannelNumber implements Comparable<ChannelNumber> { 31 private static final int[] CHANNEL_DELIMITER_KEYCODES = { 32 KeyEvent.KEYCODE_MINUS, KeyEvent.KEYCODE_NUMPAD_SUBTRACT, KeyEvent.KEYCODE_PERIOD, 33 KeyEvent.KEYCODE_NUMPAD_DOT, KeyEvent.KEYCODE_SPACE 34 }; 35 36 /** The major part of the channel number. */ 37 public String majorNumber; 38 /** The flag which indicates whether it has a delimiter or not. */ 39 public boolean hasDelimiter; 40 /** The major part of the channel number. */ 41 public String minorNumber; 42 43 public ChannelNumber() { 44 reset(); 45 } 46 47 public void reset() { 48 setChannelNumber("", false, ""); 49 } 50 51 private void setChannelNumber(String majorNumber, boolean hasDelimiter, String minorNumber) { 52 this.majorNumber = majorNumber; 53 this.hasDelimiter = hasDelimiter; 54 this.minorNumber = minorNumber; 55 } 56 57 @Override 58 public String toString() { 59 if (hasDelimiter) { 60 return majorNumber + Channel.CHANNEL_NUMBER_DELIMITER + minorNumber; 61 } 62 return majorNumber; 63 } 64 65 @Override 66 public int compareTo(@NonNull ChannelNumber another) { 67 int major = Integer.parseInt(majorNumber); 68 int minor = hasDelimiter ? Integer.parseInt(minorNumber) : 0; 69 70 int opponentMajor = Integer.parseInt(another.majorNumber); 71 int opponentMinor = another.hasDelimiter 72 ? Integer.parseInt(another.minorNumber) : 0; 73 if (major == opponentMajor) { 74 return minor - opponentMinor; 75 } 76 return major - opponentMajor; 77 } 78 79 @Override 80 public boolean equals(Object obj) { 81 if (obj instanceof ChannelNumber) { 82 ChannelNumber channelNumber = (ChannelNumber) obj; 83 return TextUtils.equals(majorNumber, channelNumber.majorNumber) 84 && TextUtils.equals(minorNumber, channelNumber.minorNumber) 85 && hasDelimiter == channelNumber.hasDelimiter; 86 } 87 return super.equals(obj); 88 } 89 90 @Override 91 public int hashCode() { 92 return Objects.hash(majorNumber, hasDelimiter, minorNumber); 93 } 94 95 public static boolean isChannelNumberDelimiterKey(int keyCode) { 96 for (int delimiterKeyCode : CHANNEL_DELIMITER_KEYCODES) { 97 if (delimiterKeyCode == keyCode) { 98 return true; 99 } 100 } 101 return false; 102 } 103 104 /** 105 * Returns the ChannelNumber instance. 106 * <p> 107 * Note that all the channel number argument should be normalized by 108 * {@link Channel#normalizeDisplayNumber}. The channels retrieved from 109 * {@link ChannelDataManager} are already normalized. 110 */ 111 public static ChannelNumber parseChannelNumber(String number) { 112 if (number == null) { 113 return null; 114 } 115 ChannelNumber ret = new ChannelNumber(); 116 int indexOfDelimiter = number.indexOf(Channel.CHANNEL_NUMBER_DELIMITER); 117 if (indexOfDelimiter == 0 || indexOfDelimiter == number.length() - 1) { 118 return null; 119 } else if (indexOfDelimiter < 0) { 120 ret.majorNumber = number; 121 if (!isInteger(ret.majorNumber)) { 122 return null; 123 } 124 } else { 125 ret.hasDelimiter = true; 126 ret.majorNumber = number.substring(0, indexOfDelimiter); 127 ret.minorNumber = number.substring(indexOfDelimiter + 1); 128 if (!isInteger(ret.majorNumber) || !isInteger(ret.minorNumber)) { 129 return null; 130 } 131 } 132 return ret; 133 } 134 135 /** 136 * Compares the channel numbers. 137 * <p> 138 * Note that all the channel number arguments should be normalized by 139 * {@link Channel#normalizeDisplayNumber}. The channels retrieved from 140 * {@link ChannelDataManager} are already normalized. 141 */ 142 public static int compare(String lhs, String rhs) { 143 ChannelNumber lhsNumber = parseChannelNumber(lhs); 144 ChannelNumber rhsNumber = parseChannelNumber(rhs); 145 // Null first 146 if (lhsNumber == null && rhsNumber == null) { 147 return StringUtils.compare(lhs, rhs); 148 } else if (lhsNumber == null /* && rhsNumber != null */) { 149 return -1; 150 } else if (rhsNumber == null) { 151 return 1; 152 } 153 return lhsNumber.compareTo(rhsNumber); 154 } 155 156 private static boolean isInteger(String string) { 157 try { 158 Integer.parseInt(string); 159 } catch(NumberFormatException | NullPointerException e) { 160 return false; 161 } 162 return true; 163 } 164 } 165