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