This commit is contained in:
2025-12-23 01:01:58 +08:00
commit b324ed71ca
55 changed files with 2568 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
package com.ttstd.remoteservice;
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.ttstd.remoteservice", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ttstd.remoteservice">
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 悬浮窗权限(可选,用于显示服务状态) -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".base.BaseApplication"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".activity.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 屏幕采集服务 -->
<service android:name=".service.ScreenCaptureService" />
<!-- 指令执行服务 -->
<service android:name=".service.ControlService" />
</application>
</manifest>

View File

@@ -0,0 +1,44 @@
package com.ttstd.remoteservice.activity.main;
import com.ttstd.remoteservice.R;
import com.ttstd.remoteservice.base.mvvm.BaseMvvmActivity;
import com.ttstd.remoteservice.databinding.ActivityMainBinding;
public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBinding> {
@Override
public boolean setNightMode() {
return true;
}
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected void initDataBinding() {
mViewModel.setContext(this);
mViewModel.setVDBinding(mViewDataBinding);
mViewModel.setLifecycle(getLifecycleSubject());
mViewDataBinding.setClick(new BtnClick());
}
@Override
protected void initView() {
}
@Override
protected void initData() {
}
public class BtnClick {
}
}

View File

@@ -0,0 +1,10 @@
package com.ttstd.remoteservice.activity.main;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.remoteservice.base.mvvm.BaseViewModel;
import com.ttstd.remoteservice.databinding.ActivityMainBinding;
public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEvent> {
}

View File

@@ -0,0 +1,49 @@
package com.ttstd.remoteservice.base;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Resources;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import com.ttstd.remoteservice.utils.ScreenUtils;
public class BaseAlertDialogBuilder extends AlertDialog.Builder {
/**
*
*/
private static final float ALERT_BASE_WIDTH = 480f;
private static final float ALERT_BASE_WIDTH_TABLE = 640f;
public BaseAlertDialogBuilder(@NonNull Context context) {
super(adjustAutoSize(context));
}
public BaseAlertDialogBuilder(@NonNull Context context, int themeResId) {
super(adjustAutoSize(context), themeResId);
}
private static Context adjustAutoSize(Context context) {
return new ContextWrapper(context) {
private Resources mResources;
{
Resources oldResources = super.getResources();
mResources = new Resources(oldResources.getAssets(), oldResources.getDisplayMetrics(), oldResources.getConfiguration());
}
@Override
public Resources getResources() {
if (ScreenUtils.isTablet(context)) {
// AutoSizeCompat.autoConvertDensityBaseOnWidth(mResources, ALERT_BASE_WIDTH_TABLE);
} else {
// AutoSizeCompat.autoConvertDensityBaseOnWidth(mResources, ALERT_BASE_WIDTH);
}
return mResources;
}
};
}
}

View File

@@ -0,0 +1,92 @@
package com.ttstd.remoteservice.base;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.alibaba.android.arouter.launcher.ARouter;
import com.tencent.bugly.crashreport.CrashReport;
import com.tencent.mmkv.MMKV;
import com.ttstd.remoteservice.BuildConfig;
import com.ttstd.remoteservice.utils.SystemUtils;
public class BaseApplication extends Application {
private static final String TAG = "BaseApplication";
/**
* ViewModel中因为经常旋转导致弱引用为空
*/
@SuppressLint("StaticFieldLeak")
private static Context context;
public static Context getContext() {
return context;
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: ");
context = getApplicationContext();
if (!BuildConfig.DEBUG) {
catchException();
}
// 在开始分析的地方调用,传入路径
// 如果是放到外部路径,需要添加权限
// 默认存储在/sdcard/Android/data/packagename/files
// Debug.startMethodTracing("App" + System.currentTimeMillis());
init();
}
private void init() {
Log.e(TAG, "init: ");
if (SystemUtils.isMainProcessName(this, android.os.Process.myPid())) {
String rootDir = MMKV.initialize(this);
Log.e(TAG, "mmkv root: " + rootDir);
if (BuildConfig.DEBUG) { // 这两行必须写在init之前否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行必须开启调试模式线上版本需要关闭,否则有安全风险)
}
ARouter.init(this); // 尽可能早推荐在Application中初始化
CrashReport.initCrashReport(getApplicationContext(), "845e3ed68c", false);
CrashReport.setDeviceId(this, Build.MODEL);
xcrash.XCrash.init(this);
}
}
private void catchException() {
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
Log.e("捕获异常子线程:", Thread.currentThread().getName() +
"在:" + e.getStackTrace()[0].getClassName());
}
}
);
//下面是新增方法!
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
while (true) {
try {
Looper.loop(); //会先执行这个方法,然后在执行下面的异常捕获方法!
} catch (Exception e) {
Log.e("捕获异常主线程:", Thread.currentThread().getName() + "在:" + e.getStackTrace()[0].getClassName());
e.printStackTrace();
}
}
}
});
}
}

View File

@@ -0,0 +1,88 @@
package com.ttstd.remoteservice.base;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import androidx.core.graphics.Insets;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.ttstd.remoteservice.R;
import com.ttstd.remoteservice.base.rx.BaseRxActivity;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
public abstract class BaseDataBindingActivity extends BaseRxActivity {
public BaseDataBindingActivity() {
super();
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
initDataBinding();
initView();
initData();
}
/**
* @return 是否是黑色状态栏
*/
// protected abstract boolean setNightMode();
public boolean setNightMode() {
return false;
}
/**
* @return 是否是入侵
*/
// protected abstract boolean setNightMode();
public boolean setfitWindow() {
return false;
}
protected abstract void initDataBinding();
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化数据
*/
protected abstract void initData();
public void addNavigationBarBottomPadding(View view) {
UltimateBarX.addNavigationBarBottomPadding(view);
if (Build.VERSION.SDK_INT >= 35) {
ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
Insets systemInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
v.setPadding(0, 0, 0, systemInsets.bottom);
return insets;
}
});
}
}
}

View File

@@ -0,0 +1,44 @@
package com.ttstd.remoteservice.base;
import android.os.Bundle;
import com.ttstd.remoteservice.base.rx.BaseRxDialogFragment;
public abstract class BaseDialogFragment extends BaseRxDialogFragment {
protected boolean isViewInitiated;
protected boolean isVisibleToUser;
protected boolean isDataInitiated;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isViewInitiated = true;
prepareFetchData();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
prepareFetchData();
}
public abstract void fetchData();
public boolean prepareFetchData() {
return prepareFetchData(false);
}
public boolean prepareFetchData(boolean forceUpdate) {
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
fetchData();
//注释掉保证每次都更新数据
// isDataInitiated = true;
return true;
}
return false;
}
}

View File

@@ -0,0 +1,44 @@
package com.ttstd.remoteservice.base;
import android.os.Bundle;
import com.ttstd.remoteservice.base.rx.BaseRxFragment;
public abstract class BaseFragment extends BaseRxFragment {
protected boolean isViewInitiated;
protected boolean isVisibleToUser;
protected boolean isDataInitiated;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isViewInitiated = true;
prepareFetchData();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
prepareFetchData();
}
public abstract void fetchData();
public boolean prepareFetchData() {
return prepareFetchData(false);
}
public boolean prepareFetchData(boolean forceUpdate) {
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
fetchData();
//注释掉保证每次都更新数据
// isDataInitiated = true;
return true;
}
return false;
}
}

View File

@@ -0,0 +1,80 @@
package com.ttstd.remoteservice.base;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import androidx.core.graphics.Insets;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.ttstd.remoteservice.R;
import com.ttstd.remoteservice.base.rx.BaseRxActivity;
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
public abstract class BaseTransparentActivity extends BaseRxActivity {
public BaseTransparentActivity() {
super();
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// StatusBarUtil.init(this);
UltimateBarX.statusBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
UltimateBarX.navigationBar(this)
.transparent()
.colorRes(R.color.colorPrimaryDark)
.light(setNightMode())
.fitWindow(setfitWindow())
.apply();
}
/**
* 设置布局
*/
protected abstract int getLayoutId();
/**
* @return 是否是黑色状态栏
*/
// protected abstract boolean setNightMode();
public boolean setNightMode() {
return false;
}
/**
* @return 是否是入侵
*/
// protected abstract boolean setNightMode();
public boolean setfitWindow() {
return false;
}
/**
* @param view android 15 edge-to-edge会覆盖导航栏
*/
public void addNavigationBarBottomPadding(View view) {
UltimateBarX.addNavigationBarBottomPadding(view);
if (Build.VERSION.SDK_INT >= 35) {
ViewCompat.setOnApplyWindowInsetsListener(view, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
Insets systemInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
v.setPadding(0, 0, 0, systemInsets.bottom);
return insets;
}
});
}
}
}

View File

@@ -0,0 +1,35 @@
package com.ttstd.remoteservice.base.mvp;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable;
import com.ttstd.remoteservice.base.BaseTransparentActivity;
@Deprecated
public abstract class BaseMvpActivity extends BaseTransparentActivity {
public BaseMvpActivity() {
super();
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
initView();
initData();
}
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化数据
*/
protected abstract void initData();
}

View File

@@ -0,0 +1,8 @@
package com.ttstd.remoteservice.base.mvp;
@Deprecated
public interface BasePresenter<V extends BaseView> {
void attachView(V view);
void detachView();
}

View File

@@ -0,0 +1,6 @@
package com.ttstd.remoteservice.base.mvp;
@Deprecated
public interface BaseView {
}

View File

@@ -0,0 +1,54 @@
package com.ttstd.remoteservice.base.mvvm;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.remoteservice.base.BaseTransparentActivity;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
public abstract class BaseMvvmActivity<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseTransparentActivity {
private static final String TAG = BaseMvvmActivity.class.getSimpleName();
protected VM mViewModel;
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//ViewDataBinding
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId());
mViewDataBinding.setLifecycleOwner(this);
//ViewModel
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
boolean isAbstract = Modifier.isAbstract(vmClass.getModifiers());
Log.e(TAG, "isLocalClass:" + vmClass.getSimpleName().equals(ViewModel.class.getSimpleName()) + " isAbstract:" + isAbstract);
if (!isAbstract) {//不是一个抽象类
mViewModel = new ViewModelProvider(this).get(vmClass);
}
initDataBinding();
initView();
initData();
}
protected abstract void initDataBinding();
/**
* 初始化视图
*/
protected abstract void initView();
/**
* 初始化数据
*/
protected abstract void initData();
}

View File

@@ -0,0 +1,54 @@
package com.ttstd.remoteservice.base.mvvm;
import android.content.Context;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import java.lang.ref.WeakReference;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseViewModel<VDB extends ViewDataBinding, T> extends ViewModel {
/**
* 当前viewmodel对应的页面binding
*/
protected VDB binding;
public void setVDBinding(ViewDataBinding vdBinding) {
binding = (VDB) vdBinding;
}
public VDB getVDBinding() {
if (binding == null) {
throw new NullPointerException("BaseViewModel >> getVDBinding >> null!!!");
}
return binding;
}
private WeakReference<Context> weakContext;
public void setContext(Context context) {
if (weakContext == null) {
weakContext = new WeakReference<>(context.getApplicationContext());
}
}
public Context getSafeContext() {
if (weakContext == null) {
throw new NullPointerException("BaseViewModel >> getCtx >> null!!!");
}
return weakContext.get();
}
private BehaviorSubject<T> mBehaviorSubject;
public void setLifecycle(BehaviorSubject subject) {
this.mBehaviorSubject = (BehaviorSubject<T>) subject;
}
public BehaviorSubject<T> getLifecycle() {
return mBehaviorSubject;
}
}

View File

@@ -0,0 +1,275 @@
package com.ttstd.remoteservice.base.mvvm.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.remoteservice.base.BaseDialogFragment;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
/**
* @author: lml
* @date: 2021/12/15
*/
public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseDialogFragment {
protected String mTag = this.getClass().getSimpleName();
/**
* 是否顯示了
*/
protected boolean mIsVisible;
/**
* 是否準備好了-Created
*/
protected boolean mHasPrepare;
protected VM mViewModel;
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
//
// protected Toolbar toolbar;
// protected View statusBarView;
//
protected Bundle bundle;//来自getArguments()
protected Bundle savedInstanceState;
// protected Context context;
/**
* 上下文
*/
private WeakReference<Context> ctx;
public Context getCtx() {
return ctx == null ? null : ctx.get();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// this.context = context;
ctx = new WeakReference<>(context);
}
/**
* onCreate、onResume里不能调用
*
* @return
*/
public boolean isAttached() {
boolean flag = getCtx() != null && isAdded();
Log.e(" >> isAttached >>", "flag = " + flag);
return flag;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//ViewDataBinding
mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
mViewDataBinding.setLifecycleOwner(this);
//ViewModel
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
//
return mViewDataBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// if (initStatusBarToolBar()) {
// toolbar = getToolbar();
// }
//注册eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.register(this);
//
// fitsLayoutOverlap();
initDataBinding();
initView(bundle = getArguments());
//
initData(this.savedInstanceState = savedInstanceState);
//
if (mIsVisible) {
onEnter();
}
mHasPrepare = true;
//
// LiveDataBus.get().with(ConstantUtils.DATA_BUS_LOADING_FRAGMENT, Boolean.class).observe(getActivity(), bool -> {
// L.e(" >> LiveDataBus >> DATA_BUS_LOADING_FRAGMENT: %s", bool);
// if(bool) {
// showLoading(R.string.str_please_wait);
// } else {
// hideLoading();
// }
// });
}
@Override
public void onDestroyView() {
super.onDestroyView();
mHasPrepare = false;
mViewDataBinding = null;
//移除eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.unregister(this);
//
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mIsVisible == getUserVisibleHint())
return;
mIsVisible = getUserVisibleHint();
if (mIsVisible) {
if (!mHasPrepare)
return;
onEnter();
} else {
onExit();
}
}
@LayoutRes
protected abstract int getLayoutId();
// protected abstract Toolbar getToolbar();
// protected View getStatusView() {
// return null;
// }
protected abstract void initDataBinding();
protected abstract void initView(Bundle bundle);
protected abstract void initData(Bundle savedInstanceState);
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// fitsLayoutOverlap();
}
// protected boolean isImmersionBarEnabled() {
// return true;
// }
// protected boolean initStatusBarToolBar() {
// return true;
// }
// private void fitsLayoutOverlap() {
// if (!isImmersionBarEnabled()) return;
// if (statusBarView != null) {
// ImmersionBar.setStatusBarView(getActivity(), statusBarView);
// }
// if (toolbar != null) {
// ImmersionBar.setTitleBar(getActivity(), toolbar);
// }
// }
protected void hideInputMethod(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
protected void hideInputMethod(Activity activity, EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
protected void showInputMethod(EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
}
// private CustomDialog mWaitDialog;
//
// public void showLoading(@StringRes int contentID) {
// showLoading(contentID, R.color.white);
// }
//
// public void showLoading(@StringRes int contentID, @ColorRes int color) {
// hideLoading();
// DialogX.init(getActivity());
// if (color == R.color.white) {
// mWaitDialog = DialogXUtil.getInstance().showLoading(getActivity(), getString(contentID), getResources().getColor(color));
// } else {
// mWaitDialog = DialogXUtil.getInstance().showLoading_black(getActivity(), getString(contentID), getResources().getColor(color));
// }
// }
//
// public void updateLoadingTip(@StringRes int messageID, int percent) {
// try {
// if (mWaitDialog != null && mWaitDialog.isShow()) {
// TextView tvTip = mWaitDialog.getCustomView().findViewById(R.id.tv_load_tip);
// if (tvTip != null)
// tvTip.setText(getResources().getString(messageID) + (percent == -1 ? "" : percent + "%"));
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public boolean isShowLoading() {
// return mWaitDialog != null && mWaitDialog.isShow();
// }
//
// public void hideLoading() {
// try {
// boolean isShow = isShowLoading();
// L.d(" >> hideLoading :: isShow: %s", isShow);
// if (isShow)
// mWaitDialog.dismiss();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
/**
* 進入界面
*/
protected void onEnter() {
}
/**
* 離開界面
*/
protected void onExit() {
}
}

View File

@@ -0,0 +1,275 @@
package com.ttstd.remoteservice.base.mvvm.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.remoteservice.base.BaseFragment;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
/**
* @author: lml
* @date: 2021/12/15
*/
public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseFragment {
protected String mTag = this.getClass().getSimpleName();
/**
* 是否顯示了
*/
protected boolean mIsVisible;
/**
* 是否準備好了-Created
*/
protected boolean mHasPrepare;
protected VM mViewModel;
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
//
// protected Toolbar toolbar;
// protected View statusBarView;
//
protected Bundle bundle;//来自getArguments()
protected Bundle savedInstanceState;
// protected Context context;
/**
* 上下文
*/
private WeakReference<Context> ctx;
public Context getCtx() {
return ctx == null ? null : ctx.get();
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// this.context = context;
ctx = new WeakReference<>(context);
}
/**
* onCreate、onResume里不能调用
*
* @return
*/
public boolean isAttached() {
boolean flag = getCtx() != null && isAdded();
Log.e(" >> isAttached >>", "flag = " + flag);
return flag;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//ViewDataBinding
mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
mViewDataBinding.setLifecycleOwner(this);
//ViewModel
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
//
return mViewDataBinding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// if (initStatusBarToolBar()) {
// toolbar = getToolbar();
// }
//注册eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.register(this);
//
// fitsLayoutOverlap();
initDataBinding();
initView(bundle = getArguments());
//
initData(this.savedInstanceState = savedInstanceState);
//
if (mIsVisible) {
onEnter();
}
mHasPrepare = true;
//
// LiveDataBus.get().with(ConstantUtils.DATA_BUS_LOADING_FRAGMENT, Boolean.class).observe(getActivity(), bool -> {
// L.e(" >> LiveDataBus >> DATA_BUS_LOADING_FRAGMENT: %s", bool);
// if(bool) {
// showLoading(R.string.str_please_wait);
// } else {
// hideLoading();
// }
// });
}
@Override
public void onDestroyView() {
super.onDestroyView();
mHasPrepare = false;
mViewDataBinding = null;
//移除eventbus
// if (getClass().isAnnotationPresent(BindEventBus.class))
// EventBusManager.unregister(this);
//
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (mIsVisible == getUserVisibleHint())
return;
mIsVisible = getUserVisibleHint();
if (mIsVisible) {
if (!mHasPrepare)
return;
onEnter();
} else {
onExit();
}
}
@LayoutRes
protected abstract int getLayoutId();
// protected abstract Toolbar getToolbar();
// protected View getStatusView() {
// return null;
// }
protected abstract void initDataBinding();
protected abstract void initView(Bundle bundle);
protected abstract void initData(Bundle savedInstanceState);
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// fitsLayoutOverlap();
}
// protected boolean isImmersionBarEnabled() {
// return true;
// }
// protected boolean initStatusBarToolBar() {
// return true;
// }
// private void fitsLayoutOverlap() {
// if (!isImmersionBarEnabled()) return;
// if (statusBarView != null) {
// ImmersionBar.setStatusBarView(getActivity(), statusBarView);
// }
// if (toolbar != null) {
// ImmersionBar.setTitleBar(getActivity(), toolbar);
// }
// }
protected void hideInputMethod(Activity activity) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
protected void hideInputMethod(Activity activity, EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Activity.INPUT_METHOD_SERVICE);
View view = activity.getCurrentFocus();
if (view != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
protected void showInputMethod(EditText editText) {
InputMethodManager imm = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
}
// private CustomDialog mWaitDialog;
//
// public void showLoading(@StringRes int contentID) {
// showLoading(contentID, R.color.white);
// }
//
// public void showLoading(@StringRes int contentID, @ColorRes int color) {
// hideLoading();
// DialogX.init(getActivity());
// if (color == R.color.white) {
// mWaitDialog = DialogXUtil.getInstance().showLoading(getActivity(), getString(contentID), getResources().getColor(color));
// } else {
// mWaitDialog = DialogXUtil.getInstance().showLoading_black(getActivity(), getString(contentID), getResources().getColor(color));
// }
// }
//
// public void updateLoadingTip(@StringRes int messageID, int percent) {
// try {
// if (mWaitDialog != null && mWaitDialog.isShow()) {
// TextView tvTip = mWaitDialog.getCustomView().findViewById(R.id.tv_load_tip);
// if (tvTip != null)
// tvTip.setText(getResources().getString(messageID) + (percent == -1 ? "" : percent + "%"));
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
//
// public boolean isShowLoading() {
// return mWaitDialog != null && mWaitDialog.isShow();
// }
//
// public void hideLoading() {
// try {
// boolean isShow = isShowLoading();
// L.d(" >> hideLoading :: isShow: %s", isShow);
// if (isShow)
// mWaitDialog.dismiss();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
/**
* 進入界面
*/
protected void onEnter() {
}
/**
* 離開界面
*/
protected void onExit() {
}
}

View File

@@ -0,0 +1,94 @@
package com.ttstd.remoteservice.base.rx;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxActivity}
* copied form RxActivity}
*/
public abstract class BaseRxActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<ActivityEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
@CallSuper
protected void onStart() {
super.onStart();
lifecycleSubject.onNext(ActivityEvent.START);
}
@Override
@CallSuper
protected void onResume() {
super.onResume();
lifecycleSubject.onNext(ActivityEvent.RESUME);
}
@Override
@CallSuper
protected void onPause() {
lifecycleSubject.onNext(ActivityEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
protected void onStop() {
lifecycleSubject.onNext(ActivityEvent.STOP);
super.onStop();
}
@Override
@CallSuper
protected void onDestroy() {
lifecycleSubject.onNext(ActivityEvent.DESTROY);
super.onDestroy();
}
}

View File

@@ -0,0 +1,123 @@
package com.ttstd.remoteservice.base.rx;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxFragment}
* copied form RxFragment}
*/
public class BaseRxDialogFragment extends DialogFragment implements LifecycleProvider<FragmentEvent> {
private final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<FragmentEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<FragmentEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindFragment(lifecycleSubject);
}
@Override
@CallSuper
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
lifecycleSubject.onNext(FragmentEvent.ATTACH);
}
@Override
@CallSuper
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE);
}
@Override
@CallSuper
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
}
@Override
@CallSuper
public void onStart() {
super.onStart();
lifecycleSubject.onNext(FragmentEvent.START);
}
@Override
@CallSuper
public void onResume() {
super.onResume();
lifecycleSubject.onNext(FragmentEvent.RESUME);
}
@Override
@CallSuper
public void onPause() {
lifecycleSubject.onNext(FragmentEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
public void onStop() {
lifecycleSubject.onNext(FragmentEvent.STOP);
super.onStop();
}
@Override
@CallSuper
public void onDestroyView() {
lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
super.onDestroyView();
}
@Override
@CallSuper
public void onDestroy() {
lifecycleSubject.onNext(FragmentEvent.DESTROY);
super.onDestroy();
}
@Override
@CallSuper
public void onDetach() {
lifecycleSubject.onNext(FragmentEvent.DETACH);
super.onDetach();
}
}

View File

@@ -0,0 +1,123 @@
package com.ttstd.remoteservice.base.rx;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxFragment}
* copied form RxFragment}
*/
public class BaseRxFragment extends Fragment implements LifecycleProvider<FragmentEvent> {
private final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<FragmentEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<FragmentEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindFragment(lifecycleSubject);
}
@Override
@CallSuper
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
lifecycleSubject.onNext(FragmentEvent.ATTACH);
}
@Override
@CallSuper
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE);
}
@Override
@CallSuper
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
}
@Override
@CallSuper
public void onStart() {
super.onStart();
lifecycleSubject.onNext(FragmentEvent.START);
}
@Override
@CallSuper
public void onResume() {
super.onResume();
lifecycleSubject.onNext(FragmentEvent.RESUME);
}
@Override
@CallSuper
public void onPause() {
lifecycleSubject.onNext(FragmentEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
public void onStop() {
lifecycleSubject.onNext(FragmentEvent.STOP);
super.onStop();
}
@Override
@CallSuper
public void onDestroyView() {
lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
super.onDestroyView();
}
@Override
@CallSuper
public void onDestroy() {
lifecycleSubject.onNext(FragmentEvent.DESTROY);
super.onDestroy();
}
@Override
@CallSuper
public void onDetach() {
lifecycleSubject.onNext(FragmentEvent.DETACH);
super.onDetach();
}
}

View File

@@ -0,0 +1,62 @@
package com.ttstd.remoteservice.base.rx;
import android.app.Service;
import android.content.Intent;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseRxService extends Service implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<ActivityEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
public void onCreate() {
super.onCreate();
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycleSubject.onNext(ActivityEvent.STOP);
}
}

View File

@@ -0,0 +1,41 @@
package com.ttstd.remoteservice.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
public class ControlService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onLowMemory() {
super.onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
}
}

View File

@@ -0,0 +1,40 @@
package com.ttstd.remoteservice.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import androidx.annotation.Nullable;
public class ScreenCaptureService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onLowMemory() {
super.onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
}
}

View File

@@ -0,0 +1,77 @@
package com.ttstd.remoteservice.utils;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.Log;
import java.lang.reflect.Method;
public class ScreenUtils {
private static final String TAG = "ScreenUtils";
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
public static int dp2px(Resources resources, float dp) {
final float scale = resources.getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
public static int sp2px(Resources resources, float sp) {
final float scale = resources.getDisplayMetrics().scaledDensity;
return (int) (sp * scale);
}
/**
* 应用需反射调用
*/
public static boolean isTablet() {
try {
// 1. 反射获取 SystemProperties 类
Class<?> systemPropertiesClass = Class.forName("android.os.SystemProperties");
// 2. 获取 get(String key) 方法
Method getMethod = systemPropertiesClass.getDeclaredMethod("get", String.class);
// 3. 调用方法获取属性值
String characteristics = (String) getMethod.invoke(null, "ro.build.characteristics");
Log.e(TAG, "isTablet: " + characteristics);
// 4. 判断是否包含 "tablet" 标识
return characteristics != null && characteristics.contains("tablet");
} catch (Exception e) {
// 反射失败时的处理(如属性不存在或权限问题)
Log.e(TAG, "Reflection failed: " + e.getMessage());
return false;
}
}
public static boolean isTablet(Context context) {
boolean isTablet = (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
Log.e(TAG, "isTablet: " + isTablet);
return isTablet;
}
/**
* 是否是平板
*
* @param context 上下文
* @return 是平板则返回true反之返回false
*/
public static boolean isPad(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
}
}

View File

@@ -0,0 +1,24 @@
package com.ttstd.remoteservice.utils;
import android.app.ActivityManager;
import android.content.Context;
import java.util.List;
public class SystemUtils {
public static boolean isMainProcessName(Context cxt, int pid) {
String packageName = cxt.getPackageName();
ActivityManager am = (ActivityManager) cxt.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return false;
}
for (ActivityManager.RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName.equals(packageName);
}
}
return false;
}
}

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".activity.main.MainActivity">
<data>
<variable
name="click"
type="com.ttstd.remoteservice.activity.main.MainActivity.BtnClick" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">TTSTDRemoteService</string>
</resources>

View File

@@ -0,0 +1,18 @@
<resources>
<!-- Base application theme. -->
<!-- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">-->
<!-- &lt;!&ndash; Customize your theme here. &ndash;&gt;-->
<!-- <item name="colorPrimary">@color/colorPrimary</item>-->
<!-- <item name="colorPrimaryDark">@color/colorPrimaryDark</item>-->
<!-- <item name="colorAccent">@color/colorAccent</item>-->
<!-- </style>-->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@@ -0,0 +1,17 @@
package com.ttstd.remoteservice;
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);
}
}