1.完成IcLauncher基础功能。
authorkfl_614 <kfl_614@163.com>
Mon, 23 Mar 2020 12:42:36 +0000 (20:42 +0800)
committerkfl_614 <kfl_614@163.com>
Mon, 23 Mar 2020 12:42:36 +0000 (20:42 +0800)
Signed-off-by: kfl_614 <kfl_614@163.com>
31 files changed:
app/build.gradle
app/proguard-rules.pro
app/src/main/AndroidManifest.xml
app/src/main/java/com/ic/launcher/IcLauncherApplication.java
app/src/main/java/com/ic/launcher/MainActivity.java
app/src/main/java/com/ic/launcher/adapter/AppInfoAdapter.java
app/src/main/java/com/ic/launcher/adapter/IndicatorAdapter.java [new file with mode: 0644]
app/src/main/java/com/ic/launcher/configure/l02/L02AppInfo.java
app/src/main/java/com/ic/launcher/fragment/AppListFragment.java
app/src/main/java/com/ic/launcher/fragment/L02MainFragment.java
app/src/main/java/com/ic/launcher/fragment/l02/L02StatusBarFragment.java
app/src/main/java/com/ic/launcher/fragment/l02/L02WatchFragment.java
app/src/main/java/com/ic/launcher/listener/MainClockGestureDetectorImpl.java
app/src/main/java/com/ic/launcher/mode/AppListModel.java
app/src/main/java/com/ic/launcher/mode/StateBarModel.java
app/src/main/java/com/ic/launcher/mode/WatchFaceModel.java
app/src/main/java/com/ic/launcher/presenter/AppListPresenter.java
app/src/main/java/com/ic/launcher/presenter/BatteryPresenter.java
app/src/main/java/com/ic/launcher/presenter/HardwarePresenter.java [new file with mode: 0644]
app/src/main/java/com/ic/launcher/presenter/PhoneStatePresenter.java
app/src/main/java/com/ic/launcher/presenter/QuerySimCardState.java [new file with mode: 0644]
app/src/main/java/com/ic/launcher/presenter/TimeDatePresenter.java
app/src/main/java/com/ic/launcher/util/CrashHandler.java [new file with mode: 0644]
app/src/main/java/com/ic/launcher/util/ServiceUtil.java
app/src/main/res/drawable/ic_transparent_circle.xml [new file with mode: 0644]
app/src/main/res/drawable/ic_white_circle.xml [new file with mode: 0644]
app/src/main/res/drawable/l02_battery_charging_anim.xml
app/src/main/res/layout/fragment_app_list.xml
app/src/main/res/layout/item_indicator_layout.xml [new file with mode: 0644]
app/src/main/res/values/strings.xml
app/version_code.properties [new file with mode: 0644]

index bb500df..e7d619a 100644 (file)
@@ -1,14 +1,23 @@
 apply plugin: 'com.android.application'
 
+static def getL02ApkBaseName() {
+    return "ICL02Launcher"
+}
+
+
 android {
+
+    def currentVersionCode = getVersionCode()
+    def currentVersionName = releaseTime()
+
     compileSdkVersion 29
     buildToolsVersion "29.0.2"
     defaultConfig {
         applicationId "com.ic.launcher"
         minSdkVersion 19
         targetSdkVersion 29
-        versionCode 1
-        versionName "1.0"
+        versionCode currentVersionCode
+        versionName "${currentVersionName}"
     }
 
     dataBinding {
@@ -21,14 +30,79 @@ android {
         sourceCompatibility JavaVersion.VERSION_1_8
     }
 
+    sourceSets {
+        main {
+            jni.srcDirs = []
+            manifest.srcFile 'src/main/AndroidManifest.xml'
+            java.srcDirs = ['src/main/java', 'src/main/aidl']
+            resources.srcDirs = ['src/main/java', 'src/main/aidl']
+            aidl.srcDirs = ['src/main/aidl']
+            res.srcDirs = ['src/main/res']
+            assets.srcDirs = ['src/main/assets']
+        }
+    }
+
+    signingConfigs {
+        key {
+            storeFile file("EQC_G5.jks")
+            storePassword "123456"
+            keyAlias "watch_g5"
+            keyPassword "123456"
+        }
+    }
+
     buildTypes {
         release {
-            minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            zipAlignEnabled true     // zip 对齐优化
+            shrinkResources true       // 移除未使用的资源文件
+            signingConfig signingConfigs.key
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+            renderscriptDebuggable = false
+            debuggable = false
+            jniDebuggable = false
+            manifestPlaceholders = [:]
+        }
+
+        debug {
+            signingConfig signingConfigs.key
+        }
+    }
+
+    android.applicationVariants.all { variant ->
+        variant.outputs.all {
+            if (variant.buildType.name == "release") {
+                outputFileName = "${getL02ApkBaseName()}_V${currentVersionCode}_${currentVersionName}_release.apk"
+            } else {
+                outputFileName = "${getL02ApkBaseName()}_V${currentVersionCode}_${currentVersionName}_debug.apk"
+            }
         }
     }
 }
 
+def getVersionCode() {
+    def versionCodeFile = file('version_code.properties')
+    if (versionCodeFile.canRead()) {
+        Properties properties = new Properties()
+        properties.load(new FileInputStream(versionCodeFile))
+        def versionCode = properties['VERSION_CODE'].toInteger()//读取version_code.properties文件存放的版本号。
+        def runTasks = gradle.startParameter.taskNames
+        def task = ':app:assembleRelease'
+        if (task in runTasks) {
+            properties['VERSION_CODE'] = (++versionCode).toString()
+            properties.store(versionCodeFile.newWriter(), null)
+        }
+
+        return versionCode
+    } else {
+        throw new GradleException("无法读取version_code.properties文件!")
+    }
+}
+
+def releaseTime() {
+    return new Date().format("yyyyMMddHHmm", TimeZone.getTimeZone("GMT+08:00"))
+}
+
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation 'androidx.appcompat:appcompat:1.1.0'
index f1b4245..a86af50 100644 (file)
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
 #-renamesourcefileattribute SourceFile
+-dontoptimize                           # 混合时不使用大小写混合,混合后的类名为小写
+-dontusemixedcaseclassnames             # 混合时不使用大小写混合,混合后的类名为小写
+-dontskipnonpubliclibraryclasses        # 不跳过library中的非public的类
+-dontskipnonpubliclibraryclassmembers   # 不跳过library中的非public的类成员
+-dontpreverify                          # 不做预校验,preverify是proguard的四个步骤之一,Android平台不需要preverify,去掉这一步能够加快混淆速度
+-verbose                                # 在处理时打印冗长的信息
+
+# 保留异常表,以便编译器知道哪些异常可能被抛出
+# 保留InnerClasses,否则外部无法引用内部类
+# 保留Signature,否则无法访问泛型
+-keepattributes Exceptions,SourceFile,LineNumberTable,EnclosingMethod,Signature,*Annotation*,InnerClasses,Deprecated
+
+################################## 需要保留的公共部分 ##################################
+# 保留四大组件,自定义的Application等等这些类不被混淆
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class * extends android.view.View
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keep class android.support.** { *; }
+-keep public class * extends android.support.v4.**
+-keep public class * extends android.support.v7.**
+-keep public class * extends android.support.annotation.**
+-keep public class * extends android.app.Fragment
+-keep class com.google.** { *; }
+
+# 保留R下面的资源
+-keep class **.R$* {*;}
+-keepclassmembers class **.R$* {
+    public static <fields>;
+}
+
+# 保留本地native方法不被混淆
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# 保留在Activity中的方法参数是view的方法,
+# 这样以来我们在layout中写的onClick就不会被影响
+-keepclassmembers class * extends android.app.Activity{
+    public void *(android.view.View);
+}
+
+# 保留我们自定义控件(继承自View)不被混淆
+-keep public class * extends android.view.View{
+    *** get*();
+    void set*(***);
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclasseswithmembernames class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembernames class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+# 保留枚举类不被混淆
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+# 保留Parcelable序列化类不被混淆
+-keep class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}
+
+# 保留Serializable序列化的类不被混淆
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private static final java.io.ObjectStreamField[] serialPersistentFields;
+    !static !transient <fields>;
+    !private <fields>;
+    !private <methods>;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
+-keepclassmembers class * {
+    void *(**On*Event);
+    void *(**On*Listener);
+}
+
+
+-keep class com.sun.jna.** { *; }
+-dontwarn com.sun.jna.**
+
+-keep public class com.android.support.a
+-keep public class com.android.support.b
+-keep public class rx.internal.util
\ No newline at end of file
index 5184a30..c8fe296 100644 (file)
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    package="com.ic.launcher">
+    package="com.ic.launcher"
+    android:sharedUserId="android.uid.system">
 
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 
index 134dc0c..0779d2a 100644 (file)
@@ -6,6 +6,7 @@ import com.ic.launcher.presenter.AppListPresenter;
 import com.ic.launcher.presenter.BatteryPresenter;
 import com.ic.launcher.presenter.PhoneStatePresenter;
 import com.ic.launcher.presenter.TimeDatePresenter;
+import com.ic.launcher.util.CrashHandler;
 import com.ic.launcher.util.IcLog;
 
 public class IcLauncherApplication extends Application {
@@ -25,6 +26,9 @@ public class IcLauncherApplication extends Application {
         mBatteryPresenter = new BatteryPresenter(this);
         timeDatePresenter = new TimeDatePresenter(this);
         appListPresenter = new AppListPresenter(this);
+
+        CrashHandler crashHandler = CrashHandler.getInstance();
+        crashHandler.init(getApplicationContext());
     }
 
     public PhoneStatePresenter getPhoneStatePresenter() {
index f47e5d8..53b24c7 100644 (file)
@@ -5,6 +5,7 @@ import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentManager;
 import androidx.fragment.app.FragmentTransaction;
 
+import android.content.Intent;
 import android.os.Bundle;
 
 import com.ic.launcher.fragment.AppListFragment;
@@ -21,6 +22,7 @@ public class MainActivity extends AppCompatActivity implements
         MainClockGestureDetectorImpl.OnClockGestureFlingListener,
         IChangeMainFragmentListener {
 
+    private static final String PACKAGE_NAME_LAUNCHER = "mobile.miki";
     private FragmentManager fragmentManager;
 
     private List<Fragment> mAddFt;
@@ -35,12 +37,23 @@ public class MainActivity extends AppCompatActivity implements
         mAddFt = new ArrayList<>();
         fragmentManager = getSupportFragmentManager();
 
-        crateFragment();
+        l02MainFt = L02MainFragment.newInstance();
+
         commitFragment(l02MainFt);
 
         ServiceUtil.startServiceUtil(getApplicationContext(),
-                "com.ic.action.WORK_SERVICE",
-                getPackageName());
+                "com.ic.action.WORK_SERVICE", getPackageName());
+    }
+
+    @Override
+    public void onDouble() {
+        try {
+            // 启动工厂测试
+            Intent intent = getPackageManager().getLaunchIntentForPackage(PACKAGE_NAME_LAUNCHER);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            startActivity(intent);
+        } catch (Exception ignored) {
+        }
     }
 
     @Override
@@ -48,10 +61,21 @@ public class MainActivity extends AppCompatActivity implements
         switch (flag) {
             case 1:
                 //左滑
+                if (appListFt == null) {
+                    appListFt = AppListFragment.newInstance(1);
+                } else {
+                    appListFt.setMoveEndItem(false);
+                }
                 commitFragment(appListFt);
                 break;
             case 2:
                 //右滑
+                if (appListFt == null) {
+                    appListFt = AppListFragment.newInstance(2);
+                } else {
+                    appListFt.setMoveEndItem(true);
+                }
+                commitFragment(appListFt);
                 break;
         }
     }
@@ -62,11 +86,6 @@ public class MainActivity extends AppCompatActivity implements
         commitFragment(l02MainFt);
     }
 
-    private void crateFragment() {
-        l02MainFt = L02MainFragment.newInstance();
-        appListFt = AppListFragment.newInstance();
-    }
-
     private void commitFragment(Fragment fragment) {
         FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
 
index ecdc53d..3c49af5 100644 (file)
@@ -21,18 +21,22 @@ public class AppInfoAdapter extends RecyclerView.Adapter<AppInfoAdapter.AppHolde
     private List<AppInfoEntity> appArray;
     private ICheckAppItemListener mListener;
 
-    public AppInfoAdapter(Context context,ICheckAppItemListener listener) {
+    public AppInfoAdapter(Context context, ICheckAppItemListener listener) {
         this.mContext = context;
         appArray = new ArrayList<>();
         this.mListener = listener;
     }
 
-    public void updateAppInfoList(List<AppInfoEntity> list){
+    public void updateAppInfoList(List<AppInfoEntity> list) {
         appArray.clear();
         appArray.addAll(list);
         notifyDataSetChanged();
     }
 
+    public int getAppEndIndex() {
+        return appArray.size() - 1;
+    }
+
     @NonNull
     @Override
     public AppHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -41,7 +45,7 @@ public class AppInfoAdapter extends RecyclerView.Adapter<AppInfoAdapter.AppHolde
 
     @Override
     public void onBindViewHolder(@NonNull AppHolder holder, int position) {
-        holder.bindAppInfo(mContext, appArray.get(position),mListener,position);
+        holder.bindAppInfo(mContext, appArray.get(position), mListener, position);
     }
 
     @Override
@@ -58,7 +62,7 @@ public class AppInfoAdapter extends RecyclerView.Adapter<AppInfoAdapter.AppHolde
         }
 
 
-        public void bindAppInfo(Context context, AppInfoEntity entity, ICheckAppItemListener listener,int pos) {
+        public void bindAppInfo(Context context, AppInfoEntity entity, ICheckAppItemListener listener, int pos) {
             if (binding == null || entity == null) {
                 return;
             }
diff --git a/app/src/main/java/com/ic/launcher/adapter/IndicatorAdapter.java b/app/src/main/java/com/ic/launcher/adapter/IndicatorAdapter.java
new file mode 100644 (file)
index 0000000..170f9b6
--- /dev/null
@@ -0,0 +1,63 @@
+package com.ic.launcher.adapter;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.ic.launcher.databinding.ItemIndicatorLayoutBinding;
+
+
+public class IndicatorAdapter extends RecyclerView.Adapter<IndicatorAdapter.IndicatorHolder> {
+
+    private boolean[] stateList;
+
+    private int oldCheckIndex=0;
+
+    public IndicatorAdapter(boolean[] state){
+        stateList = state;
+    }
+
+    public void updateIndicatorState(int index){
+        if(oldCheckIndex != index){
+            stateList[index] = true;
+            stateList[oldCheckIndex] =false;
+            oldCheckIndex = index;
+
+            notifyDataSetChanged();
+        }
+    }
+
+    @NonNull
+    @Override
+    public IndicatorHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new IndicatorHolder(ItemIndicatorLayoutBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull IndicatorHolder holder, int position) {
+        holder.bindItem(stateList[position]);
+    }
+
+    @Override
+    public int getItemCount() {
+        return stateList.length;
+    }
+
+    static class IndicatorHolder extends RecyclerView.ViewHolder {
+        private ItemIndicatorLayoutBinding binding = null;
+
+        public IndicatorHolder(@NonNull ItemIndicatorLayoutBinding itemView) {
+            super(itemView.getRoot());
+            binding = itemView;
+        }
+
+        public void bindItem(boolean state) {
+            if (binding != null) {
+                binding.setIsCheckItem(state);
+                binding.executePendingBindings();
+            }
+        }
+    }
+}
index 239ad06..1be15c0 100644 (file)
@@ -28,10 +28,10 @@ public class L02AppInfo {
                     "", ""));
             //天气
             add(new AppInfoEntity(BACK_COLOUR, R.mipmap.icon_weather_l02, R.string.app_name_weather,
-                    "com.ic.hardware", "com.ic.hardware.WeatherActivity"));
+                    "com.ic.weather", "com.ic.weather.WeatherActivity"));
             //心率
             add(new AppInfoEntity(BACK_COLOUR, R.mipmap.icon_heartrate_l02, R.string.app_name_heart,
-                    "com.ic.hardware", "com.ic.hardware.BloodActivity"));
+                    "com.ic.blood", "com.ic.blood.BloodActivity"));
             //支付包
             add(new AppInfoEntity(BACK_COLOUR, R.mipmap.icon_alipay_l02, R.string.app_name_alipay,
                     "com.eg.android.AlipayGphone", "com.alipay.watch.ui.MainActivity"));
index 3bd0537..29ba6b7 100644 (file)
@@ -8,11 +8,9 @@ import androidx.annotation.NonNull;
 import androidx.fragment.app.Fragment;
 import androidx.lifecycle.ViewModelProvider;
 import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.LinearSmoothScroller;
 import androidx.recyclerview.widget.PagerSnapHelper;
 import androidx.recyclerview.widget.RecyclerView;
 
-import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -20,14 +18,12 @@ import android.view.ViewGroup;
 import com.ic.launcher.IcLauncherApplication;
 import com.ic.launcher.R;
 import com.ic.launcher.adapter.AppInfoAdapter;
-import com.ic.launcher.entity.AppInfoEntity;
+import com.ic.launcher.adapter.IndicatorAdapter;
 import com.ic.launcher.listener.IChangeMainFragmentListener;
 import com.ic.launcher.listener.ICheckAppItemListener;
 import com.ic.launcher.mode.AppListModel;
 import com.ic.launcher.util.IcLog;
 
-import static android.widget.AbsListView.OnScrollListener.SCROLL_STATE_IDLE;
-
 
 public class AppListFragment extends Fragment {
     private static final String TAG = "AppListFragment";
@@ -37,13 +33,17 @@ public class AppListFragment extends Fragment {
 
     private IcLauncherApplication mApp;
 
-    private TopSmoothScroller mScroller;
-    private LinearLayoutManager mLayoutManager;
     private IChangeMainFragmentListener changeMainFragmentListener;
     private AppInfoAdapter appAdapter;
 
     private AppListModel appListModel;
 
+    private RecyclerView indicatorRv;
+    private IndicatorAdapter indicatorAdapter;
+
+    //1 显示第一个  2  显示最后一个
+    private int mCurrType;
+
     @Override
     public void onAttach(@NonNull Context context) {
         super.onAttach(context);
@@ -57,14 +57,22 @@ public class AppListFragment extends Fragment {
         }
     }
 
-    public static AppListFragment newInstance() {
-        return new AppListFragment();
+    public static AppListFragment newInstance(int type) {
+        AppListFragment fragment = new AppListFragment();
+        Bundle args = new Bundle();
+        args.putInt("type", type);
+        fragment.setArguments(args);
+        return fragment;
     }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mApp = (IcLauncherApplication) mContext.getApplicationContext();
+        Bundle bundle = getArguments();
+        if (bundle != null) {
+            mCurrType = bundle.getInt("type");
+        }
     }
 
     @Override
@@ -72,21 +80,45 @@ public class AppListFragment extends Fragment {
                              Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.fragment_app_list, container, false);
         mAppRv = view.findViewById(R.id.app_list);
+        indicatorRv = view.findViewById(R.id.app_indicator_rv);
+
         initRecyclerView();
         initAppListModel();
+
         return view;
     }
 
     @Override
     public void onResume() {
         super.onResume();
-        mScroller.setTargetPosition(0);
-        mLayoutManager.startSmoothScroll(mScroller);
+        IcLog.i(TAG, "onResume");
+        if (mCurrType == 2) {
+            //只有初始化进来的时候才执行,
+            mAppRv.scrollToPosition(appAdapter.getAppEndIndex());
+            indicatorAdapter.updateIndicatorState(appAdapter.getAppEndIndex());
+            mCurrType = 0;
+        }
     }
 
-    private void initRecyclerView(){
-        appAdapter = new AppInfoAdapter(mContext,itemListener);
-        mLayoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false);
+    @Override
+    public void onPause() {
+        super.onPause();
+        IcLog.i(TAG, "onPause");
+    }
+
+    public void setMoveEndItem(boolean move) {
+        if (move) {
+            mAppRv.scrollToPosition(appAdapter.getAppEndIndex());
+            indicatorAdapter.updateIndicatorState(appAdapter.getAppEndIndex());
+        } else {
+            mAppRv.scrollToPosition(0);
+            indicatorAdapter.updateIndicatorState(0);
+        }
+    }
+
+    private void initRecyclerView() {
+        appAdapter = new AppInfoAdapter(mContext, itemListener);
+        LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false);
 
         mAppRv.setLayoutManager(mLayoutManager);
         mAppRv.setAdapter(appAdapter);
@@ -94,57 +126,81 @@ public class AppListFragment extends Fragment {
         new PagerSnapHelper().attachToRecyclerView(mAppRv);
 
         mAppRv.addOnScrollListener(scrollListener);
-
-        mScroller = new TopSmoothScroller(mContext);
     }
 
     private void initAppListModel() {
-
-        AppListModel.Factory factory = new AppListModel.Factory(mApp,mApp.getAppListPresenter());
+        AppListModel.Factory factory = new AppListModel.Factory(mApp, mApp.getAppListPresenter());
         appListModel = new ViewModelProvider(this, factory).get(AppListModel.class);
 
         appListModel.getObsAppInfoList().observe(getViewLifecycleOwner(), appList -> {
             appAdapter.updateAppInfoList(appList);
         });
 
+        appListModel.getObsAppState().observe(getViewLifecycleOwner(), this::initIndicator);
+
         appListModel.updateAppInfoList();
     }
 
+    private void initIndicator(boolean[] state) {
+        indicatorAdapter = new IndicatorAdapter(state);
+        LinearLayoutManager mLayoutManager = new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false);
+        indicatorRv.setLayoutManager(mLayoutManager);
+        indicatorRv.setAdapter(indicatorAdapter);
+    }
+
     private RecyclerView.OnScrollListener scrollListener = new RecyclerView.OnScrollListener() {
 
-        private boolean isAllowLeft = false;
-        private boolean isAllowRight = false;
+        private boolean isJudge = true;
 
         @Override
         public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
             super.onScrollStateChanged(recyclerView, newState);
-            if (newState == SCROLL_STATE_IDLE) {
-                if (!isAllowLeft || !isAllowRight) {
-                    //此处返回主界面
-                    IcLog.i(TAG, "Change To Main Fragment");
-                    changeMainFragmentListener.onChangeMain();
-                }
+            //IcLog.i(TAG, "newState:" + newState);
+            switch (newState) {
+                case RecyclerView.SCROLL_STATE_IDLE:
+                    judgeItemPosition(recyclerView, isJudge);
+                    break;
+                case RecyclerView.SCROLL_STATE_DRAGGING:
+                    isJudge = true;
+                    break;
+                case RecyclerView.SCROLL_STATE_SETTLING:
+                    isJudge = false;
+                    break;
             }
         }
 
         @Override
         public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
             super.onScrolled(recyclerView, dx, dy);
-            isAllowLeft = mAppRv.canScrollHorizontally(-1);
-            isAllowRight = mAppRv.canScrollHorizontally(1);
         }
-    };
 
-    private class TopSmoothScroller extends LinearSmoothScroller{
-        public TopSmoothScroller(Context context) {
-            super(context);
-        }
+        private void judgeItemPosition(RecyclerView recyclerView, boolean isJudge) {
+            LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager();
+            if (manager == null) return;
+            //获取最后一个完全显示的ItemPosition ,角标值
+            int first = manager.findFirstVisibleItemPosition();
+            indicatorAdapter.updateIndicatorState(first);
+            if (!isJudge) {
+                return;
+            }
+            int lastVisibleItem = manager.findLastCompletelyVisibleItemPosition();
+            //所有条目,数量值
+            int totalItemCount = manager.getItemCount();
+
+            // 判断是否滚动到底部,并且是向右滚动
+            if (lastVisibleItem == (totalItemCount - 1)) {
+                //加载更多功能的代码
+                IcLog.i(TAG, "lastVisibleItem:" + lastVisibleItem);
+                changeMainFragmentListener.onChangeMain();
+            }
 
-        @Override
-        protected int getHorizontalSnapPreference() {
-            return SNAP_TO_START;//具体见源码注释
+            if (lastVisibleItem == 0) {
+                //加载更多功能的代码
+                IcLog.i(TAG, "Start VisibleItem:" + lastVisibleItem);
+                changeMainFragmentListener.onChangeMain();
+            }
         }
-    }
+    };
 
     private ICheckAppItemListener itemListener = new ICheckAppItemListener() {
         @Override
@@ -152,4 +208,6 @@ public class AppListFragment extends Fragment {
             appListModel.onStartAppActivity(pos);
         }
     };
+
+
 }
index 8027c59..439576b 100644 (file)
@@ -17,10 +17,13 @@ import com.ic.launcher.R;
 import com.ic.launcher.fragment.l02.L02WatchFragment;
 import com.ic.launcher.fragment.l02.L02StatusBarFragment;
 import com.ic.launcher.listener.MainClockGestureDetectorImpl;
+import com.ic.launcher.util.IcLog;
 
 
 public class L02MainFragment extends Fragment {
 
+    private static final String TAG = "L02MainFragment";
+
     private Context mContext;
 
     private L02StatusBarFragment statusBarFt;
@@ -50,16 +53,24 @@ public class L02MainFragment extends Fragment {
         return view;
     }
 
+    @Override
+    public void onHiddenChanged(boolean hidden) {
+        super.onHiddenChanged(hidden);
+        IcLog.i(TAG, "onHiddenChanged hidden" + hidden);
+    }
+
     @Override
     public void onResume() {
         super.onResume();
         controlStatusBar(true);
+        IcLog.i(TAG, "onResume");
     }
 
     @Override
     public void onPause() {
         super.onPause();
         controlStatusBar(false);
+        IcLog.i(TAG, "onPause");
     }
 
     @Override
@@ -96,7 +107,7 @@ public class L02MainFragment extends Fragment {
 
     @SuppressLint("ClickableViewAccessibility")
     private void initOnTouchListener(View view) {
-        gesture = new GestureDetectorCompat(mContext,new MainClockGestureDetectorImpl(flingListener));
+        gesture = new GestureDetectorCompat(mContext, new MainClockGestureDetectorImpl(flingListener));
         view.setOnTouchListener((v, event) -> {
             if (gesture.onTouchEvent(event)) {
                 return true;
index bda1c9c..772592d 100644 (file)
@@ -15,6 +15,7 @@ import android.view.ViewGroup;
 import com.ic.launcher.IcLauncherApplication;
 import com.ic.launcher.databinding.FragmentL02StatusBarBinding;
 import com.ic.launcher.mode.StateBarModel;
+import com.ic.launcher.util.IcLog;
 
 
 public class L02StatusBarFragment extends Fragment {
@@ -43,25 +44,30 @@ public class L02StatusBarFragment extends Fragment {
                              Bundle savedInstanceState) {
         mStateBarBinding = FragmentL02StatusBarBinding.inflate(inflater, container, false);
         initStateBarModel();
+        phoneModel.registerBatteryReceiver();
+        IcLog.i(TAG,"onCreateView");
         return mStateBarBinding.getRoot();
     }
 
     @Override
     public void onResume() {
         super.onResume();
+        IcLog.i(TAG,"onResume");
         phoneModel.onResumeView();
     }
 
     @Override
     public void onPause() {
         super.onPause();
+        IcLog.i(TAG,"onPause");
         phoneModel.onPauseView();
     }
 
     @Override
-    public void onDestroyView() {
-        super.onDestroyView();
+    public void onDestroy() {
+        super.onDestroy();
         phoneModel.onDestroyView();
+        phoneModel.unregisterBatteryReceiver();
     }
 
     private void initStateBarModel() {
@@ -92,6 +98,6 @@ public class L02StatusBarFragment extends Fragment {
             mStateBarBinding.setBatteryValue(value);
         });
 
-        phoneModel.querySimCardState();
+        phoneModel.onStartQuery();
     }
 }
index 0183986..e012afa 100644 (file)
@@ -15,9 +15,12 @@ import android.view.ViewGroup;
 import com.ic.launcher.IcLauncherApplication;
 import com.ic.launcher.databinding.FragmentL02WatchBinding;
 import com.ic.launcher.mode.WatchFaceModel;
+import com.ic.launcher.util.IcLog;
 
 public class L02WatchFragment extends Fragment {
 
+    private static final String TAG = "L02WatchFragment";
+
     private Context mContext;
     private IcLauncherApplication mApp;
 
@@ -43,9 +46,6 @@ public class L02WatchFragment extends Fragment {
 
         watchBinding = FragmentL02WatchBinding.inflate(inflater, container, false);
         initWatchFaceModel();
-
-        watchBinding.setHeartValue("100");
-        watchBinding.setStepValue("100000");
         return watchBinding.getRoot();
     }
 
@@ -53,20 +53,23 @@ public class L02WatchFragment extends Fragment {
     public void onResume() {
         super.onResume();
         watchFaceModel.onResumeView();
+        IcLog.i(TAG, "onResume");
     }
 
     @Override
     public void onPause() {
         super.onPause();
         watchFaceModel.onPauseView();
+        IcLog.i(TAG, "onPause");
     }
 
     @Override
-    public void onDestroyView() {
-        super.onDestroyView();
+    public void onDestroy() {
+        super.onDestroy();
         watchFaceModel.onDestroyView();
     }
 
+
     private void initWatchFaceModel() {
         WatchFaceModel.Factory factory = new WatchFaceModel.Factory(mApp, mApp.getTimeDatePresenter());
         watchFaceModel = new ViewModelProvider(this, factory).get(WatchFaceModel.class);
@@ -82,5 +85,17 @@ public class L02WatchFragment extends Fragment {
         watchFaceModel.getObsYear().observe(getViewLifecycleOwner(), year -> {
             watchBinding.setWatchDate(year);
         });
+
+        watchFaceModel.getObsHardwareHeart().observe(getViewLifecycleOwner(), heart -> {
+            watchBinding.setHeartValue(heart);
+        });
+
+        watchFaceModel.getObsHardwarePed().observe(getViewLifecycleOwner(), ped -> {
+            watchBinding.setStepValue(ped);
+        });
+
+        watchBinding.setHeartValue("0");
+        watchBinding.setStepValue("0");
+        watchFaceModel.onCreateView();
     }
 }
index dab2a74..ece2e7c 100644 (file)
@@ -1,5 +1,6 @@
 package com.ic.launcher.listener;
 
+import android.os.SystemClock;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 
@@ -8,26 +9,37 @@ import com.ic.launcher.util.IcLog;
 public class MainClockGestureDetectorImpl extends GestureDetector.SimpleOnGestureListener {
     private static final String TAG = "MainClockGesture";
 
-    public interface OnClockGestureFlingListener{
+    private final int COUNTS = 2;//点击次数
+    private final long DURATION = 3 * 1000;//规定有效时间
+    private long[] mHits = new long[COUNTS];
+
+    public interface OnClockGestureFlingListener {
         void onDirection(int flag);
+        void onDouble();
     }
 
     private OnClockGestureFlingListener mListener;
 
-    public MainClockGestureDetectorImpl(OnClockGestureFlingListener flingListener ){
+    public MainClockGestureDetectorImpl(OnClockGestureFlingListener flingListener) {
         this.mListener = flingListener;
     }
 
+    @Override
+    public boolean onDown(MotionEvent e) {
+        doubleClick();
+        return super.onDown(e);
+    }
+
     @Override
     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-        IcLog.i(TAG, "onFling x1:" + e1.getX()+ "x2:" + e2.getX());
+        IcLog.i(TAG, "onFling x1:" + e1.getX() + "x2:" + e2.getX());
 
-        if(e1.getX() - e2.getX() > 30){
+        if (e1.getX() - e2.getX() > 30) {
             onCallBackListener(1);
             return true;
         }
 
-        if(e2.getX() - e1.getX() > 30){
+        if (e2.getX() - e1.getX() > 30) {
             onCallBackListener(2);
             return true;
         }
@@ -35,9 +47,19 @@ public class MainClockGestureDetectorImpl extends GestureDetector.SimpleOnGestur
         return super.onFling(e1, e2, velocityX, velocityY);
     }
 
-    private void onCallBackListener(int flag){
-        if(mListener != null){
+    private void onCallBackListener(int flag) {
+        if (mListener != null) {
             mListener.onDirection(flag);
         }
     }
+
+    private void doubleClick() {
+        System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1);
+        mHits[mHits.length - 1] = SystemClock.uptimeMillis();
+        if (mHits[0] >= (SystemClock.uptimeMillis() - DURATION)) {
+            if (mListener != null) {
+                mListener.onDouble();
+            }
+        }
+    }
 }
index 549e742..09693b5 100644 (file)
@@ -19,18 +19,31 @@ public class AppListModel extends AndroidViewModel {
     private final AppListPresenter appListPresenter;
 
     private final LiveData<List<AppInfoEntity>> mObsAppInfoList;
+    private final LiveData<boolean[]> mObsAppState;
+    private final LiveData<String> mObsActivityError;
 
     public AppListModel(@NonNull Application application,
                         @NonNull AppListPresenter appListPresenter) {
         super(application);
         this.appListPresenter = appListPresenter;
         mObsAppInfoList = appListPresenter.getObsAppInfoList();
+
+        mObsAppState = appListPresenter.getObsAppState();
+        mObsActivityError = appListPresenter.getObsActivityError();
     }
 
     public LiveData<List<AppInfoEntity>> getObsAppInfoList() {
         return mObsAppInfoList;
     }
 
+    public LiveData<boolean[]> getObsAppState() {
+        return mObsAppState;
+    }
+
+    public LiveData<String> getObsActivityError() {
+        return mObsActivityError;
+    }
+
     public void updateAppInfoList() {
         appListPresenter.updateSystemApp();
     }
index 6c02a44..3c6b3d3 100644 (file)
@@ -10,6 +10,7 @@ import androidx.lifecycle.ViewModelProvider;
 
 import com.ic.launcher.presenter.BatteryPresenter;
 import com.ic.launcher.presenter.PhoneStatePresenter;
+import com.ic.launcher.util.IcLog;
 
 public class StateBarModel extends AndroidViewModel {
 
@@ -64,18 +65,25 @@ public class StateBarModel extends AndroidViewModel {
         return mObsBatteryResID;
     }
 
-    public void querySimCardState(){
-        mPhonePresenter.querySimCardState();
+
+    public void onStartQuery(){
+        mPhonePresenter.onStartQuery();
     }
 
     public void onResumeView(){
         mPhonePresenter.onResumePresenter();
+    }
+
+    public void registerBatteryReceiver(){
         mBatteryPresenter.registerBatteryReceiver();
     }
 
+    public void unregisterBatteryReceiver(){
+        mBatteryPresenter.unRegisterBatteryReceiver();
+    }
+
     public void onPauseView(){
         mPhonePresenter.onPausePresenter();
-        mBatteryPresenter.unRegisterBatteryReceiver();
     }
 
     public void onDestroyView(){
index dff03e4..eb1e6e4 100644 (file)
@@ -5,10 +5,10 @@ import android.app.Application;
 import androidx.annotation.NonNull;
 import androidx.lifecycle.AndroidViewModel;
 import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MediatorLiveData;
 import androidx.lifecycle.ViewModel;
 import androidx.lifecycle.ViewModelProvider;
 
+import com.ic.launcher.presenter.HardwarePresenter;
 import com.ic.launcher.presenter.TimeDatePresenter;
 
 import java.util.ArrayList;
@@ -16,20 +16,29 @@ import java.util.ArrayList;
 
 public class WatchFaceModel extends AndroidViewModel {
 
-    private TimeDatePresenter timeDatePresenter;
+    private final TimeDatePresenter timeDatePresenter;
+    private final HardwarePresenter hardwarePresenter;
 
     private final LiveData<ArrayList<Integer>> mObsTime;
     private final LiveData<String> mObsYear;
     private final LiveData<String> mObsWeek;
 
+    private final LiveData<String> mObsHardwareHeart;
+    private final LiveData<String> mObsHardwarePed;
 
     public WatchFaceModel(@NonNull Application application,
                           @NonNull TimeDatePresenter timeDatePresenter) {
         super(application);
+
         this.timeDatePresenter = timeDatePresenter;
+        hardwarePresenter = new HardwarePresenter(application);
+
         mObsTime = timeDatePresenter.getObsTime();
         mObsYear = timeDatePresenter.getObsYear();
         mObsWeek = timeDatePresenter.getmObsWeek();
+
+        mObsHardwareHeart = hardwarePresenter.getObsHardwareHeart();
+        mObsHardwarePed = hardwarePresenter.getObsHardwarePed();
     }
 
     public LiveData<ArrayList<Integer>> getObsTime() {
@@ -44,8 +53,21 @@ public class WatchFaceModel extends AndroidViewModel {
         return mObsWeek;
     }
 
+    public LiveData<String> getObsHardwareHeart() {
+        return mObsHardwareHeart;
+    }
+
+    public LiveData<String> getObsHardwarePed() {
+        return mObsHardwarePed;
+    }
+
+    public void onCreateView(){
+        hardwarePresenter.registerHardwareBroadcast();
+    }
+
     public void onResumeView() {
         timeDatePresenter.onResumePresenter();
+        hardwarePresenter.sendGetPedBroadcast();
     }
 
     public void onPauseView() {
@@ -54,6 +76,7 @@ public class WatchFaceModel extends AndroidViewModel {
 
     public void onDestroyView() {
         timeDatePresenter.unRegisterTimeDateReceiver();
+        hardwarePresenter.unRegisterHardwareReceiver();
     }
 
     public static class Factory extends ViewModelProvider.NewInstanceFactory {
index e07f88f..4ff9840 100644 (file)
@@ -17,16 +17,18 @@ public class AppListPresenter {
 
     private Context context;
     private MutableLiveData<List<AppInfoEntity>> mObsAppInfoList;
+    private MutableLiveData<boolean[]> mObsAppState;
     private MutableLiveData<String> mObsActivityError;
 
     private String mErrorInfo;
 
-    public AppListPresenter(Context context){
-        this.context=context;
+    public AppListPresenter(Context context) {
+        this.context = context;
         mObsAppInfoList = new MutableLiveData<>();
         mObsActivityError = new MutableLiveData<>();
+        mObsAppState = new MutableLiveData<>();
 
-        mErrorInfo  = context.getString(R.string.error_start_activity);
+        mErrorInfo = context.getString(R.string.error_start_activity);
     }
 
     public MutableLiveData<List<AppInfoEntity>> getObsAppInfoList() {
@@ -37,12 +39,22 @@ public class AppListPresenter {
         return mObsActivityError;
     }
 
-    public void updateSystemApp(){
+    public MutableLiveData<boolean[]> getObsAppState() {
+        return mObsAppState;
+    }
+
+    public void updateSystemApp() {
         mObsAppInfoList.setValue(L02AppInfo.mL02DefaultAppList);
+        boolean[] state = new boolean[L02AppInfo.mL02DefaultAppList.size()];
+        for (int i = 0; i < state.length; i++) {
+            state[i] = false;
+        }
+        state[0] = true;
+        mObsAppState.setValue(state);
     }
 
-    public void startAppActivity(int index){
-        if(index <0 || index > L02AppInfo.mL02DefaultAppList.size()){
+    public void startAppActivity(int index) {
+        if (index < 0 || index > L02AppInfo.mL02DefaultAppList.size()) {
             mObsActivityError.setValue(mErrorInfo);
             return;
         }
@@ -51,7 +63,7 @@ public class AppListPresenter {
         String pck = entity.getPackageName();
         String activity = entity.getActivityName();
 
-        if(TextUtils.isEmpty(pck) || TextUtils.isEmpty(activity)){
+        if (TextUtils.isEmpty(pck) || TextUtils.isEmpty(activity)) {
             mObsActivityError.setValue(mErrorInfo);
             return;
         }
@@ -61,7 +73,7 @@ public class AppListPresenter {
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             intent.setComponent(new ComponentName(pck, activity));
             context.startActivity(intent);
-        } catch (Exception e ) {
+        } catch (Exception e) {
             mObsActivityError.setValue(mErrorInfo);
         }
     }
index 53f0f84..9ca23e4 100644 (file)
@@ -10,6 +10,7 @@ import android.text.TextUtils;
 import androidx.lifecycle.MediatorLiveData;
 
 import com.ic.launcher.configure.l02.L02Icon;
+import com.ic.launcher.util.IcLog;
 
 public class BatteryPresenter {
 
@@ -46,10 +47,15 @@ public class BatteryPresenter {
     }
 
     public void unRegisterBatteryReceiver() {
-        mContext.unregisterReceiver(mIntentReceiver);
+        try {
+            mContext.unregisterReceiver(mIntentReceiver);
+        }catch (Exception e){
+            IcLog.e("BatteryPresenter",e.getMessage());
+        }
+
     }
 
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
 
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/app/src/main/java/com/ic/launcher/presenter/HardwarePresenter.java b/app/src/main/java/com/ic/launcher/presenter/HardwarePresenter.java
new file mode 100644 (file)
index 0000000..73eed9f
--- /dev/null
@@ -0,0 +1,76 @@
+package com.ic.launcher.presenter;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.text.TextUtils;
+
+import androidx.lifecycle.MediatorLiveData;
+
+public class HardwarePresenter {
+
+    private static final String HEART_ACTION = "com.ic.action.BLOOD_HEART";
+    private static final String PED_ACTION = "com.ic.action.PED_COUNT";
+
+    private static final String GET_PED_ACTION = "com.ic.action.GET_PED_COUNT";
+
+    private Context mContext;
+    private MediatorLiveData<String> mObsHardwareHeart;
+    private MediatorLiveData<String> mObsHardwarePed;
+
+    public HardwarePresenter(Context context) {
+
+        this.mContext = context;
+
+        mObsHardwareHeart = new MediatorLiveData<>();
+        mObsHardwarePed = new MediatorLiveData<>();
+    }
+
+    public MediatorLiveData<String> getObsHardwareHeart() {
+        return mObsHardwareHeart;
+    }
+
+    public MediatorLiveData<String> getObsHardwarePed() {
+        return mObsHardwarePed;
+    }
+
+    public void registerHardwareBroadcast() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(PED_ACTION);
+        filter.addAction(HEART_ACTION);
+        mContext.registerReceiver(mIntentReceiver, filter);
+    }
+
+    public void unRegisterHardwareReceiver() {
+        mContext.unregisterReceiver(mIntentReceiver);
+    }
+
+    public void sendGetPedBroadcast(){
+        Intent pedIntent = new Intent();
+        pedIntent.setAction(GET_PED_ACTION);
+        mContext.sendBroadcast(pedIntent);
+    }
+
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent != null) {
+                String acyion = intent.getAction();
+                if(TextUtils.isEmpty(acyion)){
+                    return;
+                }
+                switch (acyion) {
+                    case PED_ACTION:
+                        int count = intent.getIntExtra("PED_COUNT", 0);
+                        mObsHardwarePed.setValue(String.valueOf(count));
+                        break;
+                    case HEART_ACTION:
+                        int heart = intent.getIntExtra("BLOOD_HEART", 0);
+                        mObsHardwareHeart.setValue(String.valueOf(heart));
+                        break;
+                }
+            }
+        }
+    };
+}
index 52293bc..71d5afe 100644 (file)
@@ -21,13 +21,13 @@ public class PhoneStatePresenter {
     private MediatorLiveData<Integer> mObsPhoneSignalID;
     private MediatorLiveData<Integer> mObsPhoneTransportID;
 
+    private QuerySimCardState simCardState;
     private QueryTrafficPooler trafficPooler;
 
     private TelephonyManager mTM;
 
     private Thread mThread;
-
-    private boolean isSimCard = false;
+    private Thread mQueryThread;
 
     public PhoneStatePresenter(Context context) {
         mTM = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -36,6 +36,7 @@ public class PhoneStatePresenter {
         mObsPhoneTransportID = new MediatorLiveData<>();
 
         trafficPooler = new QueryTrafficPooler(mObsPhoneTransportID);
+        simCardState = new QuerySimCardState(mTM,mObsPhoneTypeID,mObsPhoneSignalID);
     }
 
     public LiveData<Integer> getPhoneTypeObservable() {
@@ -50,50 +51,27 @@ public class PhoneStatePresenter {
         return mObsPhoneTransportID;
     }
 
-    public void querySimCardState() {
-        int resIdIndex = 0;
-        if (mTM == null) {
-            return;
-        }
-
-        int simState = mTM.getSimState();
-        switch (simState) {
-            case TelephonyManager.SIM_STATE_READY:
-                resIdIndex = 2;
-                break;
-            case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
-            case TelephonyManager.SIM_STATE_PUK_REQUIRED:
-            case TelephonyManager.SIM_STATE_PIN_REQUIRED:
-                resIdIndex = 1;
-                break;
-        }
-
-        if (resIdIndex == 2) {
-            mObsPhoneTypeID.setValue(queryPhoneNetworkTypeResId(mTM));
-            mObsPhoneSignalID.setValue(l02SignalResId[6]);
-            startQueryTraffic();
-            isSimCard = true;
-        } else if (resIdIndex == 1) {
-            mObsPhoneSignalID.setValue(l02SignalResId[1]);
-        } else {
-            mObsPhoneSignalID.setValue(l02SignalResId[0]);
-        }
+    public void onStartQuery(){
+        mQueryThread = new Thread(simCardState);
+        mQueryThread.start();
+        startQueryTraffic();
     }
 
     public void onResumePresenter() {
-        if (isSimCard) {
-            resumeQueryTraffic();
-            listenPhoneState();
-        }
+        resumeQuerySimCard();
+        resumeQueryTraffic();
+        listenPhoneState();
     }
 
     public void onPausePresenter() {
+        pauseQuerySimCard();
         pauseQueryTraffic();
         stopListenPhoneState();
     }
 
     public void onDestroyPresenter() {
         stopQueryTraffic();
+        stopQuerySimCard();
         mTM=null;
     }
 
@@ -110,6 +88,26 @@ public class PhoneStatePresenter {
         mThread = null;
     }
 
+    public void resumeQuerySimCard() {
+        if (mQueryThread != null && simCardState != null) {
+            simCardState.resumeQueryThread();
+        }
+    }
+
+    public void pauseQuerySimCard() {
+        if (mQueryThread != null && simCardState != null) {
+            simCardState.pauseQueryThread();
+        }
+    }
+
+    private void stopQuerySimCard() {
+        if (mQueryThread != null) {
+            mQueryThread.interrupt();
+        }
+
+        mQueryThread = null;
+    }
+
     public void resumeQueryTraffic() {
         if (mThread != null && trafficPooler != null) {
             trafficPooler.resumeQueryThread();
@@ -148,12 +146,7 @@ public class PhoneStatePresenter {
         }
     };
 
-    private int queryPhoneNetworkTypeResId(TelephonyManager tm) {
-        if (tm.getNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) {
-            return l02PhoneTypeResId[0];
-        }
-        return -1;
-    }
+
 
     private int checkSignalLevel(int dbm){
         int index=6;
diff --git a/app/src/main/java/com/ic/launcher/presenter/QuerySimCardState.java b/app/src/main/java/com/ic/launcher/presenter/QuerySimCardState.java
new file mode 100644 (file)
index 0000000..0bc3808
--- /dev/null
@@ -0,0 +1,70 @@
+package com.ic.launcher.presenter;
+
+import android.telephony.TelephonyManager;
+
+import androidx.lifecycle.MediatorLiveData;
+
+import com.ic.launcher.util.IcLog;
+
+import static com.ic.launcher.configure.l02.L02Icon.l02PhoneTypeResId;
+import static com.ic.launcher.configure.l02.L02Icon.l02SignalResId;
+
+public class QuerySimCardState extends QueryThread {
+
+    private static final String TAG = "QuerySimCardState";
+
+    private TelephonyManager mTM;
+    private MediatorLiveData<Integer> mObsPhoneTypeID;
+    private MediatorLiveData<Integer> mObsPhoneSignalID;
+
+    public QuerySimCardState(TelephonyManager mTM,
+                             MediatorLiveData<Integer> mObsPhoneTypeID,
+                             MediatorLiveData<Integer> mObsPhoneSignalID) {
+        this.mTM=mTM;
+        this.mObsPhoneTypeID=mObsPhoneTypeID;
+        this.mObsPhoneSignalID=mObsPhoneSignalID;
+    }
+
+    @Override
+    void onThreadExecute() {
+        querySimCardState();
+    }
+
+    private void querySimCardState() {
+        int resIdIndex = 0;
+        if (mTM == null) {
+            return;
+        }
+
+        int simState = mTM.getSimState();
+
+        IcLog.i(TAG,"simState:"+simState);
+
+        switch (simState) {
+            case TelephonyManager.SIM_STATE_READY:
+                resIdIndex = 2;
+                break;
+            case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
+            case TelephonyManager.SIM_STATE_PUK_REQUIRED:
+            case TelephonyManager.SIM_STATE_PIN_REQUIRED:
+                resIdIndex = 1;
+                break;
+        }
+
+        if (resIdIndex == 2) {
+            mObsPhoneTypeID.postValue(l02PhoneTypeResId[0]);
+            mObsPhoneSignalID.postValue(l02SignalResId[6]);
+        } else if (resIdIndex == 1) {
+            mObsPhoneSignalID.postValue(l02SignalResId[1]);
+        } else {
+            mObsPhoneSignalID.postValue(l02SignalResId[0]);
+        }
+    }
+
+    private int queryPhoneNetworkTypeResId(TelephonyManager tm) {
+        if (tm.getNetworkType() == TelephonyManager.NETWORK_TYPE_LTE) {
+            return l02PhoneTypeResId[0];
+        }
+        return -1;
+    }
+}
\ No newline at end of file
index be1e7ac..49bfe38 100644 (file)
@@ -77,10 +77,14 @@ public class TimeDatePresenter {
     }
 
     public void unRegisterTimeDateReceiver() {
-        mContext.unregisterReceiver(mIntentReceiver);
+        try {
+            mContext.unregisterReceiver(mIntentReceiver);
+        }catch (Exception e){
+            IcLog.e(TAG,e.getMessage());
+        }
     }
 
-    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+    private  BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
diff --git a/app/src/main/java/com/ic/launcher/util/CrashHandler.java b/app/src/main/java/com/ic/launcher/util/CrashHandler.java
new file mode 100644 (file)
index 0000000..2396a75
--- /dev/null
@@ -0,0 +1,213 @@
+package com.ic.launcher.util;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.ic.launcher.R;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Field;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Created by Administrator on 2017/5/18.
+ */
+
+public class CrashHandler implements Thread.UncaughtExceptionHandler {
+    public static final String TAG = CrashHandler.class.getName();
+
+    // 系统默认的UncaughtException处理类
+    private Thread.UncaughtExceptionHandler mDefaultHandler;
+    // CrashHandler实例
+    private static CrashHandler INSTANCE = new CrashHandler();
+    // 程序的Context对象
+    private Context mContext;
+    // 用来存储设备信息和异常信息
+    private Map<String, String> infos = new HashMap<String, String>();
+    // 用于格式化日期,作为日志文件名的一部分
+    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS", Locale.getDefault());
+    private DateFormat dateFormatDate = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
+
+    /**
+     * 保证只有一个CrashHandler实例
+     */
+    private CrashHandler() {
+    }
+
+    /**
+     * 获取CrashHandler实例 ,单例模式
+     */
+    public static CrashHandler getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * 初始化
+     *
+     * @param context
+     */
+    public void init(Context context) {
+        mContext = context;
+        // 获取系统默认的UncaughtException处理器
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        // 设置该CrashHandler为程序的默认处理器
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+    /**
+     * 当UncaughtException发生时会转入该函数来处理
+     */
+    @Override
+    public void uncaughtException(Thread thread, Throwable ex) {
+        if (!handleException(ex) && mDefaultHandler != null) {
+            // 如果用户没有处理则让系统默认的异常处理器来处理
+            mDefaultHandler.uncaughtException(thread, ex);
+        } else {
+            try {
+                Thread.sleep(3000);
+            } catch (InterruptedException e) {
+//                Log.e(TAG, "error : " + e);
+                e.printStackTrace();
+            }
+        }
+
+        // 退出程序
+        ex.printStackTrace();
+        android.os.Process.killProcess(android.os.Process.myPid());
+        System.exit(1);
+    }
+
+    /**
+     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
+     *
+     * @param ex
+     * @return true:如果处理了该异常信息;否则返回false.
+     */
+    private boolean handleException(Throwable ex) {
+        if (ex == null) {
+            return false;
+        }
+        // 使用Toast来显示异常信息
+        new Thread() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                if (mContext != null) {
+                    Toast.makeText(mContext, R.string.program_abnormal, Toast.LENGTH_LONG)
+                            .show();
+                    Looper.loop();
+                }
+            }
+        }.start();
+        // 收集设备参数信息
+        collectDeviceInfo(mContext);
+        // 保存日志文件
+        saveCrashInfo2File(ex);
+        return true;
+    }
+
+    /**
+     * 收集设备参数信息
+     *
+     * @param ctx
+     */
+    public void collectDeviceInfo(Context ctx) {
+        infos.clear();
+        infos.put("===", " PackageInfo ===");
+        try {
+            PackageManager pm = ctx.getPackageManager();
+            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
+
+            if (pi != null) {
+                String versionName = pi.versionName;
+                String versionCode = pi.versionCode + "";
+                infos.put("versionName", versionName);
+                infos.put("versionCode", versionCode);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "an error occured when collect package info " + e);
+        }
+
+        infos.put("===", " Build ===");
+        Field[] fields = Build.class.getDeclaredFields();
+        for (Field field : fields) {
+            try {
+                field.setAccessible(true);
+                infos.put(field.getName(), field.get(null).toString());
+                Log.d(TAG, field.getName() + ":" + field.get(null));
+            } catch (Exception e) {
+                Log.e(TAG, "an error occured when collect crash info," + e);
+            }
+        }
+    }
+
+    /**
+     * 保存错误信息到文件中
+     *
+     * @param ex
+     * @return 返回文件名称, 便于将文件传送到服务器
+     */
+    private String saveCrashInfo2File(Throwable ex) {
+
+        String time = formatter.format(new Date());
+        StringBuilder sb = new StringBuilder();
+        sb.append("=== ").append(time).append(" ===\n");
+
+        Writer writer = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(writer);
+        ex.printStackTrace(printWriter);
+//        ex.printStackTrace();
+        Throwable cause = ex.getCause();
+        while (cause != null) {
+            cause.printStackTrace(printWriter);
+            cause = cause.getCause();
+//            cause.printStackTrace();
+        }
+        printWriter.close();
+        String result = writer.toString();
+        sb.append(result);
+        sb.append("\n");
+
+        sb.append("--- DEVICE INFO ---\n");
+        for (Map.Entry<String, String> entry : infos.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            sb.append(key).append("=").append(value).append("\n");
+        }
+        sb.append("\n\n");
+
+        try {
+            String fileName = "IcLauncher-" + dateFormatDate.format(new Date()) + ".log";
+            if (Environment.getExternalStorageState().equals(
+                    Environment.MEDIA_MOUNTED)) {
+                File dir = new File(Environment.getExternalStorageDirectory().getPath(), "Android/ic/crash/IcLauncher");
+                String path = dir.getPath();
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+                FileOutputStream fos = new FileOutputStream(new File(path, fileName), true);
+                fos.write(sb.toString().getBytes());
+                fos.close();
+            }
+            return fileName;
+        } catch (Exception e) {
+            Log.e(TAG, "an error occured while writing file...," + e);
+        }
+        return null;
+    }
+}
index c79a084..1a3b429 100644 (file)
@@ -19,9 +19,10 @@ public class ServiceUtil {
         }
     }
 
-    public static void bindServiceUtil(Context context , String action, ServiceConnection connection,int type) {
+    public static void bindServiceUtil(Context context , String action, ServiceConnection connection,String pck,int type) {
         Intent in = getExplicitIntent(context, new Intent(action));
         if(in != null){
+            in.setPackage(pck);
             in.putExtra("REQ_TYPE",type);
             context.bindService(in, connection, 0);
         }
diff --git a/app/src/main/res/drawable/ic_transparent_circle.xml b/app/src/main/res/drawable/ic_transparent_circle.xml
new file mode 100644 (file)
index 0000000..7fd130f
--- /dev/null
@@ -0,0 +1,5 @@
+<vector android:height="18dp" android:tint="#A7A5A5"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#A7A5A5" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_white_circle.xml b/app/src/main/res/drawable/ic_white_circle.xml
new file mode 100644 (file)
index 0000000..a032825
--- /dev/null
@@ -0,0 +1,5 @@
+<vector android:height="18dp" android:tint="#FFFFFF"
+    android:viewportHeight="24.0" android:viewportWidth="24.0"
+    android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"/>
+</vector>
index b9ab884..f460bf9 100644 (file)
@@ -3,20 +3,20 @@
     android:oneshot="false">
     <item
         android:drawable="@mipmap/l02_battery_0"
-        android:duration="150" />
+        android:duration="800" />
     <item
         android:drawable="@mipmap/l02_battery_1"
-        android:duration="150" />
+        android:duration="800" />
     <item
         android:drawable="@mipmap/l02_battery_2"
-        android:duration="150" />
+        android:duration="800" />
     <item
         android:drawable="@mipmap/l02_battery_3"
-        android:duration="150" />
+        android:duration="800" />
     <item
         android:drawable="@mipmap/l02_battery_4"
-        android:duration="150" />
+        android:duration="800" />
     <item
         android:drawable="@mipmap/l02_battery_5"
-        android:duration="150" />
+        android:duration="800" />
 </animation-list>
\ No newline at end of file
index cf356fd..9def55e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -8,6 +8,15 @@
     <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/app_list"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"/>
+        android:layout_height="match_parent"
+        android:overScrollMode="never"/>
 
-</LinearLayout>
\ No newline at end of file
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/app_indicator_rv"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|center_horizontal"
+        android:overScrollMode="never"
+        android:layout_marginBottom="5dp"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_indicator_layout.xml b/app/src/main/res/layout/item_indicator_layout.xml
new file mode 100644 (file)
index 0000000..8ca6c6d
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <data>
+
+        <variable
+            name="isCheckItem"
+            type="boolean" />
+    </data>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@{isCheckItem ? @drawable/ic_white_circle : @drawable/ic_transparent_circle}" />
+    </LinearLayout>
+</layout>
index 79c519f..2078193 100644 (file)
@@ -16,4 +16,6 @@
     <string name="app_name_setting">设置</string>
 
     <string name="error_start_activity">应用程序启动失败</string>
+
+    <string name="program_abnormal">很抱歉,程序出现异常,即将退出</string>
 </resources>
diff --git a/app/version_code.properties b/app/version_code.properties
new file mode 100644 (file)
index 0000000..9d0e34f
--- /dev/null
@@ -0,0 +1,2 @@
+#Sat Mar 07 15:05:05 CST 2020
+VERSION_CODE=10