├── .gitignore ├── README.md ├── blogs ├── articles │ ├── custom_update.md │ ├── first_not_update.md │ ├── force_update.md │ ├── ignore_update.md │ ├── manual_update.md │ └── umeng_update_best_practice.md └── images │ ├── update1.png │ ├── update2.png │ └── update3.png ├── fb └── v4.3 │ ├── res │ └── layout │ │ ├── umeng_fb_activity_contact.xml │ │ ├── umeng_fb_activity_conversation.xml │ │ ├── umeng_fb_list_header.xml │ │ ├── umeng_fb_list_item.xml │ │ └── umeng_fb_new_reply_alert_dialog.xml │ └── src │ └── com │ └── umeng │ └── fb │ ├── ContactActivity.java │ └── ConversationActivity.java └── update ├── README.md ├── default ├── README.md ├── light.png └── res │ ├── drawable │ ├── umeng_update_button_cancel_normal.xml │ ├── umeng_update_button_cancel_selector.xml │ ├── umeng_update_button_cancel_tap.xml │ ├── umeng_update_button_ok_normal.xml │ ├── umeng_update_button_ok_selector.xml │ ├── umeng_update_button_ok_tap.xml │ ├── umeng_update_dialog_bg.xml │ └── umeng_update_wifi_disable.png │ ├── layout │ └── umeng_update_dialog.xml │ ├── values-zh │ ├── umeng_common_strings.xml │ └── umeng_update_string.xml │ └── values │ ├── umeng_common_strings.xml │ └── umeng_update_string.xml ├── libs ├── armeabi-v7a │ └── libbspatch.so ├── armeabi │ └── libbspatch.so ├── mips │ └── libbspatch.so └── x86 │ └── libbspatch.so └── white-blue ├── README.md ├── res ├── drawable │ ├── umeng_update_button_cancel_normal.xml │ ├── umeng_update_button_cancel_selector.xml │ ├── umeng_update_button_cancel_tap.xml │ ├── umeng_update_button_ok_normal.xml │ ├── umeng_update_button_ok_selector.xml │ ├── umeng_update_button_ok_tap.xml │ ├── umeng_update_dialog_bg.xml │ ├── umeng_update_title_bg.xml │ └── umeng_update_wifi_disable.png ├── layout │ └── umeng_update_dialog.xml ├── values-zh │ ├── umeng_common_strings.xml │ └── umeng_update_string.xml └── values │ ├── umeng_common_strings.xml │ └── umeng_update_string.xml └── white-blue.png /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 友盟组件主题 2 | ======================= 3 | 4 | 此工程提供针对友盟 组件 的多种主题样式。 5 | 6 | 1. [双向反馈](https://github.com/ntop001/umeng-android-sdk-theme/tree/master/fb) 7 | 2. [自动更新](https://github.com/ntop001/umeng-android-sdk-theme/tree/master/update) 8 | -------------------------------------------------------------------------------- /blogs/articles/custom_update.md: -------------------------------------------------------------------------------- 1 | ## 自定义更新 2 | 3 | 使用友盟自动更新模块实现自定义更新的示例: 4 | 有时候您可能会要求使用自定义的UI方式来展示更新提示,这里给出使用自定义更新的样例,下面使用的是友盟默认的布局的实现,实际您可以根据您自己的需要进行相应的更改。 5 | 6 | 在Activity的`onCreate`方法中设置更新回调: 7 | 8 | ```java 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | ...... 13 | final Context mContext = this; 14 | // 设置禁止自动弹窗,自定义弹窗操作 15 | UmengUpdateAgent.setUpdateAutoPopup(false); 16 | // 设置更新回调,自主处理更新 17 | UmengUpdateAgent.setUpdateListener(new UmengUpdateListener() { 18 | 19 | @Override 20 | public void onUpdateReturned(int updateStatus, 21 | UpdateResponse updateInfo) { 22 | switch (updateStatus) { 23 | case UpdateStatus.Yes: // 检测到有更新 24 | showUpdateDialog(updateInfo); 25 | break; 26 | case UpdateStatus.No: // 检测到没有更新 27 | Toast.makeText(mContext, "没有更新", Toast.LENGTH_SHORT).show(); 28 | break; 29 | case UpdateStatus.NoneWifi: // 当前不是Wifi环境 30 | Toast.makeText(mContext, "没有wifi连接, 只在wifi下更新", 31 | Toast.LENGTH_SHORT).show(); 32 | break; 33 | case UpdateStatus.Timeout: // 检测超时 34 | Toast.makeText(mContext, "超时", Toast.LENGTH_SHORT).show(); 35 | break; 36 | } 37 | } 38 | }); 39 | UmengUpdateAgent.update(this); 40 | } 41 | ``` 42 | 定义方法`showUpdateDialog`展示更新提示: 43 | ```java 44 | private void showUpdateDialog(UpdateResponse updateInfo) { 45 | //如果版本已经被忽略,不弹框 46 | if (UmengUpdateAgent.isIgnore(this, updateInfo)) { 47 | return; 48 | } 49 | //获取下载完的文件,如果未下载则为null 50 | final File file = UmengUpdateAgent.downloadedFile(this, updateInfo); 51 | boolean isDownloaded = file != null; 52 | //创建更新对话框 53 | createDialog(updateInfo, isDownloaded, file).show(); 54 | } 55 | ``` 56 | 定义方法`createDialog`创建更新对话框: 57 | ```java 58 | /** 59 | * 创建更新对话框 60 | * @param updateInfo 更新信息 61 | * @param isDownloaded 是否已经下载 62 | * @param file 下载完的文件,如果未下载为null 63 | * @return 更新对话框 64 | */ 65 | private Dialog createDialog(final UpdateResponse updateInfo, 66 | boolean isDownloaded, final File file) { 67 | final boolean[] isIgnore = { false }; 68 | final int[] result = { UpdateStatus.NotNow }; 69 | final Context context = this; 70 | 71 | // 如果您的应用是全屏的,可以在这里设置Dialog也为全屏 72 | final Dialog dialog = new Dialog(context, 73 | android.R.style.Theme_Translucent_NoTitleBar); 74 | View v = LayoutInflater.from(context).inflate( 75 | R.layout.umeng_update_dialog, null); 76 | 77 | // 忽略勾选框的状态回调 78 | OnCheckedChangeListener cl = new OnCheckedChangeListener() { 79 | 80 | @Override 81 | public void onCheckedChanged(CompoundButton buttonView, 82 | boolean isChecked) { 83 | isIgnore[0] = isChecked; 84 | } 85 | }; 86 | // 点击回调,记录用户的不同选择 87 | OnClickListener ll = new OnClickListener() { 88 | 89 | @Override 90 | public void onClick(View v) { 91 | if (R.id.umeng_update_id_ok == v.getId()) { 92 | result[0] = UpdateStatus.Update; 93 | } else if (R.id.umeng_update_id_ignore == v.getId()) { 94 | result[0] = UpdateStatus.Ignore; 95 | } else if (isIgnore[0]) { 96 | result[0] = UpdateStatus.Ignore; 97 | } 98 | dialog.dismiss(); 99 | } 100 | 101 | }; 102 | // 对话框消失回调,处理用户的不同选择 103 | dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { 104 | @Override 105 | public void onDismiss(DialogInterface dialogInterface) { 106 | switch (result[0]) { 107 | case UpdateStatus.Update: 108 | // 用户选择更新 109 | if (file == null) { 110 | //开始下载 111 | UmengUpdateAgent.startDownload(context, updateInfo); 112 | } else { 113 | //开始安装 114 | UmengUpdateAgent.startInstall(context, file); 115 | } 116 | break; 117 | case UpdateStatus.Ignore: 118 | // 用户选择忽略 119 | UmengUpdateAgent.ignoreUpdate(context, updateInfo); 120 | break; 121 | case UpdateStatus.NotNow: 122 | // 用户选择取消 123 | break; 124 | } 125 | } 126 | }); 127 | 128 | // 获得网络连接服务 129 | ConnectivityManager connManager = (ConnectivityManager) context 130 | .getSystemService(Context.CONNECTIVITY_SERVICE); 131 | // 获取WIFI网络连接状态 132 | State state = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI) 133 | .getState(); 134 | // 如果正在使用WIFI网络或已经下载完成不显示无Wifi的图标 135 | int visibility = (State.CONNECTED == state) || isDownloaded ? View.GONE 136 | : View.VISIBLE; 137 | v.findViewById(R.id.umeng_update_wifi_indicator).setVisibility( 138 | visibility); 139 | 140 | v.findViewById(R.id.umeng_update_id_ok).setOnClickListener(ll); 141 | v.findViewById(R.id.umeng_update_id_cancel).setOnClickListener(ll); 142 | v.findViewById(R.id.umeng_update_id_ignore).setOnClickListener(ll); 143 | v.findViewById(R.id.umeng_update_id_close).setOnClickListener(ll); 144 | ((CheckBox) v.findViewById(R.id.umeng_update_id_check)) 145 | .setOnCheckedChangeListener(cl); 146 | // 设置对话框显示文本 147 | String content = updateContentString(updateInfo, isDownloaded); 148 | TextView tv = (TextView) v.findViewById(R.id.umeng_update_content); 149 | tv.requestFocus(); 150 | tv.setText(content); 151 | 152 | dialog.setContentView(v); 153 | 154 | return dialog; 155 | } 156 | ``` 157 | 定义方法`updateContentString`处理更新信息,转换为更新对话框显示文本: 158 | ```java 159 | /** 160 | * 根据更新信息编排对话框显示文本 161 | * 162 | * @param updateInfo 163 | * 更新信息 164 | * @param isDownloaded 165 | * 是否已经下载 166 | * @return 更新对话框显示文本 167 | */ 168 | public String updateContentString(UpdateResponse updateInfo, 169 | boolean isDownloaded) { 170 | String versionPrefix = getString(R.string.UMNewVersion); 171 | String sizePrefix = getString(R.string.UMTargetSize); 172 | String deltaPrefix = getString(R.string.UMUpdateSize); 173 | String updateLogPrefix = getString(R.string.UMUpdateContent); 174 | String installApk = getString(R.string.UMDialog_InstallAPK); 175 | // 已经下载的情况 176 | if (isDownloaded) { 177 | return String.format("%s %s\n" + "%s\n\n" + "%s\n" + "%s\n", 178 | versionPrefix, updateInfo.version, installApk, 179 | updateLogPrefix, updateInfo.updateLog); 180 | } 181 | 182 | String deltaContent; 183 | // 增量更新和全量更新的情况 184 | if (updateInfo.delta) { 185 | deltaContent = String.format("\n%s %s", deltaPrefix, 186 | getFileSizeDescription(updateInfo.size)); 187 | } else { 188 | deltaContent = ""; 189 | } 190 | // 未下载的情况 191 | return String.format("%s %s\n" + "%s %s%s\n\n" + "%s\n" + "%s\n", 192 | versionPrefix, updateInfo.version, sizePrefix, 193 | getFileSizeDescription(updateInfo.target_size), deltaContent, 194 | updateLogPrefix, updateInfo.updateLog); 195 | } 196 | ``` 197 | 定义方法`getFileSizeDescription`处理文件大小,格式化为需要显示的文本: 198 | ```java 199 | /** 200 | * 将字节数转换为用于显示的文件大小格式 201 | * 202 | * @param size 203 | * 文件字节数 204 | * @return 格式化后的文件大小文本 205 | */ 206 | public static String getFileSizeDescription(String size) { 207 | String value = ""; 208 | long bytes = 0; 209 | try { 210 | bytes = Long.valueOf(size).longValue(); 211 | } catch (NumberFormatException e) { 212 | return size; 213 | } 214 | if (bytes < 1024) { 215 | value = (int) bytes + "B"; 216 | } else if (bytes < 1048576) { 217 | DecimalFormat df = new DecimalFormat("#0.00"); 218 | value = df.format((float) bytes / 1024.0) + "K"; 219 | } else if (bytes < 1073741824) { 220 | DecimalFormat df = new DecimalFormat("#0.00"); 221 | value = df.format((float) bytes / 1048576.0) + "M"; 222 | } else { 223 | DecimalFormat df = new DecimalFormat("#0.00"); 224 | value = df.format((float) bytes / 1073741824.0) + "G"; 225 | } 226 | return value; 227 | } 228 | ``` 229 | -------------------------------------------------------------------------------- /blogs/articles/first_not_update.md: -------------------------------------------------------------------------------- 1 | ## 首次启动不更新 2 | 3 | 有的时候,开发者会希望在应用在下载后第一次启动时不要启动自动更新,这样即使下载的不是最近版本也不会在首次启动时提醒更新。 4 | 5 | 在应用程序入口`Activity`里的`OnCreate()`方法中调用 6 | ```java 7 | @Override 8 | protected void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | SharedPreferences sharedPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE); 11 | boolean isFirstStart = sharedPreferences.getBoolean(KEY_FIRST_START, true); 12 | if (isFirstStart) { 13 | sharedPreferences.edit().putBoolean(KEY_FIRST_START, false).commit(); 14 | } else { 15 | UmengUpdateAgent.update(this); 16 | } 17 | ``` 18 | 其中`SHARED_PREFERENCES_NAME`为sharedPreference文件名,`KEY_FIRST_START`为判断是否为第一次启动值的key 19 | -------------------------------------------------------------------------------- /blogs/articles/force_update.md: -------------------------------------------------------------------------------- 1 | ## 强制更新 2 | 3 | 有的时候,开发者会想要用户必须进行更新,下面给出使用友盟自动更新模块(V2.3)进行强制更新的示例: 4 | 5 | 修改`umeng_update_dialog.xml`中相关控件 6 | 7 | ```xml 8 | 20 | ``` 21 | 22 | 在其中增加`android:visibility="gone"`属性,该控件是实现以后再说功能的按钮,隐藏该按钮让dialog只显示更新的选项。 23 | 24 | 再增加关于对话框按键的监听,对于未选择更新的行为,关闭app 25 | 26 | ```java 27 | UmengUpdateAgent.setDialogListener(new UmengDialogButtonListener() { 28 | 29 | @Override 30 | public void onClick(int status) { 31 | switch (status) { 32 | case UpdateStatus.Update: 33 | break; 34 | default: 35 | //close the app 36 | MyActivity.this.finish(); 37 | } 38 | } 39 | }); 40 | ``` 41 | -------------------------------------------------------------------------------- /blogs/articles/ignore_update.md: -------------------------------------------------------------------------------- 1 | ## 忽略更新 2 | 3 | 使用友盟自动更新模块实现忽略更新的示例: 4 | 有时候对于某些更新很小的版本,用户并没有更新意愿,但是即使选择了下次再说之后,下次启动应用的时候仍然会弹窗提醒用户,友盟自动更新模块在V2.3增加了忽略更新功能,但不是默认开启的,这里给出忽略更新的使用说明。 5 | 6 | 修改`umeng_update_dialog.xml`中相关控件 7 | 8 | ```xml 9 | 19 | ``` 20 | 21 | 去除其中`android:visibility="gone"`属性,该控件是位于dialog右上角的关闭按钮,用来代替以后再说功能按钮。 22 | 23 | 修改`umeng_update_dialog.xml`中相关控件 24 | 25 | ```xml 26 | 39 | ``` 40 | 41 | 去除其中`android:visibility="gone"`属性,该控件是实现忽略更新功能的按钮。 42 | 43 | 同时修改`umeng_update_dialog.xml`中相关控件 44 | 45 | ```xml 46 | 58 | ``` 59 | 60 | 在其中增加`android:visibility="gone"`属性,该控件是实现以后再说功能的按钮,隐藏该按钮的原因是在dialog中同时显示3个按钮会显得过于拥挤,该功能可以通过点击右上角的关闭按钮或者按回退键来实现。 61 | 62 | -------------------------------------------------------------------------------- /blogs/articles/manual_update.md: -------------------------------------------------------------------------------- 1 | ## 手动更新 2 | 3 | 使用友盟自动更新模块实现手动更新的示例: 4 | 除了在进入应用的第一个页面使用自动更新检测版本更新以外,很多应用会在应用的设置页面提供手动检测更新的功能,并手动处理更新逻辑,这里以使用控件点击监听为例展示怎用使用手动更新的功能。 5 | 6 | ```java 7 | private View.OnClickListener listener = new View.OnClickListener() { 8 | public void onClick(View v) { 9 | //禁止自动弹框,手动处理相关逻辑 10 | UmengUpdateAgent.setUpdateAutoPopup(false); 11 | UmengUpdateAgent.setUpdateListener(new UmengUpdateListener() { 12 | 13 | @Override 14 | public void onUpdateReturned(int updateStatus, UpdateResponse updateInfo) { 15 | switch (updateStatus) { 16 | case 0: // has update 17 | UmengUpdateAgent.showUpdateDialog(mContext, updateInfo); 18 | break; 19 | case 1: // has no update 20 | Toast.makeText(mContext, "没有更新", Toast.LENGTH_SHORT) 21 | .show(); 22 | break; 23 | case 2: // none wifi 24 | Toast.makeText(mContext, "没有wifi连接, 只在wifi下更新", Toast.LENGTH_SHORT) 25 | .show(); 26 | break; 27 | case 3: // time out 28 | Toast.makeText(mContext, "超时", Toast.LENGTH_SHORT) 29 | .show(); 30 | break; 31 | case 4: // is updating 32 | // We show toast already, do not show again. 33 | break; 34 | } 35 | } 36 | }); 37 | //当希望手动更新可以在非wifi环境下工作,可以无视版本忽略时,使用此方法 38 | UmengUpdateAgent.forceUpdate(mContext); 39 | } 40 | }; 41 | ``` 42 | 43 | 因为这些关于更新的设置是全局的,所以如果不希望手动更新中的设置运用到自动更新中去,则可以在当前页面退出时将设置恢复成默认,或者在自动更新之前恢复设置,例如: 44 | ```java 45 | @Override 46 | protected void onStop() { 47 | super.onStop(); 48 | UmengUpdateAgent.setUpdateOnlyWifi(true); 49 | UmengUpdateAgent.setUpdateAutoPopup(true); 50 | UmengUpdateAgent.setUpdateListener(null); 51 | } 52 | ``` 53 | 或 54 | ```java 55 | @Override 56 | protected void onCreate(Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | UmengUpdateAgent.setUpdateOnlyWifi(true); 59 | UmengUpdateAgent.setUpdateAutoPopup(true); 60 | UmengUpdateAgent.setUpdateListener(null); 61 | UmengUpdateAgent.update(this); 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /blogs/articles/umeng_update_best_practice.md: -------------------------------------------------------------------------------- 1 | #如何更好的使用自动更新 2 | 3 | 更新提示的三种设计思路 4 | 5 | 1. 后台检测,弹出更新提示框 6 | 7 | 2. 在设置界面提供更新检测按钮,即时检测 8 | 9 | 3. 提前检测,在设置界面提示最新版本 10 | 11 | 12 | 这三种实现都基于友盟的自动更新服务,友盟自动更新集成文档在[*这里*](http://dev.umeng.com/doc/document_update_android.html) 13 | 14 | ## 1. 后台检测,弹出更新提示框 15 | 16 | 使用友盟统计分析服务,调用如下一行代码即可完成: 17 | 18 | ``` 19 | public void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | ... 22 | UmengUpdateAgent.update(this); 23 | ... 24 | } 25 | ``` 26 | 27 | 这行代码会链接友盟服务器,判断是否有新版应用程序,如果发现可更新的应用程序安装包,会提示用户是否更新。用户选择更新后,安装包会在后台下载自动安装更新。 28 | 29 | ## 2. 在设置界面提供更新检测按钮,即时检测 30 | 31 | 很多App都会在设置界面提供 *新版本检测* 功能,点击按钮之后会调用如下的方法即可实现。 32 | 33 | ``` 34 | public void onCheckUpdateClicked(){ 35 | final ProgressDialog dialog = new ProgressDialog(this); 36 | 37 | UmengUpdateAgent.setUpdateAutoPopup(false); 38 | UmengUpdateAgent.setUpdateListener(new UmengUpdateListener() { 39 | @Override 40 | public void onUpdateReturned(int updateStatus,UpdateResponse updateInfo) { 41 | dialog.dismiss(); 42 | 43 | switch (updateStatus) { 44 | case 0: // has update 45 | UmengUpdateAgent.showUpdateDialog(mContext, updateInfo); 46 | break; 47 | case 1: // has no update 48 | Toast.makeText(mContext, "没有更新", Toast.LENGTH_SHORT) 49 | .show(); 50 | break; 51 | case 2: // none wifi 52 | Toast.makeText(mContext, "没有wifi连接, 只在wifi下更新", Toast.LENGTH_SHORT) 53 | .show(); 54 | break; 55 | case 3: // time out 56 | Toast.makeText(mContext, "超时", Toast.LENGTH_SHORT) 57 | .show(); 58 | break; 59 | } 60 | } 61 | }); 62 | 63 | UmengUpdateAgent.update(this); 64 | dialog.show(); 65 | } 66 | ``` 67 | 68 | ## 3. 提前检测,在设置界面提示最新版本 69 | 70 | 这种方式或许更友好一点,第一种方式频繁的给用户提示新版本不是很友好,第二种方式又过于被动,很多情况下用户不会主动检测新版本,如果在设置界面就告诉用户最新版本或许是一种折中的做法。 71 | 72 | #### 1. 需要先写两个工具方法,记录和恢复更新状态 73 | 74 | ``` 75 | private void saveResponse(Context context,UpdateResponse updateInfo){ 76 | SharedPreferences update = context.getSharedPreferences("um_update_info", MODE_PRIVATE); 77 | if( update != null ) { 78 | update.edit().putString("serial_update_info", updateInfo.toString()).commit(); 79 | } 80 | } 81 | 82 | private UpdateResponse parseResponse(Context context){ 83 | 84 | SharedPreferences update = context.getSharedPreferences("um_update_info", MODE_PRIVATE); 85 | JSONObject json = null; 86 | 87 | if(update != null ){ 88 | try{ 89 | json = new JSONObject(update.getString("serial_update_info", null)); 90 | }catch(Exception e){ 91 | e.printStackTrace(); 92 | } 93 | } 94 | 95 | return new UpdateResponse( json ); 96 | } 97 | ``` 98 | 99 | #### 2. 在程序入口调用如下方法,检测新版,并保存更新状态 100 | 101 | ``` 102 | public void checkUpdate(){ 103 | UmengUpdateAgent.setUpdateAutoPopup(false); 104 | UmengUpdateAgent.setUpdateListener(new UmengUpdateListener() { 105 | @Override 106 | public void onUpdateReturned(int updateStatus,UpdateResponse updateInfo) { 107 | dialog.dismiss(); 108 | 109 | switch (updateStatus) { 110 | case 0: // has update 111 | case 1: // has no update 112 | saveResponse( mContext , updateInfo ); 113 | break; 114 | case 2: // none wifi 115 | Toast.makeText(mContext, "没有wifi连接, 只在wifi下更新", Toast.LENGTH_SHORT) 116 | .show(); 117 | break; 118 | case 3: // time out 119 | Toast.makeText(mContext, "超时", Toast.LENGTH_SHORT) 120 | .show(); 121 | break; 122 | } 123 | } 124 | }); 125 | 126 | UmengUpdateAgent.update(this); 127 | } 128 | 129 | ``` 130 | 131 | #### 3. 在打开设置页面的时候,读取缓存的状态,提示用户 132 | 133 | 134 | ``` 135 | UpdateResponse updateInfo = parseResponse(mContext); 136 | 137 | if( updateInfo.hasUpdate) { 138 | UmengUpdateAgent.showUpdateDialog( mContext, updateInfo ); 139 | } 140 | 141 | // 或者直接强制更新: 142 | // if( updateInfo.hasUpdate) { 143 | // UmengUpdateAgent.startDownload(mContext, updateInfo); 144 | // } 145 | 146 | ``` 147 | 148 | ### 4.其他小技巧 149 | 150 | 默认总是在程序启动的时候检测版本,浪费流量,那么加一些自己的小策略,比如按一定时间间隔更新: 151 | 152 | ``` 153 | private static final String KEY_LAST_UPDATE_TIME = "umeng_last_update_time"; 154 | 155 | /** 156 | * 自动更新,在main activity 中调用,此方法会请求服务器,检查是否有最新版本 157 | * 158 | * @param context 159 | * 当前Activity 160 | * @param internal 161 | * 控制自动更新请求的频率,单位毫秒,eg:update(context,24*60*60*1000) ,每天更新一次 162 | */ 163 | public static void update(Context context, final long internal) { 164 | final Context mContext = context; 165 | if (mContext == null) { 166 | Log.i(TAG, "unexpected null Context"); 167 | return; 168 | } 169 | 170 | SharedPreferences preference = getUpdateSettingPreferences(mContext); 171 | long lastUpdateTime = preference.getLong(KEY_LAST_UPDATE_TIME, 0); 172 | long now = System.currentTimeMillis(); 173 | 174 | if ((now - lastUpdateTime) > internal) { 175 | update(mContext); 176 | preference.edit().putLong(KEY_LAST_UPDATE_TIME, now).commit(); 177 | } 178 | } 179 | ``` 180 | 181 | 这样调用 `update( mContext, 24*60*60*1000 );` 就可以实现按天更新。 182 | 183 | 给自动更新对话框添加更新的逻辑,默认两个按钮 “立即更新” “以后再说”, 我想把以后再说改成 “不再提示” 怎么办 ? 184 | 185 | 修改对应的 xml 文件如下: 186 | 187 | ``` 188 | 189 | 190 | 198 | 207 | 215 | 216 | ``` 217 | 218 | 上面的修改首先隐藏了原来的更新提示按钮(注意只能隐藏,不能删除),然后添加一个新的按钮,回调函数是 `onButtonClick` , 这样在调用update 的 Activity 地方实现回调函数就可以了。 219 | 220 | ``` 221 | public void onButtonClick(View v){ 222 | // 这里只是演示,需要开发者按需求处理新的逻辑, 223 | Log.i("--->", "onButtonClick"); 224 | } 225 | ``` 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /blogs/images/update1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/blogs/images/update1.png -------------------------------------------------------------------------------- /blogs/images/update2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/blogs/images/update2.png -------------------------------------------------------------------------------- /blogs/images/update3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/blogs/images/update3.png -------------------------------------------------------------------------------- /fb/v4.3/res/layout/umeng_fb_activity_contact.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 15 | 23 | 24 | 31 | 32 | 40 | 41 | 42 | 47 | 48 | 59 | 60 | 61 | 72 | 73 | -------------------------------------------------------------------------------- /fb/v4.3/res/layout/umeng_fb_activity_conversation.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 15 | 29 | 30 | 37 | 38 | 39 | 45 | 46 | 55 | 56 | 63 | 64 | 65 | 71 | 72 | 86 | 87 | 88 | 95 | 96 | 109 | 110 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /fb/v4.3/res/layout/umeng_fb_list_header.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 21 | 22 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /fb/v4.3/res/layout/umeng_fb_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 26 | 27 | 33 | 34 | 40 | 41 | 42 | 46 | 47 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /fb/v4.3/res/layout/umeng_fb_new_reply_alert_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /fb/v4.3/src/com/umeng/fb/ContactActivity.java: -------------------------------------------------------------------------------- 1 | package com.umeng.fb; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.app.Activity; 10 | import android.content.Context; 11 | import android.os.Bundle; 12 | import android.view.View; 13 | import android.view.View.OnClickListener; 14 | import android.view.inputmethod.InputMethodManager; 15 | import android.widget.EditText; 16 | import android.widget.ImageView; 17 | import android.widget.TextView; 18 | 19 | import com.umeng.fb.model.UserInfo; 20 | 21 | /** 22 | * Activity for user to fill plain messy contact information. Developers are 23 | * encouraged to implement their own activities by following this example using 24 | * the API provided by SDK. 25 | * 26 | * @author lucas 27 | * 28 | */ 29 | public class ContactActivity extends Activity { 30 | 31 | /** 32 | * The predefined key used by Umeng Feedback SDK to store non-structural 33 | * plain text messy contact info. Info can be retrieved from 34 | * {@link UserInfo#getContact()} with key 35 | * {@link #KEY_UMENG_CONTACT_INFO_PLAIN_TEXT}. 36 | * This key is reserved by Umeng. Third party developers DO NOT USE this 37 | * key. 38 | */ 39 | private static final String KEY_UMENG_CONTACT_INFO_PLAIN_TEXT = "plain"; 40 | 41 | private ImageView backBtn; 42 | private ImageView saveBtn; 43 | 44 | private EditText contactInfoEdit; 45 | private FeedbackAgent agent; 46 | 47 | private TextView lastUpdateAtText; 48 | 49 | @Override 50 | protected void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | setContentView(com.umeng.fb.res.LayoutMapper 53 | .umeng_fb_activity_contact(this)); 54 | agent = new FeedbackAgent(this); 55 | 56 | backBtn = (ImageView) this.findViewById(com.umeng.fb.res.IdMapper 57 | .umeng_fb_back(this)); 58 | saveBtn = (ImageView) this.findViewById(com.umeng.fb.res.IdMapper 59 | .umeng_fb_save(this)); 60 | contactInfoEdit = (EditText) this 61 | .findViewById(com.umeng.fb.res.IdMapper 62 | .umeng_fb_contact_info(this)); 63 | lastUpdateAtText = (TextView) this 64 | .findViewById(com.umeng.fb.res.IdMapper 65 | .umeng_fb_contact_update_at(this)); 66 | 67 | try { 68 | String contact_info = agent.getUserInfo().getContact() 69 | .get(KEY_UMENG_CONTACT_INFO_PLAIN_TEXT); 70 | contactInfoEdit.setText(contact_info); 71 | 72 | long time = agent.getUserInfoLastUpdateAt(); 73 | 74 | if (time > 0) { 75 | Date date = new Date(time); 76 | String prefix = this.getResources().getString( 77 | com.umeng.fb.res.StringMapper 78 | .umeng_fb_contact_update_at(this)); 79 | lastUpdateAtText.setText(prefix 80 | + SimpleDateFormat.getDateTimeInstance().format(date)); 81 | lastUpdateAtText.setVisibility(View.VISIBLE); 82 | } else { 83 | lastUpdateAtText.setVisibility(View.GONE); 84 | } 85 | 86 | // If user has never entered any contact information, request focus 87 | // on the edittext on startup. 88 | if (com.umeng.common.util.Helper.isEmpty(contact_info)) { 89 | contactInfoEdit.requestFocus(); 90 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 91 | if (imm != null) 92 | imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); 93 | } 94 | 95 | } catch (NullPointerException e) { 96 | e.printStackTrace(); 97 | } 98 | 99 | backBtn.setOnClickListener(new OnClickListener() { 100 | @Override 101 | public void onClick(View v) { 102 | back(); 103 | } 104 | 105 | }); 106 | saveBtn.setOnClickListener(new OnClickListener() { 107 | @Override 108 | public void onClick(View v) { 109 | try { 110 | UserInfo info = agent.getUserInfo(); 111 | if (info == null) 112 | info = new UserInfo(); 113 | Map contact = info.getContact(); 114 | if (contact == null) 115 | contact = new HashMap(); 116 | String contact_info = contactInfoEdit.getEditableText() 117 | .toString(); 118 | contact.put(KEY_UMENG_CONTACT_INFO_PLAIN_TEXT, contact_info); 119 | info.setContact(contact); 120 | 121 | 122 | // Map remark = info.getRemark(); 123 | // if (remark == null) 124 | // remark = new HashMap(); 125 | // remark.put("tag1", "game"); 126 | // info.setRemark(remark); 127 | 128 | agent.setUserInfo(info); 129 | 130 | } catch (Exception e) { 131 | e.printStackTrace(); 132 | } 133 | back(); 134 | } 135 | 136 | }); 137 | } 138 | 139 | @SuppressLint("NewApi") 140 | void back() { 141 | ContactActivity.this.finish(); 142 | 143 | // add transition animation for exit. 144 | if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.DONUT) { 145 | overridePendingTransition( 146 | com.umeng.fb.res.AnimMapper 147 | .umeng_fb_slide_in_from_left(ContactActivity.this), 148 | com.umeng.fb.res.AnimMapper 149 | .umeng_fb_slide_out_from_right(ContactActivity.this)); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /fb/v4.3/src/com/umeng/fb/ConversationActivity.java: -------------------------------------------------------------------------------- 1 | package com.umeng.example.fb; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.text.SimpleDateFormat; 5 | import java.util.List; 6 | 7 | import android.annotation.SuppressLint; 8 | import android.app.Activity; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.os.Bundle; 12 | import android.text.TextUtils; 13 | import android.util.Log; 14 | import android.view.LayoutInflater; 15 | import android.view.MotionEvent; 16 | import android.view.View; 17 | import android.view.View.MeasureSpec; 18 | import android.view.View.OnClickListener; 19 | import android.view.View.OnTouchListener; 20 | import android.view.ViewGroup; 21 | import android.view.inputmethod.InputMethodManager; 22 | import android.widget.AbsListView; 23 | import android.widget.AbsListView.OnScrollListener; 24 | import android.widget.BaseAdapter; 25 | import android.widget.EditText; 26 | import android.widget.ListView; 27 | import android.widget.RelativeLayout; 28 | import android.widget.TextView; 29 | 30 | import com.umeng.example.R; 31 | import com.umeng.fb.ContactActivity; 32 | import com.umeng.fb.FeedbackAgent; 33 | import com.umeng.fb.model.Conversation; 34 | import com.umeng.fb.model.DevReply; 35 | import com.umeng.fb.model.Reply; 36 | 37 | public class ConversationActivity extends Activity { 38 | private static final String TAG = ConversationActivity.class.getName(); 39 | private FeedbackAgent agent; 40 | private Conversation defaultConversation; 41 | private ReplyListAdapter adapter; 42 | private ListView replyListView; 43 | RelativeLayout header; 44 | int headerHeight; 45 | int headerPaddingOriginal; 46 | EditText userReplyContentEdit; 47 | private int mLastMotionY; 48 | 49 | @Override 50 | protected void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | 53 | setContentView(R.layout.umeng_fb_activity_conversation); 54 | try { 55 | agent = new FeedbackAgent(this); 56 | defaultConversation = agent.getDefaultConversation(); 57 | 58 | replyListView = (ListView) findViewById(R.id.umeng_fb_reply_list); 59 | 60 | setListViewHeader(); 61 | 62 | adapter = new ReplyListAdapter(this); 63 | replyListView.setAdapter(adapter); 64 | 65 | // sync up the conversations on Activity start up. 66 | sync(); 67 | 68 | // contact info entry 69 | View contact_entry = findViewById(R.id.umeng_fb_conversation_contact_entry); 70 | 71 | contact_entry.setOnClickListener(new OnClickListener() { 72 | 73 | @SuppressLint("NewApi") 74 | @Override 75 | public void onClick(View v) { 76 | Intent intent = new Intent(); 77 | intent.setClass(ConversationActivity.this, 78 | ContactActivity.class); 79 | startActivity(intent); 80 | 81 | // play an Activity exit and entrance animation. 82 | // play the trick: 83 | // http://stackoverflow.com/questions/6495007/verifyerror-deploying-on-api-1-6 84 | if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.DONUT) { 85 | overridePendingTransition( 86 | R.anim.umeng_fb_slide_in_from_right, 87 | R.anim.umeng_fb_slide_out_from_left); 88 | 89 | } 90 | } 91 | 92 | }); 93 | 94 | if (agent.getUserInfoLastUpdateAt() > 0) 95 | contact_entry.setVisibility(View.GONE); 96 | 97 | findViewById((R.id.umeng_fb_back)).setOnClickListener( 98 | new OnClickListener() { 99 | 100 | @Override 101 | public void onClick(View v) { 102 | finish(); 103 | } 104 | }); 105 | 106 | userReplyContentEdit = (EditText) findViewById(R.id.umeng_fb_reply_content); 107 | 108 | findViewById(R.id.umeng_fb_send).setOnClickListener( 109 | new OnClickListener() { 110 | @Override 111 | public void onClick(View v) { 112 | 113 | String content = userReplyContentEdit 114 | .getEditableText().toString().trim(); 115 | if (TextUtils.isEmpty(content)) 116 | return; 117 | 118 | userReplyContentEdit.getEditableText().clear(); 119 | 120 | defaultConversation.addUserReply(content); 121 | // adapter.notifyDataSetChanged(); 122 | 123 | // scoll to the end of listview after updating the 124 | // conversation. 125 | // replyList.setSelection(adapter.getCount()-1); 126 | 127 | sync(); 128 | 129 | // hide soft input window after sending. 130 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 131 | if (imm != null) 132 | imm.hideSoftInputFromWindow( 133 | userReplyContentEdit.getWindowToken(), 134 | 0); 135 | } 136 | }); 137 | } catch (Exception e) { 138 | e.printStackTrace(); 139 | this.finish(); 140 | } 141 | 142 | } 143 | 144 | private void setListViewHeader() { 145 | LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); 146 | header = (RelativeLayout) inflater.inflate( 147 | R.layout.umeng_fb_list_header, replyListView, false); 148 | 149 | replyListView.addHeaderView(header); 150 | measureView(header); 151 | headerHeight = header.getMeasuredHeight(); 152 | headerPaddingOriginal = header.getPaddingTop(); 153 | 154 | header.setPadding(header.getPaddingLeft(), -headerHeight, 155 | header.getPaddingRight(), header.getPaddingBottom()); 156 | header.setVisibility(View.GONE); 157 | 158 | replyListView.setOnTouchListener(new OnTouchListener() { 159 | 160 | @Override 161 | public boolean onTouch(View v, MotionEvent event) { 162 | // if there is no element in the listview except the header, do 163 | // nothing. Do not show the header. 164 | if (replyListView.getAdapter().getCount() < 2) 165 | return false; 166 | switch (event.getAction()) { 167 | case MotionEvent.ACTION_UP: 168 | if (replyListView.getFirstVisiblePosition() == 0) { 169 | if ((header.getBottom() >= headerHeight + 20 || header 170 | .getTop() > 0)) { 171 | header.setVisibility(View.VISIBLE); 172 | header.setPadding(header.getPaddingLeft(), 173 | headerPaddingOriginal, 174 | header.getPaddingRight(), 175 | header.getPaddingBottom()); 176 | } else // if (header.getBottom() < headerHeight + 20 || 177 | // header.getTop() <= 0) 178 | { 179 | replyListView.setSelection(1); 180 | header.setVisibility(View.GONE); 181 | header.setPadding(header.getPaddingLeft(), 182 | -headerHeight, header.getPaddingRight(), 183 | header.getPaddingBottom()); 184 | } 185 | } 186 | break; 187 | case MotionEvent.ACTION_DOWN: 188 | mLastMotionY = (int) event.getY(); 189 | // header.setVisibility(View.VISIBLE); 190 | break; 191 | case MotionEvent.ACTION_MOVE: 192 | applyHeaderPadding(event); 193 | break; 194 | 195 | } 196 | return false; 197 | } 198 | 199 | private void applyHeaderPadding(MotionEvent ev) { 200 | // getHistorySize has been available since API 1 201 | int pointerCount = ev.getHistorySize(); 202 | 203 | for (int p = 0; p < pointerCount; p++) { 204 | if (replyListView.getFirstVisiblePosition() == 0) { 205 | int historicalY = (int) ev.getHistoricalY(p); 206 | 207 | // Calculate the padding to apply, we divide by 1.7 to 208 | // simulate a more resistant effect during pull. 209 | int topPadding = (int) (((historicalY - mLastMotionY) - headerHeight) / 1.7); 210 | 211 | header.setVisibility(View.VISIBLE); 212 | header.setPadding(header.getPaddingLeft(), topPadding, 213 | header.getPaddingRight(), 214 | header.getPaddingBottom()); 215 | } 216 | } 217 | } 218 | 219 | }); 220 | 221 | replyListView.setOnScrollListener(new OnScrollListener() { 222 | private int mScrollState; 223 | 224 | @Override 225 | public void onScroll(AbsListView view, int firstVisibleItem, 226 | int visibleItemCount, int totalItemCount) { 227 | if (mScrollState == OnScrollListener.SCROLL_STATE_FLING 228 | && firstVisibleItem == 0) { 229 | // replyListView.setSelection(1); 230 | } 231 | } 232 | 233 | @Override 234 | public void onScrollStateChanged(AbsListView view, int scrollState) { 235 | mScrollState = scrollState; 236 | 237 | } 238 | 239 | }); 240 | } 241 | 242 | private void measureView(View child) { 243 | ViewGroup.LayoutParams p = child.getLayoutParams(); 244 | if (p == null) { 245 | p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 246 | ViewGroup.LayoutParams.WRAP_CONTENT); 247 | } 248 | 249 | int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width); 250 | int lpHeight = p.height; 251 | int childHeightSpec; 252 | if (lpHeight > 0) { 253 | childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, 254 | MeasureSpec.EXACTLY); 255 | } else { 256 | childHeightSpec = MeasureSpec.makeMeasureSpec(0, 257 | MeasureSpec.UNSPECIFIED); 258 | } 259 | child.measure(childWidthSpec, childHeightSpec); 260 | } 261 | 262 | void sync() { 263 | Conversation.SyncListener listener = new Conversation.SyncListener() { 264 | 265 | @Override 266 | public void onSendUserReply(List replyList) { 267 | adapter.notifyDataSetChanged(); 268 | } 269 | 270 | @Override 271 | public void onReceiveDevReply(List replyList) { 272 | } 273 | }; 274 | defaultConversation.sync(listener); 275 | } 276 | 277 | class ReplyListAdapter extends BaseAdapter { 278 | Context mContext; 279 | LayoutInflater mInflater; 280 | 281 | public ReplyListAdapter(Context context) { 282 | this.mContext = context; 283 | mInflater = LayoutInflater.from(mContext); 284 | } 285 | 286 | /** 287 | * 288 | * @param content 289 | * 提示用户的内容 290 | * @return 291 | */ 292 | private DevReply createInitDevReply(String content) { 293 | 294 | try { 295 | Class c = com.umeng.fb.model.DevReply.class; 296 | Constructor constructor = c.getDeclaredConstructor(new Class[] { 297 | String.class, String.class, String.class, String.class, 298 | String.class }); 299 | constructor.setAccessible(true); 300 | DevReply devReply = (DevReply) constructor 301 | .newInstance(new Object[] { content, "appkey", 302 | "userid", "feedback_id", "user_name" }); 303 | return devReply; 304 | } catch (Exception e) { 305 | e.printStackTrace(); 306 | } 307 | 308 | return null; 309 | } 310 | 311 | @Override 312 | public int getCount() { 313 | List replyList = defaultConversation.getReplyList(); 314 | return (replyList == null) ? 1 : replyList.size() + 1; 315 | } 316 | 317 | /* 318 | * (non-Javadoc) 319 | * 320 | * @see android.widget.Adapter#getView(int, android.view.View, 321 | * android.view.ViewGroup) 322 | */ 323 | @Override 324 | public View getView(int position, View convertView, ViewGroup parent) { 325 | ViewHolder holder; 326 | if (convertView == null) { 327 | convertView = mInflater.inflate(R.layout.umeng_fb_list_item, 328 | null); 329 | 330 | holder = new ViewHolder(); 331 | 332 | holder.replyDate = (TextView) convertView 333 | .findViewById(R.id.umeng_fb_reply_date); 334 | 335 | holder.replyContent = (TextView) convertView 336 | .findViewById(R.id.umeng_fb_reply_content); 337 | 338 | convertView.setTag(holder); 339 | } else { 340 | holder = (ViewHolder) convertView.getTag(); 341 | } 342 | 343 | Reply reply; 344 | if (position == 0) { 345 | // 自定义提示内容 346 | reply = createInitDevReply("自定义提示内容"); 347 | } else { 348 | reply = defaultConversation.getReplyList().get(position - 1); 349 | } 350 | 351 | RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams( 352 | RelativeLayout.LayoutParams.WRAP_CONTENT, 353 | RelativeLayout.LayoutParams.WRAP_CONTENT); 354 | 355 | if (reply instanceof DevReply) { 356 | layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); // ALIGN_PARENT_RIGHT 357 | holder.replyContent.setLayoutParams(layoutParams); 358 | 359 | // set bg after layout 360 | holder.replyContent 361 | .setBackgroundResource(R.drawable.umeng_fb_reply_left_bg); 362 | } else { 363 | layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); // ALIGN_PARENT_RIGHT 364 | // layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_LEFT); 365 | holder.replyContent.setLayoutParams(layoutParams); 366 | holder.replyContent 367 | .setBackgroundResource(R.drawable.umeng_fb_reply_right_bg); 368 | } 369 | 370 | holder.replyDate.setText(SimpleDateFormat.getDateTimeInstance() 371 | .format(reply.getDatetime())); 372 | holder.replyContent.setText(reply.getContent()); 373 | 374 | return convertView; 375 | } 376 | 377 | /* 378 | * (non-Javadoc) 379 | * 380 | * @see android.widget.Adapter#getItem(int) 381 | */ 382 | @Override 383 | public Object getItem(int position) { 384 | return defaultConversation.getReplyList().get(position); 385 | } 386 | 387 | /* 388 | * (non-Javadoc) 389 | * 390 | * @see android.widget.Adapter#getItemId(int) 391 | */ 392 | @Override 393 | public long getItemId(int position) { 394 | return position; 395 | } 396 | 397 | class ViewHolder { 398 | TextView replyDate; 399 | TextView replyContent; 400 | 401 | } 402 | } 403 | 404 | } 405 | -------------------------------------------------------------------------------- /update/README.md: -------------------------------------------------------------------------------- 1 | ## 自定义自动更新对话框UI 2 | 3 | ### 默认效果图如下: 4 | 5 | 6 | 7 | ### 结构 8 | 9 | 自动更新需要的默认资源结构如下: 10 | 11 | ``` 12 | res - drawable - umeng_update_button_cancel_normal.xml 13 | - umeng_update_button_cancel_tap.xml 14 | - umeng_update_button_ok_normal.xml 15 | - umeng_update_button_ok_tap.xml 16 | - umeng_update_button_cancel_selector.xml 17 | - umeng_update_button_ok_selector.xml 18 | - umeng_update_title_bg.xml 19 | - umeng_update_dialog_bg.xml 20 | - umeng_update_wifi_disable.png 21 | 22 | - layout - umeng_update_dialog.xml 23 | 24 | - values - umeng_common_strings.xml 25 | - umeng_update_string,xml 26 | - values-zh - umeng_common_strings.xml 27 | - umeng_update_string,xml 28 | ``` 29 | 30 | 1. `drawable` 路径下定义了按钮和背景所需的图片 31 | 2. `layout` 路径是更新对话框显示的布局 32 | 3. `values` 路径下是显示的字符串资源, 这些都是可以直接修改的。 33 | 34 | 35 | ### 固定ID资源 36 | 37 | `umeng_update_dialog.xml` 中定义的 如下资源类型和ID 是不可以修改的: 38 | 39 | 40 | 41 | 资源类型 42 | ID 43 | 用途 44 | 45 | 46 | 47 | ImageView 48 | umeng_update_wifi_indicator 49 | 显示当前Wifi是否可用 50 | 51 | 52 | TextView 53 | umeng_update_content 54 | 显示更新内容 55 | 56 | 57 | 58 | Button 59 | umeng_update_id_ok 60 | 确定按钮 61 | 62 | 63 | 64 | Button 65 | umeng_update_id_cancel 66 | 取消按钮 67 | 68 | 69 | 70 | 71 | `values`路径下定义的字符串资源 `name` 属性不可更改,值可以随意修改. 72 | `drawable` 路径下的图片资源,在`SDK`代码中没有直接引用,可以直接更换图片内容 73 | (如果修改名称需要同时修改其他 `xml` 中的引用,一般不要这么做) 74 | 75 | 76 | 77 | ### 生成对话框 78 | SDK 在显示对话框之前会做如下步骤的操作: 79 | 80 | 1. 找到 `umeng_update_dialog.xml` 作为对话框的内部 `View` 81 | 2. 找到 `umeng_update_wifi_indicator` 根据当前wifi状态设置是否可见 82 | 3. 找到 `umeng_update_content` 文本区设置更新日志 83 | 4. 分别找到 `umeng_update_id_ok` 和 `umeng_update_id_cancel` 按钮设置监听 84 | 5. 显示对话框 85 | 86 | ### 如何自定义UI 87 | 88 | 通过上面的描述,应该可以清楚的自定义弹出对话框的样式, 通过 `umeng_update_dialog.xml` 修改对话框布局样式即可, 除了固定ID 89 | 的资源类型和Id不可变动外, 90 | 91 | 92 | 下面链接是我们提供的一些不同风格的UI: 93 | 94 | #### 1. [默认](https://github.com/ntop001/umeng-android-sdk-theme/tree/master/update/default) 95 | 96 | #### 2. [蓝色](https://github.com/ntop001/umeng-android-sdk-theme/tree/master/update/white-blue) 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /update/default/README.md: -------------------------------------------------------------------------------- 1 | ### 效果图 2 | 3 |  4 | 5 | ### 结构 6 | 7 | 自动更新需要的默认资源结构如下: 8 | 9 | ``` 10 | res - drawable - umeng_update_button_cancel_normal.xml 11 | - umeng_update_button_cancel_tap.xml 12 | - umeng_update_button_ok_normal.xml 13 | - umeng_update_button_ok_tap.xml 14 | - umeng_update_button_cancel_selector.xml 15 | - umeng_update_button_ok_selector.xml 16 | - umeng_update_title_bg.xml 17 | - umeng_update_dialog_bg.xml 18 | - umeng_update_wifi_disable.png 19 | 20 | - layout - umeng_update_dialog.xml 21 | 22 | - values - umeng_common_strings.xml 23 | - umeng_update_string,xml 24 | - values-zh - umeng_common_strings.xml 25 | - umeng_update_string,xml 26 | ``` 27 | 28 | 1. `drawable` 路径下定义了按钮和背景所需的图片 29 | 2. `layout` 路径是更新对话框显示的布局 30 | 3. `values` 路径下是显示的字符串资源, 这些都是可以直接修改的。 31 | -------------------------------------------------------------------------------- /update/default/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/default/light.png -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_button_cancel_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_button_cancel_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_button_cancel_tap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_button_ok_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_button_ok_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_button_ok_tap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_dialog_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /update/default/res/drawable/umeng_update_wifi_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/default/res/drawable/umeng_update_wifi_disable.png -------------------------------------------------------------------------------- /update/default/res/layout/umeng_update_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 13 | 14 | 21 | 22 | 30 | 31 | 32 | 33 | 36 | 37 | 40 | 41 | 48 | 49 | 50 | 51 | 52 | 60 | 61 | 69 | 70 | -------------------------------------------------------------------------------- /update/default/res/values-zh/umeng_common_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 正在下载中. 4 | 下载出错啦,请检查网络后继续。 5 | 暂停 6 | 继续 7 | 取消 8 | 正在下载: 9 | 请连接网络后再尝试! 10 | 下载失败 11 | 12 | -------------------------------------------------------------------------------- /update/default/res/values-zh/umeng_update_string.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 未联网 4 | 发现新版本 5 | 最新版本: 6 | 更新内容 7 | 仅需要下载: 8 | 新版本大小: 9 | (提示:非WIFI环境) 10 | 立即更新 11 | 应用更新 12 | 以后再说 13 | 正在更新中.... 14 | 最新版本已下载,是否安装? 15 | 16 | -------------------------------------------------------------------------------- /update/default/res/values/umeng_common_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The app is already in downloading list. 5 | dowload interrupted. 6 | Pause 7 | Continue 8 | Cancel 9 | Downloading: 10 | Please make sure you are connected to internet, download failed 11 | Download Failed! 12 | -------------------------------------------------------------------------------- /update/default/res/values/umeng_update_string.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Please make sure you are connected to internet,update failed 5 | New version found 6 | Latest version: 7 | Update Content 8 | Update size: 9 | Target size: 10 | (Warning: Not WIFI Condition) 11 | Update now 12 | App updating 13 | Not now 14 | Updating.... 15 | The lastest version has been downloaded, install now ? 16 | 17 | -------------------------------------------------------------------------------- /update/libs/armeabi-v7a/libbspatch.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/libs/armeabi-v7a/libbspatch.so -------------------------------------------------------------------------------- /update/libs/armeabi/libbspatch.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/libs/armeabi/libbspatch.so -------------------------------------------------------------------------------- /update/libs/mips/libbspatch.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/libs/mips/libbspatch.so -------------------------------------------------------------------------------- /update/libs/x86/libbspatch.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/libs/x86/libbspatch.so -------------------------------------------------------------------------------- /update/white-blue/README.md: -------------------------------------------------------------------------------- 1 | ### 效果图 2 | 3 |  4 | 5 | ### 结构 6 | 7 | 自动更新需要的默认资源结构如下: 8 | 9 | ``` 10 | res - drawable - umeng_update_button_cancel_normal.xml 11 | - umeng_update_button_cancel_tap.xml 12 | - umeng_update_button_ok_normal.xml 13 | - umeng_update_button_ok_tap.xml 14 | - umeng_update_button_cancel_selector.xml 15 | - umeng_update_button_ok_selector.xml 16 | - umeng_update_title_bg.xml 17 | - umeng_update_dialog_bg.xml 18 | - umeng_update_wifi_disable.png 19 | 20 | - layout - umeng_update_dialog.xml 21 | 22 | - values - umeng_common_strings.xml 23 | - umeng_update_string,xml 24 | - values-zh - umeng_common_strings.xml 25 | - umeng_update_string,xml 26 | ``` 27 | 28 | 1. `drawable` 路径下定义了按钮和背景所需的图片 29 | 2. `layout` 路径是更新对话框显示的布局 30 | 3. `values` 路径下是显示的字符串资源, 这些都是可以直接修改的。 31 | 32 | ### 修改默认实现达到上述效果 33 | 34 | #### 1. 修改布局文件 35 | 36 | 布局文件默认实现如下, 分成 `Title`, `SplitView` , `Content` , `Ok&Cancel Button` 三部分. 37 | 38 | ``` 39 | 40 | 45 | 46 | 47 | 51 | 52 | 59 | 60 | 68 | 69 | 70 | 71 | 74 | 75 | 78 | 79 | 86 | 87 | 88 | 89 | 90 | 98 | 99 | 107 | 108 | 109 | ``` 110 | 111 | 为了实现途中效果,需要: 112 | 113 | 1. 删除 `SplitView` 横划线 114 | 2. 修改 `Title` 背景色 115 | 3. 修改整个 `layout` 背景色 116 | 4. 修改 `Button` 风格 117 | 118 | 119 | 分别修改上面四个部分,得到新的 xml 如下: 120 | 121 | `umeng_update_dialog.xml` 仅仅删除了 `SplitView` ,并给 `Title` 部分添加了背景 122 | 123 | ``` 124 | 125 | 130 | 131 | 132 | 137 | 138 | 145 | 146 | 154 | 155 | 156 | 157 | 160 | 161 | 168 | 169 | 170 | 171 | 172 | 180 | 181 | 189 | 190 | 191 | ``` 192 | 193 | `umeng_update_title_bg.xml` 修改 `Title` 背景色为蓝色, 圆角修改的略小一些: 194 | 195 | ``` 196 | 197 | 198 | 200 | 201 | 202 | ``` 203 | 204 | `umeng_update_dialog_bg.xml` 修改背景的圆角更小一些: 205 | 206 | ``` 207 | 208 | 209 | 210 | 211 | 212 | ``` 213 | 214 | 修改 `Button` 风格,`Button` 涉及文件较多,分别如下: 215 | 216 | 1. `umeng_update_button_cancel_normal.xml` 取消按钮正常状态 217 | 2. `umeng_update_button_cancel_tap.xml` 取消按钮点击状态 218 | 3. `umeng_update_button_cancel_selector.xml` 取消按钮背景 `state selector` 219 | 4. `umeng_update_button_ok_normal.xml` 确认按钮正常状态 220 | 5. `umeng_update_button_ok_tap.xml` 确认按钮点击状态 221 | 6. `umeng_update_button_ok_selector.xml` 确认按钮背景 `state selector` 222 | 223 | 分别按照图中样式修改即可,代码见 `res\drawable\` 此处不再重复粘贴。 224 | 225 | 226 | 完成上面四步,就可以从默认效果转化成现在的样子,作为聪明的开发者你肯定可以用很多种方式(eg:diff file1 file2)发现 227 | 相对于默认风格究竟修改了那些东西,从而更好理解我们的布局,以获得和自己App风格更适配的更新提示框。 228 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_button_cancel_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_button_cancel_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_button_cancel_tap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_button_ok_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_button_ok_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_button_ok_tap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_dialog_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_title_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /update/white-blue/res/drawable/umeng_update_wifi_disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/white-blue/res/drawable/umeng_update_wifi_disable.png -------------------------------------------------------------------------------- /update/white-blue/res/layout/umeng_update_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 14 | 15 | 22 | 23 | 31 | 32 | 33 | 34 | 37 | 38 | 45 | 46 | 47 | 48 | 49 | 57 | 58 | 66 | 67 | -------------------------------------------------------------------------------- /update/white-blue/res/values-zh/umeng_common_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 正在下载中. 4 | 下载出错啦,请检查网络后继续。 5 | 暂停 6 | 继续 7 | 取消 8 | 正在下载: 9 | 请连接网络后再尝试! 10 | 下载失败 11 | 12 | -------------------------------------------------------------------------------- /update/white-blue/res/values-zh/umeng_update_string.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 未联网 4 | 发现新版本 5 | 最新版本: 6 | 更新内容 7 | 仅需要下载: 8 | 新版本大小: 9 | (提示:非WIFI环境) 10 | 立即更新 11 | 应用更新 12 | 以后再说 13 | 正在更新中.... 14 | 最新版本已下载,是否安装? 15 | 16 | -------------------------------------------------------------------------------- /update/white-blue/res/values/umeng_common_strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The app is already in downloading list. 5 | dowload interrupted. 6 | Pause 7 | Continue 8 | Cancel 9 | Downloading: 10 | Please make sure you are connected to internet, download failed 11 | Download Failed! 12 | -------------------------------------------------------------------------------- /update/white-blue/res/values/umeng_update_string.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Please make sure you are connected to internet,update failed 5 | New version found 6 | Latest version: 7 | Update Content 8 | Update size: 9 | Target size: 10 | (Warning: Not WIFI Condition) 11 | Update now 12 | App updating 13 | Not now 14 | Updating.... 15 | The lastest version has been downloaded, install now ? 16 | 17 | -------------------------------------------------------------------------------- /update/white-blue/white-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umeng/umeng-android-sdk-theme/92dd67701c77c3d559148f203fb9eaa37a967c6c/update/white-blue/white-blue.png --------------------------------------------------------------------------------