dev
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.mogo.mogo.module.apps;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith( AndroidJUnit4.class )
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals( "com.mogo.mogo.module.apps.test", appContext.getPackageName() );
|
||||
}
|
||||
}
|
||||
26
modules/mogo-module-apps/src/main/AndroidManifest.xml
Normal file
26
modules/mogo-module-apps/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.module.apps">
|
||||
|
||||
<application>
|
||||
<receiver
|
||||
android:name=".receiver.AppInstallReceiver"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
<!-- 一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)-->
|
||||
<action android:name="android.intent.action.PACKAGE_ADDED" />
|
||||
<!-- 一个新版本的应用安装到设备,替换之前已经存在的版本-->
|
||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||
<!-- 一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)-->
|
||||
<action android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||
<!-- 一个已存在的应用程序包已经改变,包括包名-->
|
||||
<action android:name="android.intent.action.ACTION_PACKAGE_CHANGED" />
|
||||
<!-- 用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播-->
|
||||
<action android:name="android.intent.action.ACTION_PACKAGE_RESTARTED" />
|
||||
<!-- 用户已经清楚一个包的数据,包括包名(清除包程序不能接收到这个广播)-->
|
||||
<action android:name="android.intent.action.ACTION_PACKAGE_DATA_CLEARED" />
|
||||
|
||||
<data android:scheme="package" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.mogo.module.apps;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.mogo.module.apps.model.AppInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-30
|
||||
* <p>
|
||||
* 描述
|
||||
*/
|
||||
public class AppsAdapter extends RecyclerView.Adapter< AppsAdapter.AppsViewHolder > {
|
||||
|
||||
private List< AppInfo > mAppInfos;
|
||||
|
||||
public AppsAdapter( List< AppInfo > appInfos ) {
|
||||
this.mAppInfos = appInfos;
|
||||
}
|
||||
|
||||
public void refreshAppInfos( List< AppInfo > mAppInfos ) {
|
||||
this.mAppInfos = mAppInfos;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AppsViewHolder onCreateViewHolder( @NonNull ViewGroup parent, int viewType ) {
|
||||
return new AppsViewHolder( LayoutInflater.from( parent.getContext() ).inflate( R.layout.module_apps_item_app, null ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder( @NonNull AppsViewHolder holder, int position ) {
|
||||
final AppInfo appInfo = mAppInfos.get( position );
|
||||
holder.mIcon.setImageDrawable( appInfo.getIcon() );
|
||||
holder.mName.setText( appInfo.getName() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mAppInfos == null ? 0 : mAppInfos.size();
|
||||
}
|
||||
|
||||
public static class AppsViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public ImageView mIcon;
|
||||
public TextView mName;
|
||||
|
||||
public AppsViewHolder( @NonNull View itemView ) {
|
||||
super( itemView );
|
||||
mIcon = itemView.findViewById( R.id.module_apps_id_app_icon );
|
||||
mName = itemView.findViewById( R.id.module_apps_id_app_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.mogo.module.apps;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.mogo.commons.mvp.MvpFragment;
|
||||
import com.mogo.module.apps.model.AppInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-30
|
||||
* <p>
|
||||
* 描述
|
||||
*/
|
||||
public class AppsFragment extends MvpFragment< AppsView, AppsPresenter > implements AppsView {
|
||||
|
||||
private BottomSheetBehavior mBottomSheetBehavior;
|
||||
private RecyclerView mAppsList;
|
||||
private AppsAdapter mAppsAdapter;
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.module_apps_fragment_apps;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initViews() {
|
||||
mAppsList = findViewById( R.id.module_apps_id_apps );
|
||||
mAppsList.setLayoutManager( new GridLayoutManager( getContext(), 3 ) );
|
||||
|
||||
mBottomSheetBehavior = BottomSheetBehavior.from( mAppsList );
|
||||
mBottomSheetBehavior.setSkipCollapsed( true );
|
||||
mBottomSheetBehavior.setBottomSheetCallback( new BottomSheetBehavior.BottomSheetCallback() {
|
||||
@Override
|
||||
public void onStateChanged( @NonNull View bottomSheet, int newState ) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide( @NonNull View bottomSheet, float slideOffset ) {
|
||||
|
||||
}
|
||||
} );
|
||||
mBottomSheetBehavior.setState( BottomSheetBehavior.STATE_COLLAPSED );
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected AppsPresenter createPresenter() {
|
||||
return new AppsPresenter( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated( @Nullable Bundle savedInstanceState ) {
|
||||
super.onActivityCreated( savedInstanceState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderApps( List< AppInfo > appInfos ) {
|
||||
if ( mAppsAdapter == null ) {
|
||||
mAppsAdapter = new AppsAdapter( appInfos );
|
||||
mAppsList.setAdapter( mAppsAdapter );
|
||||
} else {
|
||||
mAppsAdapter.refreshAppInfos( appInfos );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.mogo.module.apps;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.alibaba.android.arouter.facade.annotation.Route;
|
||||
import com.mogo.map.listener.IMogoMapListener;
|
||||
import com.mogo.map.location.IMogoLocationListener;
|
||||
import com.mogo.map.navi.IMogoNaviListener;
|
||||
import com.mogo.module.common.MogoModulePaths;
|
||||
import com.mogo.service.module.IMogoModuleLifecycle;
|
||||
import com.mogo.service.module.IMogoModuleProvider;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-30
|
||||
* <p>
|
||||
* 描述
|
||||
*/
|
||||
|
||||
@Route( path = MogoModulePaths.PATH_MODULE_APPS )
|
||||
public class AppsFragmentProvider implements IMogoModuleProvider {
|
||||
|
||||
private AppsFragment mAppsFragment;
|
||||
|
||||
@Override
|
||||
public Fragment createFragment( Context context, Bundle data ) {
|
||||
mAppsFragment = new AppsFragment();
|
||||
mAppsFragment.setArguments( data );
|
||||
return mAppsFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView( Context context ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getModuleName() {
|
||||
return MogoModulePaths.PATH_MODULE_APPS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMogoModuleLifecycle getCardLifecycle() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMogoMapListener getMapListener() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return IMogoModuleProvider.TYPE_FRAGMENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMogoNaviListener getNaviListener() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMogoLocationListener getLocationListener() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init( Context context ) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.mogo.module.apps;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.Observer;
|
||||
|
||||
import com.mogo.commons.mvp.Presenter;
|
||||
import com.mogo.module.apps.model.AppInfo;
|
||||
import com.mogo.utils.ThreadPoolService;
|
||||
import com.mogo.utils.UiThreadHandler;
|
||||
import com.mogo.utils.logger.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-30
|
||||
* <p>
|
||||
* 描述
|
||||
*/
|
||||
public class AppsPresenter extends Presenter< AppsView > {
|
||||
|
||||
private static final String TAG = "AppsPresenter";
|
||||
|
||||
public AppsPresenter( AppsView view ) {
|
||||
super( view );
|
||||
RefreshAppsListLiveData.getInstance().observeForever( new Observer< String >() {
|
||||
@Override
|
||||
public void onChanged( String s ) {
|
||||
renderAppsList();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate( @NonNull LifecycleOwner owner ) {
|
||||
super.onCreate( owner );
|
||||
renderAppsList();
|
||||
}
|
||||
|
||||
private void renderAppsList() {
|
||||
ThreadPoolService.execute( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final List< AppInfo > appInfoList = loadAppsList();
|
||||
UiThreadHandler.post( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if ( mView != null ) {
|
||||
mView.renderApps( appInfoList );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private List< AppInfo > loadAppsList() {
|
||||
Logger.i( TAG, "load apps list." );
|
||||
final List< AppInfo > appInfos = new ArrayList<>();
|
||||
final PackageManager packageManager = getContext().getPackageManager();
|
||||
List< PackageInfo > packages = packageManager.getInstalledPackages( 0 );
|
||||
for ( int i = 0; i < packages.size(); ++i ) {
|
||||
PackageInfo packageInfo = packages.get( i );
|
||||
//获取非系统应用
|
||||
if ( ( packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) == 0 ) {
|
||||
String appName = packageInfo.applicationInfo.loadLabel( packageManager ).toString();
|
||||
String packageName = packageInfo.packageName;
|
||||
String versionName = packageInfo.versionName;
|
||||
int versionCode = packageInfo.versionCode;
|
||||
Drawable appIcon = packageInfo.applicationInfo.loadIcon( packageManager );
|
||||
AppInfo appInfo = new AppInfo( appName, packageName, versionName, versionCode, appIcon );
|
||||
appInfos.add( appInfo );
|
||||
}
|
||||
}
|
||||
return appInfos;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.mogo.module.apps;
|
||||
|
||||
import com.mogo.commons.mvp.IView;
|
||||
import com.mogo.module.apps.model.AppInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-30
|
||||
* <p>
|
||||
* 描述
|
||||
*/
|
||||
public interface AppsView extends IView {
|
||||
|
||||
/**
|
||||
* 显示加载的app列表
|
||||
*
|
||||
* @param appInfos
|
||||
*/
|
||||
void renderApps( List< AppInfo > appInfos );
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mogo.module.apps;
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-30
|
||||
* <p>
|
||||
* 刷新app列表
|
||||
*/
|
||||
public class RefreshAppsListLiveData extends MutableLiveData< String > {
|
||||
|
||||
private static volatile RefreshAppsListLiveData sInstance;
|
||||
|
||||
private RefreshAppsListLiveData() {
|
||||
}
|
||||
|
||||
public static RefreshAppsListLiveData getInstance() {
|
||||
if ( sInstance == null ) {
|
||||
synchronized ( RefreshAppsListLiveData.class ) {
|
||||
if ( sInstance == null ) {
|
||||
sInstance = new RefreshAppsListLiveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public synchronized void release() {
|
||||
sInstance = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.mogo.module.apps.model;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2019-12-30
|
||||
* <p>
|
||||
* app 信息
|
||||
*/
|
||||
public class AppInfo {
|
||||
|
||||
private final String mName;
|
||||
private final String mPackageName;
|
||||
private final String mVersionName;
|
||||
private final int mVersionCode;
|
||||
private final Drawable mIcon;
|
||||
|
||||
public AppInfo( String name, String mPackageName, String mVersionName, int versionCode, Drawable icon ) {
|
||||
this.mName = name;
|
||||
this.mPackageName = mPackageName;
|
||||
this.mVersionName = mVersionName;
|
||||
this.mVersionCode = versionCode;
|
||||
this.mIcon = icon;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
public String getVersionName() {
|
||||
return mVersionName;
|
||||
}
|
||||
|
||||
public int getVersionCode() {
|
||||
return mVersionCode;
|
||||
}
|
||||
|
||||
public Drawable getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
package com.mogo.module.apps.receiver;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
public class AppInstallReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive( Context context, Intent intent ) {
|
||||
if ( intent.getAction().equals( Intent.ACTION_PACKAGE_ADDED ) ) {
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
notifyRefreshAppsList( packageName );
|
||||
}
|
||||
if ( intent.getAction().equals( Intent.ACTION_PACKAGE_REMOVED ) ) {
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
notifyRefreshAppsList( packageName );
|
||||
}
|
||||
if ( intent.getAction().equals( Intent.ACTION_PACKAGE_REPLACED ) ) {
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
notifyRefreshAppsList( packageName );
|
||||
}
|
||||
if ( intent.getAction().equals( Intent.ACTION_PACKAGE_CHANGED ) ) {
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
notifyRefreshAppsList( packageName );
|
||||
}
|
||||
if ( intent.getAction().equals( Intent.ACTION_PACKAGE_RESTARTED ) ) {
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
notifyRefreshAppsList( packageName );
|
||||
}
|
||||
if ( intent.getAction().equals( Intent.ACTION_PACKAGE_DATA_CLEARED ) ) {
|
||||
String packageName = intent.getData().getSchemeSpecificPart();
|
||||
notifyRefreshAppsList( packageName );
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyRefreshAppsList( String packageName ) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/module_apps_id_apps"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:behavior_hideable="false"
|
||||
app:behavior_peekHeight="50dp"
|
||||
app:layout_behavior="@string/bottom_sheet_behavior" />
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/module_apps_id_app_icon"
|
||||
android:layout_width="75dp"
|
||||
android:layout_height="75dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/module_apps_id_app_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
3
modules/mogo-module-apps/src/main/res/values/strings.xml
Normal file
3
modules/mogo-module-apps/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">mogo-module-apps</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mogo.mogo.module.apps;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals( 4, 2 + 2 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user